Refactor
This commit is contained in:
parent
bafbf200f2
commit
4cef4ee402
@ -20,13 +20,15 @@ application {
|
|||||||
applicationDefaultJvmArgs = listOf("-Dio.ktor.development=$isDevelopment")
|
applicationDefaultJvmArgs = listOf("-Dio.ktor.development=$isDevelopment")
|
||||||
}
|
}
|
||||||
|
|
||||||
//tasks.withType<KotlinCompile>{
|
tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile>{
|
||||||
// kotlinOptions{
|
kotlinOptions{
|
||||||
// freeCompilerArgs = freeCompilerArgs + "-Xcontext-receivers"
|
languageVersion = "1.7"
|
||||||
// }
|
apiVersion = "1.7"
|
||||||
//}
|
freeCompilerArgs = freeCompilerArgs + "-Xcontext-receivers"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
val dataforgeVersion by extra("0.6.0-dev-3")
|
val dataforgeVersion by extra("0.6.0-dev-4")
|
||||||
val ktorVersion = KScienceVersions.ktorVersion
|
val ktorVersion = KScienceVersions.ktorVersion
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
@ -6,7 +6,7 @@ import io.ktor.server.application.call
|
|||||||
import io.ktor.server.application.install
|
import io.ktor.server.application.install
|
||||||
import io.ktor.server.engine.embeddedServer
|
import io.ktor.server.engine.embeddedServer
|
||||||
import io.ktor.server.html.respondHtml
|
import io.ktor.server.html.respondHtml
|
||||||
import io.ktor.server.http.content.resources
|
import io.ktor.server.http.content.files
|
||||||
import io.ktor.server.http.content.static
|
import io.ktor.server.http.content.static
|
||||||
import io.ktor.server.netty.Netty
|
import io.ktor.server.netty.Netty
|
||||||
import io.ktor.server.plugins.statuspages.StatusPages
|
import io.ktor.server.plugins.statuspages.StatusPages
|
||||||
@ -14,43 +14,48 @@ import io.ktor.server.response.respond
|
|||||||
import io.ktor.server.routing.get
|
import io.ktor.server.routing.get
|
||||||
import io.ktor.server.routing.route
|
import io.ktor.server.routing.route
|
||||||
import io.ktor.server.routing.routing
|
import io.ktor.server.routing.routing
|
||||||
|
import kotlinx.css.CssBuilder
|
||||||
|
import kotlinx.html.CommonAttributeGroupFacade
|
||||||
|
import kotlinx.html.style
|
||||||
import ru.mipt.plugins.configureTemplating
|
import ru.mipt.plugins.configureTemplating
|
||||||
import ru.mipt.spc.magprog.DataSetSiteContext
|
import ru.mipt.spc.magprog.DataSetPageContext
|
||||||
import ru.mipt.spc.magprog.SiteContext
|
import ru.mipt.spc.magprog.PageContext
|
||||||
import ru.mipt.spc.magprog.magProgPage
|
import ru.mipt.spc.magprog.magProgPage
|
||||||
import space.kscience.dataforge.context.Context
|
import space.kscience.dataforge.context.Context
|
||||||
import space.kscience.dataforge.io.io
|
import space.kscience.dataforge.io.io
|
||||||
import space.kscience.dataforge.io.yaml.YamlPlugin
|
|
||||||
import space.kscience.snark.DirectoryDataTree
|
import space.kscience.snark.DirectoryDataTree
|
||||||
|
import space.kscience.snark.SnarkPlugin
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
|
|
||||||
|
fun CommonAttributeGroupFacade.css(block: CssBuilder.() -> Unit) {
|
||||||
|
style = CssBuilder().block().toString()
|
||||||
|
}
|
||||||
|
|
||||||
class AuthenticationException : RuntimeException()
|
class AuthenticationException : RuntimeException()
|
||||||
class AuthorizationException : RuntimeException()
|
class AuthorizationException : RuntimeException()
|
||||||
|
|
||||||
internal fun Application.magProgSite(prefix: String = "magprog"){
|
internal fun Application.magProgPage(rootPath: Path, prefix: String = "magprog") {
|
||||||
val context = Context("spc-site") {
|
val context = Context("spc-site") {
|
||||||
plugin(YamlPlugin)
|
plugin(SnarkPlugin)
|
||||||
}
|
}
|
||||||
|
|
||||||
val io = context.io
|
val io = context.io
|
||||||
val directory = javaClass.getResource("/magprog/content")!!.toURI()
|
val content = DirectoryDataTree(io, rootPath.resolve("content"))
|
||||||
val content = DirectoryDataTree(io, Path.of(directory))
|
|
||||||
|
|
||||||
|
|
||||||
val magprogSiteContext: SiteContext = DataSetSiteContext(context,"magprog", content)
|
val magprogPageContext: PageContext = DataSetPageContext(context, prefix, content)
|
||||||
|
|
||||||
routing {
|
routing {
|
||||||
route(prefix) {
|
route(prefix) {
|
||||||
get {
|
get {
|
||||||
call.respondHtml {
|
call.respondHtml {
|
||||||
with(magprogSiteContext){
|
with(magprogPageContext) {
|
||||||
magProgPage()
|
magProgPage()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
static {
|
static {
|
||||||
resources("magprog/assets")
|
files(rootPath.resolve("assets").toFile())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -59,12 +64,12 @@ internal fun Application.magProgSite(prefix: String = "magprog"){
|
|||||||
|
|
||||||
fun main() {
|
fun main() {
|
||||||
embeddedServer(Netty, port = 8080, host = "0.0.0.0") {
|
embeddedServer(Netty, port = 8080, host = "0.0.0.0") {
|
||||||
magProgSite()
|
magProgPage(rootPath = Path.of(javaClass.getResource("/magprog")!!.toURI()))
|
||||||
install(StatusPages) {
|
install(StatusPages) {
|
||||||
exception<AuthenticationException> { call, cause ->
|
exception<AuthenticationException> { call, _ ->
|
||||||
call.respond(HttpStatusCode.Unauthorized)
|
call.respond(HttpStatusCode.Unauthorized)
|
||||||
}
|
}
|
||||||
exception<AuthorizationException> { call, cause ->
|
exception<AuthorizationException> { call, _ ->
|
||||||
call.respond(HttpStatusCode.Forbidden)
|
call.respond(HttpStatusCode.Forbidden)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,15 +19,16 @@ import space.kscience.dataforge.meta.toMeta
|
|||||||
import space.kscience.dataforge.misc.DFInternal
|
import space.kscience.dataforge.misc.DFInternal
|
||||||
import space.kscience.dataforge.names.Name
|
import space.kscience.dataforge.names.Name
|
||||||
import space.kscience.snark.DirectoryDataTree.Companion.META_FILE_EXTENSION_KEY
|
import space.kscience.snark.DirectoryDataTree.Companion.META_FILE_EXTENSION_KEY
|
||||||
|
import space.kscience.snark.HtmlData
|
||||||
import kotlin.reflect.KType
|
import kotlin.reflect.KType
|
||||||
import kotlin.reflect.full.isSubtypeOf
|
import kotlin.reflect.full.isSubtypeOf
|
||||||
import kotlin.reflect.typeOf
|
import kotlin.reflect.typeOf
|
||||||
|
|
||||||
class DataSetSiteContext(
|
class DataSetPageContext(
|
||||||
override val context: Context,
|
override val context: Context,
|
||||||
val prefix: String,
|
val prefix: String,
|
||||||
val dataSet: DataSet<Any>,
|
val dataSet: DataSet<Any>,
|
||||||
) : SiteContext {
|
) : PageContext {
|
||||||
|
|
||||||
override fun resolveResource(name: String): String = "$prefix/$name"
|
override fun resolveResource(name: String): String = "$prefix/$name"
|
||||||
|
|
@ -9,10 +9,11 @@ import space.kscience.dataforge.meta.string
|
|||||||
import space.kscience.dataforge.misc.DFInternal
|
import space.kscience.dataforge.misc.DFInternal
|
||||||
import space.kscience.dataforge.names.Name
|
import space.kscience.dataforge.names.Name
|
||||||
import space.kscience.dataforge.names.startsWith
|
import space.kscience.dataforge.names.startsWith
|
||||||
|
import space.kscience.snark.HtmlData
|
||||||
import kotlin.reflect.KType
|
import kotlin.reflect.KType
|
||||||
import kotlin.reflect.typeOf
|
import kotlin.reflect.typeOf
|
||||||
|
|
||||||
interface SiteContext: ContextAware {
|
interface PageContext: ContextAware {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Resolve a resource full path by its name
|
* Resolve a resource full path by its name
|
||||||
@ -37,12 +38,12 @@ interface SiteContext: ContextAware {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@OptIn(DFInternal::class)
|
@OptIn(DFInternal::class)
|
||||||
inline fun <reified T: Any> SiteContext.resolve(name: Name): Data<T>? = resolve(typeOf<T>(), name)
|
inline fun <reified T: Any> PageContext.resolve(name: Name): Data<T>? = resolve(typeOf<T>(), name)
|
||||||
|
|
||||||
@OptIn(DFInternal::class)
|
@OptIn(DFInternal::class)
|
||||||
inline fun <reified T:Any> SiteContext.resolveAll(noinline filter: (name: Name, meta: Meta) -> Boolean): DataSet<T> =
|
inline fun <reified T:Any> PageContext.resolveAll(noinline filter: (name: Name, meta: Meta) -> Boolean): DataSet<T> =
|
||||||
resolveAll(typeOf<T>(), filter)
|
resolveAll(typeOf<T>(), filter)
|
||||||
|
|
||||||
fun SiteContext.findByType(contentType: String, baseName: Name = Name.EMPTY) = resolveAllHtml { name, meta ->
|
fun PageContext.findByType(contentType: String, baseName: Name = Name.EMPTY) = resolveAllHtml { name, meta ->
|
||||||
name.startsWith(baseName) && meta["content_type"].string == contentType
|
name.startsWith(baseName) && meta["content_type"].string == contentType
|
||||||
}
|
}
|
@ -2,14 +2,19 @@ package ru.mipt.spc.magprog
|
|||||||
|
|
||||||
import kotlinx.css.*
|
import kotlinx.css.*
|
||||||
import kotlinx.html.*
|
import kotlinx.html.*
|
||||||
|
import ru.mipt.css
|
||||||
import space.kscience.dataforge.meta.string
|
import space.kscience.dataforge.meta.string
|
||||||
|
import space.kscience.snark.HtmlData
|
||||||
|
import space.kscience.snark.htmlData
|
||||||
|
import space.kscience.snark.id
|
||||||
|
import space.kscience.snark.order
|
||||||
|
|
||||||
class Person(val block: HtmlData) : HtmlData by block {
|
class Person(val block: HtmlData) : HtmlData by block {
|
||||||
val name: String by meta.string { error("Mentor name is not defined") }
|
val name: String by meta.string { error("Mentor name is not defined") }
|
||||||
val photo: String? by meta.string()
|
val photo: String? by meta.string()
|
||||||
}
|
}
|
||||||
|
|
||||||
context(SiteContext)
|
context(PageContext)
|
||||||
private fun FlowContent.personCards(list: List<Person>, prefix: String) {
|
private fun FlowContent.personCards(list: List<Person>, prefix: String) {
|
||||||
list.forEach { mentor ->
|
list.forEach { mentor ->
|
||||||
section {
|
section {
|
||||||
@ -34,7 +39,7 @@ private fun FlowContent.personCards(list: List<Person>, prefix: String) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
context(SiteContext)
|
context(PageContext)
|
||||||
fun FlowContent.mentors() {
|
fun FlowContent.mentors() {
|
||||||
val mentors = findByType("magprog_mentor").values.map {
|
val mentors = findByType("magprog_mentor").values.map {
|
||||||
Person(it)
|
Person(it)
|
||||||
@ -54,7 +59,7 @@ fun FlowContent.mentors() {
|
|||||||
personCards(mentors,"mentor")
|
personCards(mentors,"mentor")
|
||||||
}
|
}
|
||||||
|
|
||||||
context(SiteContext)
|
context(PageContext)
|
||||||
fun FlowContent.team() {
|
fun FlowContent.team() {
|
||||||
val team = findByType("magprog_team").values.map { Person(it) }.sortedBy { it.order }
|
val team = findByType("magprog_team").values.map { Person(it) }.sortedBy { it.order }
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@ package ru.mipt.spc.magprog
|
|||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
import kotlinx.css.*
|
import kotlinx.css.*
|
||||||
import kotlinx.html.*
|
import kotlinx.html.*
|
||||||
|
import ru.mipt.css
|
||||||
import space.kscience.dataforge.data.await
|
import space.kscience.dataforge.data.await
|
||||||
import space.kscience.dataforge.meta.Meta
|
import space.kscience.dataforge.meta.Meta
|
||||||
import space.kscience.dataforge.meta.get
|
import space.kscience.dataforge.meta.get
|
||||||
@ -11,6 +12,9 @@ import space.kscience.dataforge.meta.string
|
|||||||
import space.kscience.dataforge.names.Name
|
import space.kscience.dataforge.names.Name
|
||||||
import space.kscience.dataforge.names.asName
|
import space.kscience.dataforge.names.asName
|
||||||
import space.kscience.dataforge.names.plus
|
import space.kscience.dataforge.names.plus
|
||||||
|
import space.kscience.snark.HtmlData
|
||||||
|
import space.kscience.snark.htmlData
|
||||||
|
import space.kscience.snark.id
|
||||||
|
|
||||||
//fun CssBuilder.magProgCss() {
|
//fun CssBuilder.magProgCss() {
|
||||||
// rule(".magprog-body") {
|
// rule(".magprog-body") {
|
||||||
@ -66,13 +70,13 @@ private val PROGRAM_PATH: Name = CONTENT_NODE_NAME + "program"
|
|||||||
private val RECOMMENDED_COURSES_PATH: Name = CONTENT_NODE_NAME + "recommendedCourses"
|
private val RECOMMENDED_COURSES_PATH: Name = CONTENT_NODE_NAME + "recommendedCourses"
|
||||||
private val PARTNERS_PATH: Name = CONTENT_NODE_NAME + "partners"
|
private val PARTNERS_PATH: Name = CONTENT_NODE_NAME + "partners"
|
||||||
|
|
||||||
context(SiteContext) private fun FlowContent.programSection() {
|
context(PageContext) private fun FlowContent.programSection() {
|
||||||
val programBlock = resolveHtml(PROGRAM_PATH)!!
|
val programBlock = resolveHtml(PROGRAM_PATH)!!
|
||||||
val recommendedBlock = resolveHtml(RECOMMENDED_COURSES_PATH)!!
|
val recommendedBlock = resolveHtml(RECOMMENDED_COURSES_PATH)!!
|
||||||
div("inner") {
|
div("inner") {
|
||||||
h2 { +"Учебная программа" }
|
h2 { +"Учебная программа" }
|
||||||
htmlData(programBlock)
|
htmlData(programBlock)
|
||||||
button(classes = "fit btn btn-primary btn-lg") {
|
button(classes = "fit btn btn-secondary") {
|
||||||
attributes["data-bs-toggle"] = "collapse"
|
attributes["data-bs-toggle"] = "collapse"
|
||||||
attributes["data-bs-target"] = "#recommended-courses-collapse-text"
|
attributes["data-bs-target"] = "#recommended-courses-collapse-text"
|
||||||
attributes["aria-expanded"] = "false"
|
attributes["aria-expanded"] = "false"
|
||||||
@ -81,14 +85,14 @@ context(SiteContext) private fun FlowContent.programSection() {
|
|||||||
}
|
}
|
||||||
div("collapse pt-3") {
|
div("collapse pt-3") {
|
||||||
id = "recommended-courses-collapse-text"
|
id = "recommended-courses-collapse-text"
|
||||||
div("card card-body") {
|
div {
|
||||||
htmlData(recommendedBlock)
|
htmlData(recommendedBlock)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
context(SiteContext) private fun FlowContent.partners() {
|
context(PageContext) private fun FlowContent.partners() {
|
||||||
//val partnersData: Meta = resolve<Any>(PARTNERS_PATH)?.meta ?: Meta.EMPTY
|
//val partnersData: Meta = resolve<Any>(PARTNERS_PATH)?.meta ?: Meta.EMPTY
|
||||||
val partnersData: Meta = runBlocking { resolve<Meta>(PARTNERS_PATH)?.await()} ?: Meta.EMPTY
|
val partnersData: Meta = runBlocking { resolve<Meta>(PARTNERS_PATH)?.await()} ?: Meta.EMPTY
|
||||||
div("inner") {
|
div("inner") {
|
||||||
@ -113,7 +117,7 @@ context(SiteContext) private fun FlowContent.partners() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
context(SiteContext) fun HTML.magProgPage() {
|
context(PageContext) fun HTML.magProgPage() {
|
||||||
val sections = listOf<MagProgSection>(
|
val sections = listOf<MagProgSection>(
|
||||||
wrapSection(resolveHtml(INTRO_PATH)!!, "intro"),
|
wrapSection(resolveHtml(INTRO_PATH)!!, "intro"),
|
||||||
MagProgSection(
|
MagProgSection(
|
||||||
@ -158,6 +162,10 @@ context(SiteContext) fun HTML.magProgPage() {
|
|||||||
name = "viewport"
|
name = "viewport"
|
||||||
content = "width=device-width, initial-scale=1, user-scalable=no"
|
content = "width=device-width, initial-scale=1, user-scalable=no"
|
||||||
}
|
}
|
||||||
|
link {
|
||||||
|
rel = "stylesheet"
|
||||||
|
href = resolveResource("css/bootstrap.min.css")
|
||||||
|
}
|
||||||
link {
|
link {
|
||||||
rel = "stylesheet"
|
rel = "stylesheet"
|
||||||
href = resolveResource("css/main.css")
|
href = resolveResource("css/main.css")
|
||||||
@ -237,6 +245,9 @@ context(SiteContext) fun HTML.magProgPage() {
|
|||||||
script {
|
script {
|
||||||
src = resolveResource("js/util.js")
|
src = resolveResource("js/util.js")
|
||||||
}
|
}
|
||||||
|
script {
|
||||||
|
src = resolveResource("js/bootstrap.min.js")
|
||||||
|
}
|
||||||
script {
|
script {
|
||||||
src = resolveResource("js/main.js")
|
src = resolveResource("js/main.js")
|
||||||
}
|
}
|
||||||
|
@ -1,9 +0,0 @@
|
|||||||
package ru.mipt.spc.magprog
|
|
||||||
|
|
||||||
import kotlinx.css.CssBuilder
|
|
||||||
import kotlinx.html.CommonAttributeGroupFacade
|
|
||||||
import kotlinx.html.style
|
|
||||||
|
|
||||||
fun CommonAttributeGroupFacade.css(block: CssBuilder.() -> Unit) {
|
|
||||||
style = CssBuilder().block().toString()
|
|
||||||
}
|
|
@ -13,10 +13,8 @@ import space.kscience.dataforge.names.NameToken
|
|||||||
import space.kscience.dataforge.names.asName
|
import space.kscience.dataforge.names.asName
|
||||||
import space.kscience.dataforge.names.plus
|
import space.kscience.dataforge.names.plus
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
import kotlin.io.path.extension
|
import java.nio.file.attribute.BasicFileAttributes
|
||||||
import kotlin.io.path.isDirectory
|
import kotlin.io.path.*
|
||||||
import kotlin.io.path.listDirectoryEntries
|
|
||||||
import kotlin.io.path.nameWithoutExtension
|
|
||||||
import kotlin.reflect.KType
|
import kotlin.reflect.KType
|
||||||
import kotlin.reflect.typeOf
|
import kotlin.reflect.typeOf
|
||||||
|
|
||||||
@ -32,7 +30,10 @@ class DirectoryDataTree(val io: IOPlugin, val path: Path) : DataTree<ByteArray>
|
|||||||
val meta = envelope.meta.copy {
|
val meta = envelope.meta.copy {
|
||||||
META_FILE_PATH_KEY put filePath.toString()
|
META_FILE_PATH_KEY put filePath.toString()
|
||||||
META_FILE_EXTENSION_KEY put filePath.extension
|
META_FILE_EXTENSION_KEY put filePath.extension
|
||||||
//TODO add other file information
|
|
||||||
|
val attributes = filePath.readAttributes<BasicFileAttributes>()
|
||||||
|
META_FILE_UPDATE_TIME_KEY put attributes.lastModifiedTime().toInstant().toString()
|
||||||
|
META_FILE_CREATE_TIME_KEY put attributes.creationTime().toInstant().toString()
|
||||||
}
|
}
|
||||||
return Data(meta){
|
return Data(meta){
|
||||||
envelope.data?.toByteArray() ?: ByteArray(0)
|
envelope.data?.toByteArray() ?: ByteArray(0)
|
||||||
@ -57,6 +58,8 @@ class DirectoryDataTree(val io: IOPlugin, val path: Path) : DataTree<ByteArray>
|
|||||||
val META_FILE_KEY = "file".asName()
|
val META_FILE_KEY = "file".asName()
|
||||||
val META_FILE_PATH_KEY = META_FILE_KEY + "path"
|
val META_FILE_PATH_KEY = META_FILE_KEY + "path"
|
||||||
val META_FILE_EXTENSION_KEY = META_FILE_KEY + "extension"
|
val META_FILE_EXTENSION_KEY = META_FILE_KEY + "extension"
|
||||||
|
val META_FILE_CREATE_TIME_KEY = META_FILE_KEY +"created"
|
||||||
|
val META_FILE_UPDATE_TIME_KEY = META_FILE_KEY +"update"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
package ru.mipt.spc.magprog
|
package space.kscience.snark
|
||||||
|
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
import kotlinx.html.FlowContent
|
import kotlinx.html.FlowContent
|
20
src/main/kotlin/space/kscience/snark/SnarkHtmlParser.kt
Normal file
20
src/main/kotlin/space/kscience/snark/SnarkHtmlParser.kt
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
package space.kscience.snark
|
||||||
|
|
||||||
|
import io.ktor.http.ContentType
|
||||||
|
import kotlinx.html.div
|
||||||
|
import kotlinx.html.unsafe
|
||||||
|
import space.kscience.dataforge.meta.Meta
|
||||||
|
import kotlin.reflect.KType
|
||||||
|
import kotlin.reflect.typeOf
|
||||||
|
|
||||||
|
object SnarkHtmlParser:SnarkParser<HtmlFragment> {
|
||||||
|
override val contentType: ContentType = ContentType.Text.Html
|
||||||
|
override val fileExtensions: Set<String> = setOf("html")
|
||||||
|
override val resultType: KType = typeOf<HtmlFragment>()
|
||||||
|
|
||||||
|
override suspend fun parse(bytes: ByteArray, meta: Meta): HtmlFragment = {
|
||||||
|
div{
|
||||||
|
unsafe { +bytes.decodeToString() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -8,8 +8,10 @@ import space.kscience.dataforge.io.yaml.YamlPlugin
|
|||||||
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.string
|
import space.kscience.dataforge.meta.string
|
||||||
|
import space.kscience.dataforge.misc.DFExperimental
|
||||||
import space.kscience.dataforge.misc.Type
|
import space.kscience.dataforge.misc.Type
|
||||||
import space.kscience.dataforge.names.Name
|
import space.kscience.dataforge.names.Name
|
||||||
|
import space.kscience.dataforge.names.asName
|
||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
import kotlin.reflect.KType
|
import kotlin.reflect.KType
|
||||||
|
|
||||||
@ -19,18 +21,20 @@ interface SnarkParser<R : Any> {
|
|||||||
|
|
||||||
val fileExtensions: Set<String>
|
val fileExtensions: Set<String>
|
||||||
|
|
||||||
val priority: Int
|
val priority: Int get() = DEFAULT_PRIORITY
|
||||||
|
|
||||||
val resultType: KType
|
val resultType: KType
|
||||||
|
|
||||||
suspend fun parse(bytes: ByteArray): R
|
suspend fun parse(bytes: ByteArray, meta: Meta): R
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val TYPE = "snark.parser"
|
const val TYPE = "snark.parser"
|
||||||
|
const val DEFAULT_PRIORITY = 10
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@OptIn(DFExperimental::class)
|
||||||
class SnarkPlugin : AbstractPlugin() {
|
class SnarkPlugin : AbstractPlugin() {
|
||||||
val yaml by require(YamlPlugin)
|
val yaml by require(YamlPlugin)
|
||||||
val io get() = yaml.io
|
val io get() = yaml.io
|
||||||
@ -40,13 +44,30 @@ class SnarkPlugin : AbstractPlugin() {
|
|||||||
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> {
|
private val parseAction = Action.map<ByteArray, Any> {
|
||||||
result { bytes ->
|
val parser: SnarkParser<*>? = parsers.values.filter { parser ->
|
||||||
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
|
||||||
}.maxByOrNull {
|
}.maxByOrNull {
|
||||||
it.priority
|
it.priority
|
||||||
}?.parse(bytes) ?: bytes
|
}
|
||||||
|
|
||||||
|
//ensure that final type is correct
|
||||||
|
if (parser == null) {
|
||||||
|
logger.warn { "The parser is not found for data with meta $meta" }
|
||||||
|
result { it }
|
||||||
|
} else {
|
||||||
|
result(parser.resultType) { bytes ->
|
||||||
|
parser.parse(bytes, meta)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun content(target: String): Map<Name, Any> {
|
||||||
|
return when(target){
|
||||||
|
SnarkParser.TYPE -> mapOf(
|
||||||
|
"html".asName() to SnarkHtmlParser
|
||||||
|
)
|
||||||
|
else ->super.content(target)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
7
src/main/resources/magprog/assets/css/bootstrap.min.css
vendored
Normal file
7
src/main/resources/magprog/assets/css/bootstrap.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
7
src/main/resources/magprog/assets/js/bootstrap.min.js
vendored
Normal file
7
src/main/resources/magprog/assets/js/bootstrap.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
@ -3,6 +3,7 @@ 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 java.nio.file.Path
|
||||||
import kotlin.test.Test
|
import kotlin.test.Test
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
@ -10,7 +11,7 @@ class ApplicationTest {
|
|||||||
@Test
|
@Test
|
||||||
fun testRoot() = testApplication {
|
fun testRoot() = testApplication {
|
||||||
application {
|
application {
|
||||||
magProgSite()
|
magProgPage(rootPath = Path.of(javaClass.getResource("/magprog")!!.toURI()))
|
||||||
}
|
}
|
||||||
client.get("/magprog").apply {
|
client.get("/magprog").apply {
|
||||||
assertEquals(HttpStatusCode.OK, status)
|
assertEquals(HttpStatusCode.OK, status)
|
||||||
|
Loading…
Reference in New Issue
Block a user