Add text preprocessors

This commit is contained in:
Alexander Nozik 2022-06-25 23:27:29 +03:00
parent 781b185349
commit bf884732c1
No known key found for this signature in database
GPG Key ID: F7FCF2DD25C71357
9 changed files with 90 additions and 25 deletions

View File

@ -1,6 +1,7 @@
--- ---
type: page type: page
title: Consulting title: Consulting
transformation: snark.replaceLinks
language: en language: en
--- ---
@ -20,7 +21,7 @@ Our research, which primarily includes open source projects, has already been ap
<img src="images/pic03.jpg" alt="" /> <img src="images/pic03.jpg" alt="" />
</span> </span>
<header class="major"> <header class="major">
<h3><a href="consulting/research" class="link">Research</a></h3> <h3><a href="${homeRef}/consulting/research" class="link">Research</a></h3>
<p>Analytics, prototyping and research for fundamental science and industry.</p> <p>Analytics, prototyping and research for fundamental science and industry.</p>
</header> </header>
</article> </article>
@ -29,7 +30,7 @@ Our research, which primarily includes open source projects, has already been ap
<img src="images/pic04.jpg" alt="" /> <img src="images/pic04.jpg" alt="" />
</span> </span>
<header class="major"> <header class="major">
<h3><a href="consulting/expert" class="link">Expert activity</a></h3> <h3><a href="${homeRef}/consulting/expert" class="link">Expert activity</a></h3>
<p>Analysis and improvement of existing scientific software and projects.</p> <p>Analysis and improvement of existing scientific software and projects.</p>
</header> </header>
</article> </article>
@ -38,7 +39,7 @@ Our research, which primarily includes open source projects, has already been ap
<img src="images/pic05.jpg" alt="" /> <img src="images/pic05.jpg" alt="" />
</span> </span>
<header class="major"> <header class="major">
<h3><a href="consulting/architecture" class="link">Architecture</a></h3> <h3><a href="${homeRef}/consulting/architecture" class="link">Architecture</a></h3>
<p>Design the architecture for scientific and technological applications.</p> <p>Design the architecture for scientific and technological applications.</p>
</header> </header>
</article> </article>
@ -47,7 +48,7 @@ Our research, which primarily includes open source projects, has already been ap
<img src="images/pic06.jpg" alt="" /> <img src="images/pic06.jpg" alt="" />
</span> </span>
<header class="major"> <header class="major">
<h3><a href="consulting/development" class="link">Development</a></h3> <h3><a href="${homeRef}/consulting/development" class="link">Development</a></h3>
<p>The development and support of open-source and in-house solutions( libraries, frameworks and end-user application).</p> <p>The development and support of open-source and in-house solutions( libraries, frameworks and end-user application).</p>
</header> </header>
</article> </article>

View File

@ -6,19 +6,23 @@ import kotlinx.html.FlowContent
import kotlinx.html.TagConsumer import kotlinx.html.TagConsumer
import space.kscience.dataforge.data.Data import space.kscience.dataforge.data.Data
import space.kscience.dataforge.data.await import space.kscience.dataforge.data.await
import space.kscience.dataforge.meta.Meta
import space.kscience.dataforge.meta.get import space.kscience.dataforge.meta.get
import space.kscience.dataforge.meta.int import space.kscience.dataforge.meta.int
import space.kscience.dataforge.meta.string import space.kscience.dataforge.meta.string
//TODO replace by VisionForge type //TODO replace by VisionForge type
typealias HtmlFragment = context(PageBuilder, TagConsumer<*>) () -> Unit //typealias HtmlFragment = context(PageBuilder, TagConsumer<*>) () -> Unit
fun interface HtmlFragment {
fun TagConsumer<*>.renderFragment(page: PageBuilder)
//TODO move pageBuilder to a context receiver after KT-52967 is fixed
}
typealias HtmlData = Data<HtmlFragment> typealias HtmlData = Data<HtmlFragment>
fun HtmlData(meta: Meta, content: context(PageBuilder, TagConsumer<*>) () -> Unit): HtmlData = //fun HtmlData(meta: Meta, content: context(PageBuilder) TagConsumer<*>.() -> Unit): HtmlData =
Data(content, meta) // Data(HtmlFragment(content), meta)
internal val HtmlData.id: String get() = meta["id"]?.string ?: "block[${hashCode()}]" internal val HtmlData.id: String get() = meta["id"]?.string ?: "block[${hashCode()}]"
internal val HtmlData.language: String? get() = meta["language"].string?.lowercase() internal val HtmlData.language: String? get() = meta["language"].string?.lowercase()
@ -26,5 +30,5 @@ internal val HtmlData.language: String? get() = meta["language"].string?.lowerca
internal val HtmlData.order: Int? get() = meta["order"]?.int internal val HtmlData.order: Int? get() = meta["order"]?.int
context(PageBuilder) fun FlowContent.htmlData(data: HtmlData) = runBlocking(Dispatchers.IO) { context(PageBuilder) fun FlowContent.htmlData(data: HtmlData) = runBlocking(Dispatchers.IO) {
data.await().invoke(this@PageBuilder, consumer) with(data.await()) { consumer.renderFragment(this@PageBuilder) }
} }

View File

@ -14,7 +14,6 @@ import io.ktor.server.routing.createRouteFromPath
import io.ktor.server.routing.get import io.ktor.server.routing.get
import io.ktor.server.routing.routing import io.ktor.server.routing.routing
import kotlinx.html.HTML import kotlinx.html.HTML
import space.kscience.dataforge.context.Context
import space.kscience.dataforge.data.DataTree import space.kscience.dataforge.data.DataTree
import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.meta.Meta
import space.kscience.dataforge.meta.withDefault import space.kscience.dataforge.meta.withDefault
@ -57,7 +56,7 @@ internal class KtorSiteBuilder(
val pageBaseUrl: String, val pageBaseUrl: String,
override val meta: Meta = this@KtorSiteBuilder.meta, override val meta: Meta = this@KtorSiteBuilder.meta,
) : PageBuilder { ) : PageBuilder {
override val context: Context get() = this@KtorSiteBuilder.context override val snark: SnarkPlugin get() = this@KtorSiteBuilder.snark
override val data: DataTree<*> get() = this@KtorSiteBuilder.data override val data: DataTree<*> get() = this@KtorSiteBuilder.data
override fun resolveRef(ref: String): String = resolveRef(pageBaseUrl, ref) override fun resolveRef(ref: String): String = resolveRef(pageBaseUrl, ref)

View File

@ -1,5 +1,6 @@
package space.kscience.snark package space.kscience.snark
import space.kscience.dataforge.context.Context
import space.kscience.dataforge.context.ContextAware import space.kscience.dataforge.context.ContextAware
import space.kscience.dataforge.data.* import space.kscience.dataforge.data.*
import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.meta.Meta
@ -16,6 +17,11 @@ internal fun Name.toWebPath() = tokens.joinToString(separator = "/"){
} }
interface PageBuilder : ContextAware { interface PageBuilder : ContextAware {
val snark: SnarkPlugin
override val context: Context get() = snark.context
val data: DataTree<*> val data: DataTree<*>
val meta: Meta val meta: Meta

View File

@ -10,6 +10,7 @@ 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
import space.kscience.dataforge.meta.string import space.kscience.dataforge.meta.string
import space.kscience.dataforge.misc.Type
import space.kscience.dataforge.names.Name import space.kscience.dataforge.names.Name
import space.kscience.dataforge.names.NameToken import space.kscience.dataforge.names.NameToken
import space.kscience.dataforge.names.asName import space.kscience.dataforge.names.asName
@ -109,11 +110,14 @@ fun SiteBuilder.pages(
} }
@Type(SiteLayout.TYPE)
fun interface SiteLayout { fun interface SiteLayout {
context(SiteBuilder) fun render(item: DataTreeItem<*>) context(SiteBuilder) fun render(item: DataTreeItem<*>)
companion object { companion object {
const val TYPE = "snark.layout"
const val LAYOUT_KEY = "layout" const val LAYOUT_KEY = "layout"
const val ASSETS_KEY = "assets" const val ASSETS_KEY = "assets"
val INDEX_PAGE_TOKEN = NameToken("index") val INDEX_PAGE_TOKEN = NameToken("index")
@ -134,6 +138,7 @@ fun interface SiteLayout {
} }
} }
object DefaultSiteLayout : SiteLayout { object DefaultSiteLayout : SiteLayout {
context(SiteBuilder) override fun render(item: DataTreeItem<*>) { context(SiteBuilder) override fun render(item: DataTreeItem<*>) {
pages(item) pages(item)

View File

@ -18,6 +18,7 @@ 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 space.kscience.dataforge.names.asName
import space.kscience.dataforge.names.parseAsName
import space.kscience.dataforge.workspace.FileData import space.kscience.dataforge.workspace.FileData
import space.kscience.dataforge.workspace.readDataDirectory import space.kscience.dataforge.workspace.readDataDirectory
import java.nio.file.Path import java.nio.file.Path
@ -36,12 +37,12 @@ interface SnarkParser<out R> {
val priority: Int get() = DEFAULT_PRIORITY val priority: Int get() = DEFAULT_PRIORITY
fun parse(snark: SnarkPlugin, meta: Meta, bytes: ByteArray): R fun parse(context: Context, meta: Meta, bytes: ByteArray): R
fun reader(snark: SnarkPlugin, meta: Meta) = object : IOReader<R> { fun reader(context: Context, meta: Meta) = object : IOReader<R> {
override val type: KType get() = this@SnarkParser.type override val type: KType get() = this@SnarkParser.type
override fun readObject(input: Input): R = parse(snark, meta, input.readBytes()) override fun readObject(input: Input): R = parse(context, meta, input.readBytes())
} }
companion object { companion object {
@ -56,7 +57,7 @@ internal class SnarkParserWrapper<R : Any>(
override val type: KType, override val type: KType,
override val fileExtensions: Set<String>, override val fileExtensions: Set<String>,
) : SnarkParser<R> { ) : SnarkParser<R> {
override fun parse(snark: SnarkPlugin, meta: Meta, bytes: ByteArray): R = bytes.asBinary().readWith(reader) override fun parse(context: Context, meta: Meta, bytes: ByteArray): R = bytes.asBinary().readWith(reader)
} }
/** /**
@ -79,6 +80,14 @@ class SnarkPlugin : AbstractPlugin() {
context.gather(SnarkParser.TYPE, true) context.gather(SnarkParser.TYPE, true)
} }
private val layouts: Map<Name, SiteLayout> by lazy {
context.gather(SiteLayout.TYPE, true)
}
private val textTransformations: Map<Name, TextTransformation> by lazy {
context.gather(TextTransformation.TYPE, true)
}
fun readDirectory(path: Path): DataTree<Any> = io.readDataDirectory(path) { dataPath, meta -> fun readDirectory(path: Path): DataTree<Any> = io.readDataDirectory(path) { dataPath, meta ->
val fileExtension = meta[FileData.META_FILE_EXTENSION_KEY].string ?: dataPath.extension val fileExtension = meta[FileData.META_FILE_EXTENSION_KEY].string ?: dataPath.extension
val parser: SnarkParser<Any> = parsers.values.filter { parser -> val parser: SnarkParser<Any> = parsers.values.filter { parser ->
@ -90,11 +99,20 @@ class SnarkPlugin : AbstractPlugin() {
byteArraySnarkParser byteArraySnarkParser
} }
parser.reader(this, meta) parser.reader(context, meta)
} }
fun layout(meta: Meta): SiteLayout = when (meta[SiteLayout.LAYOUT_KEY]) { internal fun layout(layoutMeta: Meta): SiteLayout {
else -> DefaultSiteLayout val layoutName = layoutMeta.string
?: layoutMeta["name"].string ?: error("Layout name not defined in $layoutMeta")
return layouts[layoutName.parseAsName()] ?: error("Layout with name $layoutName not found in $this")
}
internal fun textTransformation(transformationMeta: Meta): TextTransformation {
val transformationName = transformationMeta.string
?: transformationMeta["name"].string ?: error("Transformation name not defined in $transformationMeta")
return textTransformations[transformationName.parseAsName()]
?: error("Text transformation with name $transformationName not found in $this")
} }
override fun content(target: String): Map<Name, Any> = when (target) { override fun content(target: String): Map<Name, Any> = when (target) {
@ -107,6 +125,9 @@ class SnarkPlugin : AbstractPlugin() {
"jpg".asName() to SnarkParser(ImageIOReader, "jpg", "jpeg"), "jpg".asName() to SnarkParser(ImageIOReader, "jpg", "jpeg"),
"gif".asName() to SnarkParser(ImageIOReader, "gif"), "gif".asName() to SnarkParser(ImageIOReader, "gif"),
) )
TextTransformation.TYPE -> mapOf(
"replaceLinks".asName() to TextTransformation.replaceLinks
)
else -> super.content(target) else -> super.content(target)
} }

View File

@ -7,8 +7,10 @@ import kotlinx.html.unsafe
import org.intellij.markdown.flavours.commonmark.CommonMarkFlavourDescriptor import org.intellij.markdown.flavours.commonmark.CommonMarkFlavourDescriptor
import org.intellij.markdown.html.HtmlGenerator import org.intellij.markdown.html.HtmlGenerator
import org.intellij.markdown.parser.MarkdownParser import org.intellij.markdown.parser.MarkdownParser
import space.kscience.dataforge.context.Context
import space.kscience.dataforge.io.IOReader import space.kscience.dataforge.io.IOReader
import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.meta.Meta
import space.kscience.dataforge.meta.get
import java.awt.image.BufferedImage import java.awt.image.BufferedImage
import javax.imageio.ImageIO import javax.imageio.ImageIO
import kotlin.reflect.KType import kotlin.reflect.KType
@ -17,8 +19,13 @@ import kotlin.reflect.typeOf
abstract class SnarkTextParser<R> : SnarkParser<R> { abstract class SnarkTextParser<R> : SnarkParser<R> {
abstract fun parseText(text: String, meta: Meta): R abstract fun parseText(text: String, meta: Meta): R
override fun parse(snark: SnarkPlugin, meta: Meta, bytes: ByteArray): R = override fun parse(context: Context, meta: Meta, bytes: ByteArray): R =
parseText(bytes.decodeToString(), meta) parseText(bytes.decodeToString(), meta)
fun transformText(text: String, meta: Meta, page: PageBuilder): String =
meta[TextTransformation.TEXT_TRANSFORMATION_KEY]?.let {
with(page){ page.snark.textTransformation(it).transform(text)}
} ?: text
} }
@ -26,9 +33,9 @@ internal object SnarkHtmlParser : SnarkTextParser<HtmlFragment>() {
override val fileExtensions: Set<String> = setOf("html") override val fileExtensions: Set<String> = setOf("html")
override val type: KType = typeOf<HtmlFragment>() override val type: KType = typeOf<HtmlFragment>()
override fun parseText(text: String, meta: Meta): HtmlFragment = { override fun parseText(text: String, meta: Meta): HtmlFragment = HtmlFragment { page ->
div { div {
unsafe { +text } unsafe { +transformText(text, meta, page) }
} }
} }
} }
@ -44,10 +51,10 @@ internal object SnarkMarkdownParser : SnarkTextParser<HtmlFragment>() {
val parsedTree = markdownParser.buildMarkdownTreeFromString(text) val parsedTree = markdownParser.buildMarkdownTreeFromString(text)
val htmlString = HtmlGenerator(text, parsedTree, markdownFlavor).generateHtml() val htmlString = HtmlGenerator(text, parsedTree, markdownFlavor).generateHtml()
return { return HtmlFragment { page ->
div { div {
unsafe { unsafe {
+htmlString +SnarkHtmlParser.transformText(htmlString, meta, page)
} }
} }
} }

View File

@ -3,7 +3,6 @@ package space.kscience.snark
import kotlinx.html.HTML import kotlinx.html.HTML
import kotlinx.html.html import kotlinx.html.html
import kotlinx.html.stream.createHTML import kotlinx.html.stream.createHTML
import space.kscience.dataforge.context.Context
import space.kscience.dataforge.data.DataTree import space.kscience.dataforge.data.DataTree
import space.kscience.dataforge.meta.Meta import space.kscience.dataforge.meta.Meta
import space.kscience.dataforge.meta.withDefault import space.kscience.dataforge.meta.withDefault
@ -67,7 +66,8 @@ internal class StaticSiteBuilder(
inner class StaticPageBuilder : PageBuilder { inner class StaticPageBuilder : PageBuilder {
override val data: DataTree<*> get() = this@StaticSiteBuilder.data override val data: DataTree<*> get() = this@StaticSiteBuilder.data
override val meta: Meta get() = this@StaticSiteBuilder.meta override val meta: Meta get() = this@StaticSiteBuilder.meta
override val context: Context get() = this@StaticSiteBuilder.context override val snark: SnarkPlugin get() = this@StaticSiteBuilder.snark
override fun resolveRef(ref: String): String = resolveRef(baseUrl, ref) override fun resolveRef(ref: String): String = resolveRef(baseUrl, ref)

View File

@ -0,0 +1,22 @@
package space.kscience.snark
import space.kscience.dataforge.misc.Type
import space.kscience.dataforge.names.NameToken
@Type(TextTransformation.TYPE)
fun interface TextTransformation {
context(PageBuilder) fun transform(text: String): String
companion object {
const val TYPE = "snark.textTransformation"
val TEXT_TRANSFORMATION_KEY = NameToken("transformation")
val replaceLinks: TextTransformation = object : TextTransformation {
context(PageBuilder) override fun transform(text: String): String {
return text.replace("\${homeRef}", homeRef)
}
}
}
}