Moved to Snark engine

This commit is contained in:
Alexander Nozik 2022-05-04 19:29:29 +03:00
parent 9c75852945
commit 7de2e02433
No known key found for this signature in database
GPG Key ID: F7FCF2DD25C71357
13 changed files with 146 additions and 27 deletions

View File

@ -6,9 +6,9 @@ plugins {
application application
} }
repositories{ //repositories{
mavenLocal() // mavenLocal()
} //}
group = "ru.mipt.npm" group = "ru.mipt.npm"
version = "0.0.1-SNAPSHOT" version = "0.0.1-SNAPSHOT"
@ -28,7 +28,7 @@ tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile>{
} }
} }
val dataforgeVersion by extra("0.6.0-dev-4") val dataforgeVersion by extra("0.6.0-dev-5")
val ktorVersion = KScienceVersions.ktorVersion val ktorVersion = KScienceVersions.ktorVersion
dependencies { dependencies {

View File

@ -2,4 +2,4 @@ kotlin.code.style=official
toolsVersion=0.11.4-kotlin-1.6.20 toolsVersion=0.11.4-kotlin-1.6.20
#development=true development=true

View File

@ -1,6 +1,5 @@
package ru.mipt.spc.magprog package ru.mipt.spc.magprog
import io.ktor.server.application.Application
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import kotlinx.html.div import kotlinx.html.div
import kotlinx.html.unsafe import kotlinx.html.unsafe
@ -25,8 +24,9 @@ import kotlin.reflect.KType
import kotlin.reflect.full.isSubtypeOf import kotlin.reflect.full.isSubtypeOf
import kotlin.reflect.typeOf import kotlin.reflect.typeOf
internal val Data<*>.published: Boolean get() = meta["published"].string != "false"
class DataSetPageContext( class DataSetPageContext(
val application: Application,
override val context: Context, override val context: Context,
val prefix: String, val prefix: String,
val dataSet: DataSet<Any>, val dataSet: DataSet<Any>,
@ -65,12 +65,13 @@ class DataSetPageContext(
} }
private val Data<*>.published: Boolean get() = meta["published"].string != "false"
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
@DFInternal @DFInternal
override fun <T : Any> resolve(type: KType, name: Name): Data<T>? { override fun <T : Any> resolve(type: KType, name: Name): Data<T>? {
val data: Data<Any> = dataSet[name] ?: return null val data: Data<Any> = dataSet[name] ?: return null
if(data.type == type) return data as Data<T>
return if (type == typeOf<Meta>() && data.type == typeOf<ByteArray>()) { return if (type == typeOf<Meta>() && data.type == typeOf<ByteArray>()) {
data as Data<ByteArray> data as Data<ByteArray>
when (data.meta[META_FILE_EXTENSION_KEY].string) { when (data.meta[META_FILE_EXTENSION_KEY].string) {
@ -94,8 +95,8 @@ class DataSetPageContext(
} }
@DFInternal @DFInternal
override fun <T : Any> resolveAll(type: KType, filter: (name: Name, meta: Meta) -> Boolean): DataSet<T> = override fun <T : Any> resolveAll(type: KType, predicate: (name: Name, meta: Meta) -> Boolean): DataSet<T> =
dataSet.select(type, filter = filter) dataSet.filterIsInstance(type, predicate = predicate)
override fun resolveHtml(name: Name): HtmlData? = runBlocking { override fun resolveHtml(name: Name): HtmlData? = runBlocking {
resolve<ByteArray>(name)?.takeIf { it.published }?.toHtmlBlock() resolve<ByteArray>(name)?.takeIf { it.published }?.toHtmlBlock()

View File

@ -24,7 +24,7 @@ interface PageContext: ContextAware {
fun <T: Any> resolve(type: KType, name: Name): Data<T>? fun <T: Any> resolve(type: KType, name: Name): Data<T>?
@DFInternal @DFInternal
fun <T: Any> resolveAll(type: KType, filter: (name: Name, meta: Meta) -> Boolean): DataSet<T> fun <T: Any> resolveAll(type: KType, predicate: (name: Name, meta: Meta) -> Boolean): DataSet<T>
/** /**
* Resolve a Html builder by its full name * Resolve a Html builder by its full name

View File

@ -0,0 +1,43 @@
package ru.mipt.spc.magprog
import space.kscience.dataforge.actions.invoke
import space.kscience.dataforge.context.Context
import space.kscience.dataforge.data.Data
import space.kscience.dataforge.data.DataSet
import space.kscience.dataforge.data.filterIsInstance
import space.kscience.dataforge.data.selectOne
import space.kscience.dataforge.meta.Meta
import space.kscience.dataforge.misc.DFInternal
import space.kscience.dataforge.names.Name
import space.kscience.snark.DirectoryDataTree
import space.kscience.snark.HtmlData
import space.kscience.snark.HtmlFragment
import space.kscience.snark.SnarkPlugin
import java.nio.file.Path
import kotlin.reflect.KType
class SnarkPageContext(
val snarkPlugin: SnarkPlugin,
val path: Path,
val prefix: String,
) : PageContext {
override val context: Context get() = snarkPlugin.context
override fun resolveRef(name: String): String = "$prefix/$name"
private val directoryDataTree by lazy { DirectoryDataTree(snarkPlugin.io, path) }
private val parsedData: DataSet<Any> by lazy { snarkPlugin.parseAction(directoryDataTree) }
@DFInternal
override fun <T : Any> resolve(type: KType, name: Name): Data<T>? = parsedData.selectOne(type, name)
@DFInternal
override fun <T : Any> resolveAll(type: KType, predicate: (name: Name, meta: Meta) -> Boolean): DataSet<T> =
parsedData.filterIsInstance(type, predicate)
override fun resolveHtml(name: Name): HtmlData? = resolve(name)
override fun resolveAllHtml(filter: (name: Name, meta: Meta) -> Boolean): Map<Name, HtmlData> =
resolveAll<HtmlFragment>(filter).dataSequence().filter { it.published }.associate { it.name to it.data }
}

View File

@ -11,8 +11,8 @@ import io.ktor.server.routing.routing
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import kotlinx.html.* import kotlinx.html.*
import space.kscience.dataforge.context.Context import space.kscience.dataforge.context.Context
import space.kscience.dataforge.context.fetch
import space.kscience.dataforge.data.await import space.kscience.dataforge.data.await
import space.kscience.dataforge.io.io
import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.meta.Meta
import space.kscience.dataforge.meta.get import space.kscience.dataforge.meta.get
import space.kscience.dataforge.meta.getIndexed import space.kscience.dataforge.meta.getIndexed
@ -266,15 +266,18 @@ context(PageContext) internal fun BODY.magProgFooter() {
internal val Person.mentorPageId get() = "mentor-${id}" internal val Person.mentorPageId get() = "mentor-${id}"
internal fun Application.magProgPage(context: Context, rootPath: Path, prefix: String = "/magprog") { internal fun Application.magProgPage(context: Context, rootPath: Path, prefix: String = "/magprog") {
val io = context.io // val io = context.io
val content = DirectoryDataTree(io, rootPath.resolve("content")) // val content = DirectoryDataTree(io, rootPath.resolve("content"))
//
// val magprogPageContext: PageContext = DataSetPageContext(context, prefix, content)
val snark = context.fetch(SnarkPlugin)
val magprogPageContext: PageContext = DataSetPageContext(this, context, prefix, content) val magProgPageContext = SnarkPageContext(snark, rootPath.resolve("content"), prefix)
routing { routing {
route(prefix) { route(prefix) {
with(magprogPageContext) { with(magProgPageContext) {
static { static {
files(rootPath.resolve("assets").toFile()) files(rootPath.resolve("assets").toFile())
} }

View File

@ -0,0 +1,17 @@
package space.kscience.snark
import io.ktor.http.ContentType
import kotlinx.serialization.json.Json
import space.kscience.dataforge.meta.Meta
import space.kscience.dataforge.meta.toMeta
import kotlin.reflect.KType
import kotlin.reflect.typeOf
object SnarkJsonParser: SnarkParser<Meta> {
override val contentType: ContentType = ContentType.Application.Json
override val fileExtensions: Set<String> = setOf("json")
override val resultType: KType= typeOf<Meta>()
override suspend fun parse(bytes: ByteArray, meta: Meta): Meta =
Json.parseToJsonElement(bytes.decodeToString()).toMeta()
}

View File

@ -0,0 +1,31 @@
package space.kscience.snark
import io.ktor.http.ContentType
import kotlinx.html.div
import kotlinx.html.unsafe
import org.intellij.markdown.flavours.commonmark.CommonMarkFlavourDescriptor
import org.intellij.markdown.html.HtmlGenerator
import org.intellij.markdown.parser.MarkdownParser
import space.kscience.dataforge.meta.Meta
import kotlin.reflect.KType
import kotlin.reflect.typeOf
object SnarkMarkdownParser:SnarkParser<HtmlFragment> {
override val contentType: ContentType = ContentType.Text.Html
override val fileExtensions: Set<String> = setOf("markdown", "mdown", "mkdn", "mkd", "md")
override val resultType: KType = typeOf<HtmlFragment>()
private val markdownFlavor = CommonMarkFlavourDescriptor()
private val markdownParser = MarkdownParser(markdownFlavor)
override suspend fun parse(bytes: ByteArray, meta: Meta): HtmlFragment = {
val src = bytes.decodeToString()
div{
val parsedTree = markdownParser.buildMarkdownTreeFromString(src)
unsafe {
+HtmlGenerator(src, parsedTree, markdownFlavor).generateHtml()
}
}
}
}

View File

@ -36,14 +36,14 @@ interface SnarkParser<R : Any> {
@OptIn(DFExperimental::class) @OptIn(DFExperimental::class)
class SnarkPlugin : AbstractPlugin() { class SnarkPlugin : AbstractPlugin() {
val yaml by require(YamlPlugin) private val yaml by require(YamlPlugin)
val io get() = yaml.io val io get() = yaml.io
override val tag: PluginTag get() = Companion.tag override val tag: PluginTag get() = Companion.tag
private val parsers: Map<Name, SnarkParser<*>> by lazy { context.gather(SnarkParser.TYPE, true) } private val parsers: Map<Name, SnarkParser<*>> by lazy { context.gather(SnarkParser.TYPE, true) }
private val parseAction = Action.map<ByteArray, Any> { val parseAction = Action.map<ByteArray, Any> {
val parser: SnarkParser<*>? = parsers.values.filter { parser -> val parser: SnarkParser<*>? = parsers.values.filter { parser ->
parser.contentType.toString() == meta["contentType"].string || parser.contentType.toString() == meta["contentType"].string ||
meta[DirectoryDataTree.META_FILE_EXTENSION_KEY].string in parser.fileExtensions meta[DirectoryDataTree.META_FILE_EXTENSION_KEY].string in parser.fileExtensions
@ -65,7 +65,10 @@ class SnarkPlugin : AbstractPlugin() {
override fun content(target: String): Map<Name, Any> { override fun content(target: String): Map<Name, Any> {
return when(target){ return when(target){
SnarkParser.TYPE -> mapOf( SnarkParser.TYPE -> mapOf(
"html".asName() to SnarkHtmlParser "html".asName() to SnarkHtmlParser,
"markdown".asName() to SnarkMarkdownParser,
"json".asName() to SnarkJsonParser,
"yaml".asName() to SnarkYamlParser
) )
else ->super.content(target) else ->super.content(target)
} }

View File

@ -0,0 +1,18 @@
package space.kscience.snark
import io.ktor.http.ContentType
import space.kscience.dataforge.io.asBinary
import space.kscience.dataforge.io.readObject
import space.kscience.dataforge.io.yaml.YamlMetaFormat
import space.kscience.dataforge.meta.Meta
import kotlin.reflect.KType
import kotlin.reflect.typeOf
object SnarkYamlParser : SnarkParser<Meta> {
override val contentType: ContentType = ContentType.Application.Json
override val fileExtensions: Set<String> = setOf("yaml", "yml")
override val resultType: KType = typeOf<Meta>()
override suspend fun parse(bytes: ByteArray, meta: Meta): Meta =
YamlMetaFormat.readObject(bytes.asBinary())
}

View File

@ -5,10 +5,6 @@ section_title: Контакты
language: ru language: ru
--- ---
Сайт лаборатории: [https://npm.mipt.ru](/)
Страница направления в JetBrains Research: [https://research.jetbrains.org/groups/npm/](https://research.jetbrains.org/groups/npm/).
Все вопросы можно задать в телеграм-канале лаборатории: [https://t.me/mipt_npm](https://t.me/mipt_npm). Все вопросы можно задать в телеграм-канале лаборатории: [https://t.me/mipt_npm](https://t.me/mipt_npm).
Также можно писать на электронную почту: <a href='mailto&#58;&#110;p&#109;&#64;m%&#54;&#57;%70&#116;&#46;ru'>npm&#64;mip&#116;&#46;ru</a>. Также можно писать на электронную почту: <a href='mailto&#58;&#110;p&#109;&#64;m%&#54;&#57;%70&#116;&#46;ru'>npm&#64;mip&#116;&#46;ru</a>.

View File

@ -1,11 +1,11 @@
--- ---
content_type: magprog content_type: magprog
magprog_section: intro magprog_section: intro
section_title: Магистерская программа section_title: О программе
language: ru language: ru
--- ---
Магистерская программа МФТИ **&laquo;Разработка и применение программного обеспечения в физических исследованиях&raquo;** создана на базе [лаборатории методов ядерно-физических экспериментов (ЛМЯФЭ)](/) при поддержке двух школ МФТИ: Физтех-школы физики и исследований им. Ландау ([ЛФИ](https://mipt.ru/education/departments/lpr/)) и Физтех-школы прикладной математики и информатики ([ФПМИ](https://mipt.ru/education/departments/fpmi/)) и ряда академических и промышленных партнеров. В ее основе лежит взаимодействие студента и [научного руководителя](#mentors). Магистерская программа МФТИ **"Научное программное обеспечение"** (старое название: **"Разработка и применение программного обеспечения в физических исследованиях"**) создана при поддержке двух школ МФТИ: Физтех-школы физики и исследований им. Ландау ([ЛФИ](https://mipt.ru/education/departments/lpr/)) и Физтех-школы прикладной математики и информатики ([ФПМИ](https://mipt.ru/education/departments/fpmi/)) и ряда академических и промышленных партнеров. В ее основе лежит взаимодействие студента и [научного руководителя](#mentors).
Цель создания программы &mdash; объединение усилий ученых и программистов для разработки лучших компьютерных решений и применения этих решений в области фундаментальной и прикладной науки и инженерии. Цель создания программы &mdash; объединение усилий ученых и программистов для разработки лучших компьютерных решений и применения этих решений в области фундаментальной и прикладной науки и инженерии.

View File

@ -3,6 +3,9 @@ package ru.mipt
import io.ktor.client.request.get import io.ktor.client.request.get
import io.ktor.http.HttpStatusCode import io.ktor.http.HttpStatusCode
import io.ktor.server.testing.testApplication import io.ktor.server.testing.testApplication
import ru.mipt.spc.magprog.magProgPage
import space.kscience.dataforge.context.Context
import space.kscience.snark.SnarkPlugin
import java.nio.file.Path import java.nio.file.Path
import kotlin.test.Test import kotlin.test.Test
import kotlin.test.assertEquals import kotlin.test.assertEquals
@ -10,8 +13,12 @@ import kotlin.test.assertEquals
class ApplicationTest { class ApplicationTest {
@Test @Test
fun testRoot() = testApplication { fun testRoot() = testApplication {
val context = Context("spc-site") {
plugin(SnarkPlugin)
}
application { application {
magProgPage(rootPath = Path.of(javaClass.getResource("/magprog")!!.toURI())) magProgPage(context, rootPath = Path.of(javaClass.getResource("/magprog")!!.toURI()))
} }
client.get("/magprog").apply { client.get("/magprog").apply {
assertEquals(HttpStatusCode.OK, status) assertEquals(HttpStatusCode.OK, status)