Fully functional!

This commit is contained in:
Alexander Nozik 2022-04-30 10:32:00 +03:00
parent 5f4c5b0829
commit b90ec0fef8
No known key found for this signature in database
GPG Key ID: F7FCF2DD25C71357
61 changed files with 389 additions and 167 deletions

View File

@ -1,15 +1,18 @@
val ktor_version: String by project
val kotlin_version: String by project
val logback_version: String by project
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
import ru.mipt.npm.gradle.KScienceVersions
plugins {
id("ru.mipt.npm.gradle.project")
application
id("ru.mipt.npm.gradle.jvm")
application
}
repositories{
mavenLocal()
}
group = "ru.mipt.npm"
version = "0.0.1"
version = "0.0.1-SNAPSHOT"
application {
mainClass.set("ru.mipt.ApplicationKt")
@ -18,20 +21,29 @@ application {
applicationDefaultJvmArgs = listOf("-Dio.ktor.development=$isDevelopment")
}
tasks.withType<KotlinCompile>{
kotlinOptions{
freeCompilerArgs = freeCompilerArgs + "-Xcontext-receivers"
}
}
val dataforgeVersion by extra("0.6.0-dev-3")
val ktorVersion = KScienceVersions.ktorVersion
dependencies {
implementation("io.ktor:ktor-server-core-jvm:$ktor_version")
implementation("org.jetbrains.kotlinx:kotlinx-html:0.7.3")
implementation("io.ktor:ktor-server-html-builder-jvm:$ktor_version")
implementation("org.jetbrains:kotlin-css-jvm")
implementation("io.ktor:ktor-server-host-common-jvm:$ktor_version")
implementation("io.ktor:ktor-server-status-pages-jvm:$ktor_version")
implementation("io.ktor:ktor-server-netty-jvm:$ktor_version")
implementation("ch.qos.logback:logback-classic:$logback_version")
implementation("space.kscience:dataforge-data:0.5.2")
implementation("io.ktor:ktor-server-core:$ktorVersion")
implementation("org.jetbrains.kotlinx:kotlinx-html:0.7.5")
implementation("io.ktor:ktor-server-html-builder:$ktorVersion")
implementation("org.jetbrains.kotlin-wrappers:kotlin-css")
implementation("io.ktor:ktor-server-host-common:$ktorVersion")
implementation("io.ktor:ktor-server-status-pages:$ktorVersion")
implementation("io.ktor:ktor-server-netty:$ktorVersion")
implementation("ch.qos.logback:logback-classic:1.2.11")
implementation("space.kscience:dataforge-workspace:$dataforgeVersion")
implementation("space.kscience:dataforge-io-yaml:$dataforgeVersion")
implementation("org.jetbrains:markdown:0.3.1")
testImplementation("io.ktor:ktor-server-tests-jvm:$ktor_version")
testImplementation("org.jetbrains.kotlin:kotlin-test-junit:$kotlin_version")
testImplementation("io.ktor:ktor-server-tests:$ktorVersion")
}
kotlin {

View File

@ -1,6 +1,3 @@
ktor_version=2.0.0
kotlin_version=1.6.20
logback_version=1.2.11
kotlin.code.style=official
toolsVersion=0.11.4-kotlin-1.6.20

View File

@ -1,13 +1,73 @@
package ru.mipt
import io.ktor.http.HttpStatusCode
import io.ktor.server.application.Application
import io.ktor.server.application.call
import io.ktor.server.application.install
import io.ktor.server.engine.embeddedServer
import io.ktor.server.html.respondHtml
import io.ktor.server.http.content.resources
import io.ktor.server.http.content.static
import io.ktor.server.netty.Netty
import ru.mipt.plugins.configureRouting
import io.ktor.server.plugins.statuspages.StatusPages
import io.ktor.server.response.respond
import io.ktor.server.routing.get
import io.ktor.server.routing.route
import io.ktor.server.routing.routing
import ru.mipt.plugins.configureTemplating
import ru.mipt.spc.magprog.DataSetSiteContext
import ru.mipt.spc.magprog.DirectoryDataTree
import ru.mipt.spc.magprog.SiteContext
import ru.mipt.spc.magprog.magProgPage
import space.kscience.dataforge.context.Context
import space.kscience.dataforge.io.io
import space.kscience.dataforge.io.yaml.YamlPlugin
import java.nio.file.Path
class AuthenticationException : RuntimeException()
class AuthorizationException : RuntimeException()
internal fun Application.magProgSite(prefix: String = "magprog"){
val context = Context("spc-site"){
plugin(YamlPlugin)
}
val io = context.io
val directory = javaClass.getResource("/magprog/content")!!.toURI()
val content = DirectoryDataTree(io, Path.of(directory))
val magprogSiteContext: SiteContext = DataSetSiteContext(context,"magprog", content)
routing {
route(prefix){
get {
call.respondHtml {
with(magprogSiteContext){
magProgPage()
}
}
}
static {
resources("magprog/assets")
}
}
}
}
fun main() {
embeddedServer(Netty, port = 8080, host = "0.0.0.0") {
configureRouting()
magProgSite()
install(StatusPages) {
exception<AuthenticationException> { call, cause ->
call.respond(HttpStatusCode.Unauthorized)
}
exception<AuthorizationException> { call, cause ->
call.respond(HttpStatusCode.Forbidden)
}
}
configureTemplating()
}.start(wait = true)
}

View File

@ -1,36 +0,0 @@
package ru.mipt.plugins
import io.ktor.http.HttpStatusCode
import io.ktor.server.application.Application
import io.ktor.server.application.call
import io.ktor.server.application.install
import io.ktor.server.http.content.resources
import io.ktor.server.http.content.static
import io.ktor.server.plugins.statuspages.StatusPages
import io.ktor.server.response.respond
import io.ktor.server.response.respondText
import io.ktor.server.routing.get
import io.ktor.server.routing.routing
fun Application.configureRouting() {
routing {
get("/") {
call.respondText("Hello World!")
}
// Static plugin. Try to access `/static/index.html`
static("/magprog") {
resources("magprog")
}
}
install(StatusPages) {
exception<AuthenticationException> { call, cause ->
call.respond(HttpStatusCode.Unauthorized)
}
exception<AuthorizationException> { call, cause ->
call.respond(HttpStatusCode.Forbidden)
}
}
}
class AuthenticationException : RuntimeException()
class AuthorizationException : RuntimeException()

View File

@ -54,7 +54,7 @@ fun Application.configureTemplating() {
}
}
suspend inline fun ApplicationCall.respondCss(builder: CSSBuilder.() -> Unit) {
this.respondText(CSSBuilder().apply(builder).toString(), ContentType.Text.CSS)
suspend inline fun ApplicationCall.respondCss(builder: CssBuilder.() -> Unit) {
this.respondText(CssBuilder().apply(builder).toString(), ContentType.Text.CSS)
}

View File

@ -0,0 +1,101 @@
package ru.mipt.spc.magprog
import kotlinx.coroutines.runBlocking
import kotlinx.html.div
import kotlinx.html.unsafe
import kotlinx.serialization.json.Json
import org.intellij.markdown.flavours.commonmark.CommonMarkFlavourDescriptor
import org.intellij.markdown.html.HtmlGenerator
import org.intellij.markdown.parser.MarkdownParser
import ru.mipt.spc.magprog.DirectoryDataTree.Companion.META_FILE_EXTENSION_KEY
import space.kscience.dataforge.context.Context
import space.kscience.dataforge.data.*
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 space.kscience.dataforge.meta.get
import space.kscience.dataforge.meta.string
import space.kscience.dataforge.meta.toMeta
import space.kscience.dataforge.misc.DFInternal
import space.kscience.dataforge.names.Name
import kotlin.reflect.KType
import kotlin.reflect.typeOf
class DataSetSiteContext(
override val context: Context,
val prefix: String,
val dataSet: DataSet<Any>,
) : SiteContext {
override fun resolveResource(name: String): String = "$prefix/$name"
private val markdownFlavor = CommonMarkFlavourDescriptor()
private val markdownParser = MarkdownParser(markdownFlavor)
//TODO replace by a plugin
private suspend fun Data<ByteArray>.toHtmlBlock(): HtmlBlock {
val fileType = meta[META_FILE_EXTENSION_KEY].string
val src = await().decodeToString()
return when (fileType) {
"html" -> HtmlBlock(meta) {
div {
unsafe {
+src
}
}
}
"markdown", "mdown", "mkdn", "mkd", "md" -> HtmlBlock(meta) {
div("markdown") {
val parsedTree = markdownParser.buildMarkdownTreeFromString(src)
unsafe {
+HtmlGenerator(src, parsedTree, markdownFlavor).generateHtml()
}
}
}
else -> error("Can't convert a data with file extension $fileType to html")
}
}
private val Data<*>.published: Boolean get() = meta["published"].string != "false"
@DFInternal
override fun <T : Any> resolve(type: KType, name: Name): Data<T>? = when (type) {
typeOf<Meta>() -> {
val data = dataSet.selectOne<ByteArray>(name)
if( data == null) null else {
when (data.meta[META_FILE_EXTENSION_KEY].string) {
"json" -> data.map {
Json.parseToJsonElement(it.decodeToString()).toMeta()
}
"yaml" -> data.map {
YamlMetaFormat.readObject(it.asBinary())
}
else -> error("File with extension ${data.meta[META_FILE_EXTENSION_KEY]} could not be parsed as Meta")
} as Data<T>?
}
}
else -> dataSet.selectOne(type, name)
}
@DFInternal
override fun <T : Any> resolveAll(type: KType, filter: (name: Name, meta: Meta) -> Boolean): DataSet<T> =
dataSet.select(type, filter = filter)
override fun resolveHtml(name: Name): HtmlBlock? = runBlocking {
resolve<ByteArray>(name)?.takeIf { it.published }?.toHtmlBlock()
}
override fun resolveAllHtml(filter: (name: Name, meta: Meta) -> Boolean): Map<Name, HtmlBlock> = runBlocking {
buildMap {
resolveAll<ByteArray>(filter).dataSequence().filter { it.published }.forEach {
put(it.name, it.toHtmlBlock())
}
}
}
}

View File

@ -0,0 +1,78 @@
package ru.mipt.spc.magprog
import space.kscience.dataforge.data.Data
import space.kscience.dataforge.data.DataTree
import space.kscience.dataforge.data.DataTreeItem
import space.kscience.dataforge.data.StaticData
import space.kscience.dataforge.io.IOPlugin
import space.kscience.dataforge.io.readEnvelopeFile
import space.kscience.dataforge.io.readMetaFile
import space.kscience.dataforge.io.toByteArray
import space.kscience.dataforge.meta.Meta
import space.kscience.dataforge.meta.copy
import space.kscience.dataforge.names.NameToken
import space.kscience.dataforge.names.asName
import space.kscience.dataforge.names.plus
import java.nio.file.Path
import kotlin.io.path.extension
import kotlin.io.path.isDirectory
import kotlin.io.path.listDirectoryEntries
import kotlin.io.path.nameWithoutExtension
import kotlin.reflect.KType
import kotlin.reflect.typeOf
//internal object ByteArrayIOFormat : IOFormat<ByteArray> {
//
// override val type: KType = typeOf<ByteArray>()
//
// override fun writeObject(output: Output, obj: ByteArray) {
// output.writeFully(obj)
// }
//
// override fun readObject(input: Input): ByteArray = input.readBytes()
//
// override fun toMeta(): Meta = Meta {
// IOFormat.NAME_KEY put "ByteArray"
// }
//
//}
class DirectoryDataTree(val io: IOPlugin, val path: Path) : DataTree<ByteArray> {
override val dataType: KType
get() = typeOf<ByteArray>()
override val meta: Meta get() = io.readMetaFile(path)
private fun readFile(filePath: Path): Data<ByteArray> {
val envelope = io.readEnvelopeFile(filePath, readNonEnvelopes = true)
val meta = envelope.meta.copy {
META_FILE_PATH_KEY put filePath.toString()
META_FILE_EXTENSION_KEY put filePath.extension
//TODO add other file information
}
return StaticData(typeOf<ByteArray>(), envelope.data?.toByteArray() ?: ByteArray(0), meta)
}
override val items: Map<NameToken, DataTreeItem<ByteArray>>
get() = path.listDirectoryEntries().associate { childPath ->
val fileName = childPath.fileName.nameWithoutExtension
val item: DataTreeItem<ByteArray> = if (childPath.isDirectory()) {
DataTreeItem.Node(DirectoryDataTree(io, childPath))
} else {
DataTreeItem.Leaf(readFile(childPath))
}
NameToken(fileName) to item
}
companion object {
val META_FILE_KEY = "file".asName()
val META_FILE_PATH_KEY = META_FILE_KEY + "path"
val META_FILE_EXTENSION_KEY = META_FILE_KEY + "extension"
}
}
//TODO add transforming provider instead of ByteArray DataSet

View File

@ -13,6 +13,11 @@ interface HtmlBlock {
val content: FlowContent.() -> Unit
}
fun HtmlBlock(meta: Meta, content: FlowContent.() -> Unit ) = object: HtmlBlock{
override val meta: Meta = meta
override val content: FlowContent.() -> Unit = content
}
val HtmlBlock.id: String get() = meta["id"]?.string ?: "block[${hashCode()}]"
val HtmlBlock.language: Language get() = meta["language"]?.enum<Language>() ?: Language.RU

View File

@ -36,7 +36,9 @@ private fun FlowContent.personCards(list: List<Person>, prefix: String) {
context(SiteContext)
fun FlowContent.mentors() {
val mentors = findByType("magprog_mentor").values.map { Person(it) }.sortedBy { it.order }
val mentors = findByType("magprog_mentor").values.map {
Person(it)
}.sortedBy { it.order }
div("header") {
css {

View File

@ -1,26 +1,28 @@
package ru.mipt.spc.magprog
import io.ktor.util.InternalAPI
import space.kscience.dataforge.context.ContextAware
import space.kscience.dataforge.data.Data
import space.kscience.dataforge.data.DataSet
import space.kscience.dataforge.meta.Meta
import space.kscience.dataforge.meta.get
import space.kscience.dataforge.meta.string
import space.kscience.dataforge.misc.DFInternal
import space.kscience.dataforge.names.Name
import space.kscience.dataforge.names.startsWith
import kotlin.reflect.KType
import kotlin.reflect.typeOf
interface SiteContext {
interface SiteContext: ContextAware {
/**
* Resolve a resource full path by its name
*/
fun resolveResource(name: String): String
@InternalAPI
@DFInternal
fun <T: Any> resolve(type: KType, name: Name): Data<T>?
@InternalAPI
@DFInternal
fun <T: Any> resolveAll(type: KType, filter: (name: Name, meta: Meta) -> Boolean): DataSet<T>
/**
@ -34,10 +36,10 @@ interface SiteContext {
fun resolveAllHtml(filter: (name: Name, meta: Meta) -> Boolean): Map<Name, HtmlBlock>
}
@OptIn(InternalAPI::class)
@OptIn(DFInternal::class)
inline fun <reified T: Any> SiteContext.resolve(name: Name): Data<T>? = resolve(typeOf<T>(), name)
@OptIn(InternalAPI::class)
@OptIn(DFInternal::class)
inline fun <reified T:Any> SiteContext.resolveAll(noinline filter: (name: Name, meta: Meta) -> Boolean): DataSet<T> =
resolveAll(typeOf<T>(), filter)

View File

@ -1,15 +1,16 @@
package ru.mipt.spc.magprog
import kotlinx.coroutines.runBlocking
import kotlinx.css.*
import kotlinx.html.*
import space.kscience.dataforge.data.await
import space.kscience.dataforge.meta.Meta
import space.kscience.dataforge.meta.get
import space.kscience.dataforge.meta.string
import space.kscience.dataforge.names.Name
import space.kscience.dataforge.names.asName
import space.kscience.dataforge.names.plus
//fun CSSBuilder.magProgCss() {
//fun CssBuilder.magProgCss() {
// rule(".magprog-body") {
// rule(".magprog-header") {
// display = Display.flex
@ -45,19 +46,21 @@ private fun wrapSection(
}
}
private fun wrapSection(block: HtmlBlock, idOverride: String? = null): MagProgSection =
wrapSection(
idOverride ?: block.id,
block.meta["section_title"]?.string ?: error("Section without title"),
block.content
)
private fun wrapSection(
block: HtmlBlock,
idOverride: String? = null,
): MagProgSection = wrapSection(
idOverride ?: block.id,
block.meta["section_title"]?.string ?: error("Section without title"),
block.content
)
private val CONTENT_NODE_NAME = "content".asName()
private val INTRO_PATH: Name = TODO()
private val ENROLL_PATH: Name = TODO()
private val CONTACTS_PATH: Name = TODO()
private val PROGRAM_PATH: Name = TODO()
private val RECOMMENDED_COURSES_PATH: Name = TODO()
private val CONTENT_NODE_NAME = Name.EMPTY//"content".asName()
private val INTRO_PATH: Name = CONTENT_NODE_NAME + "intro"
private val ENROLL_PATH: Name = CONTENT_NODE_NAME + "enroll"
private val CONTACTS_PATH: Name = CONTENT_NODE_NAME + "contacts"
private val PROGRAM_PATH: Name = CONTENT_NODE_NAME + "program"
private val RECOMMENDED_COURSES_PATH: Name = CONTENT_NODE_NAME + "recommendedCourses"
private val PARTNERS_PATH: Name = CONTENT_NODE_NAME + "partners"
context(SiteContext) private fun FlowContent.programSection() {
@ -67,23 +70,24 @@ context(SiteContext) private fun FlowContent.programSection() {
h2 { +"Учебная программа" }
with(programBlock) { content() }
button(classes = "fit btn btn-primary btn-lg") {
attributes["data-bs-toggle"]="collapse"
attributes["data-bs-target"]="#recommended-courses-collapse-text"
attributes["aria-expanded"]="false"
attributes["aria-controls"]="recommended-courses-collapse-text"
attributes["data-bs-toggle"] = "collapse"
attributes["data-bs-target"] = "#recommended-courses-collapse-text"
attributes["aria-expanded"] = "false"
attributes["aria-controls"] = "recommended-courses-collapse-text"
+"Рекомендованные курсы"
}
div("collapse pt-3") {
id = "recommended-courses-collapse-text"
div("card card-body") {
with(recommendedBlock){content()}
with(recommendedBlock) { content() }
}
}
}
}
context(SiteContext) 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
div("inner") {
h2 { +"Партнеры" }
div("features") {
@ -106,8 +110,7 @@ context(SiteContext) private fun FlowContent.partners() {
}
}
context(SiteContext)
fun HTML.magProgPage() {
context(SiteContext) fun HTML.magProgPage() {
val sections = listOf<MagProgSection>(
wrapSection(resolveHtml(INTRO_PATH)!!, "intro"),
MagProgSection(
@ -140,7 +143,7 @@ fun HTML.magProgPage() {
) {
team()
},
wrapSection(resolveHtml(CONTACTS_PATH)!!, "enroll"),
wrapSection(resolveHtml(CONTACTS_PATH)!!, "contacts"),
)
head {
@ -154,12 +157,12 @@ fun HTML.magProgPage() {
}
link {
rel = "stylesheet"
href = resolveResource("assets/css/main.css")
href = resolveResource("css/main.css")
}
noScript {
link {
rel = "stylesheet"
href = resolveResource("assets/css/noscript.css")
href = resolveResource("css/noscript.css")
}
}
}
@ -171,7 +174,7 @@ fun HTML.magProgPage() {
ul {
sections.forEach { section ->
li {
a(href = section.id) {
a(href = "#${section.id}") {
+section.title
}
}
@ -214,25 +217,25 @@ fun HTML.magProgPage() {
}
}
script {
src = resolveResource("assets/js/jquery.min.js")
src = resolveResource("js/jquery.min.js")
}
script {
src = resolveResource("assets/js/jquery.scrollex.min.js")
src = resolveResource("js/jquery.scrollex.min.js")
}
script {
src = resolveResource("assets/js/jquery.scrolly.min.js")
src = resolveResource("js/jquery.scrolly.min.js")
}
script {
src = resolveResource("assets/js/browser.min.js")
src = resolveResource("js/browser.min.js")
}
script {
src = resolveResource("assets/js/breakpoints.min.js")
src = resolveResource("js/breakpoints.min.js")
}
script {
src = resolveResource("assets/js/util.js")
src = resolveResource("js/util.js")
}
script {
src = resolveResource("assets/js/main.js")
src = resolveResource("js/main.js")
}
}
}

View File

@ -1,9 +1,9 @@
package ru.mipt.spc.magprog
import kotlinx.css.CSSBuilder
import kotlinx.css.CssBuilder
import kotlinx.html.CommonAttributeGroupFacade
import kotlinx.html.style
fun CommonAttributeGroupFacade.css(block: CSSBuilder.() -> Unit) {
style = CSSBuilder().block().toString()
fun CommonAttributeGroupFacade.css(block: CssBuilder.() -> Unit) {
style = CssBuilder().block().toString()
}

View File

Before

Width:  |  Height:  |  Size: 4.2 MiB

After

Width:  |  Height:  |  Size: 4.2 MiB

View File

Before

Width:  |  Height:  |  Size: 168 KiB

After

Width:  |  Height:  |  Size: 168 KiB

View File

Before

Width:  |  Height:  |  Size: 5.6 MiB

After

Width:  |  Height:  |  Size: 5.6 MiB

View File

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 22 KiB

View File

Before

Width:  |  Height:  |  Size: 613 KiB

After

Width:  |  Height:  |  Size: 613 KiB

View File

Before

Width:  |  Height:  |  Size: 1.8 MiB

After

Width:  |  Height:  |  Size: 1.8 MiB

View File

Before

Width:  |  Height:  |  Size: 184 KiB

After

Width:  |  Height:  |  Size: 184 KiB

View File

Before

Width:  |  Height:  |  Size: 2.8 MiB

After

Width:  |  Height:  |  Size: 2.8 MiB

View File

Before

Width:  |  Height:  |  Size: 4.3 KiB

After

Width:  |  Height:  |  Size: 4.3 KiB

View File

Before

Width:  |  Height:  |  Size: 148 KiB

After

Width:  |  Height:  |  Size: 148 KiB

View File

Before

Width:  |  Height:  |  Size: 5.5 MiB

After

Width:  |  Height:  |  Size: 5.5 MiB

View File

Before

Width:  |  Height:  |  Size: 1.6 MiB

After

Width:  |  Height:  |  Size: 1.6 MiB

View File

Before

Width:  |  Height:  |  Size: 148 KiB

After

Width:  |  Height:  |  Size: 148 KiB

View File

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 16 KiB

View File

Before

Width:  |  Height:  |  Size: 41 KiB

After

Width:  |  Height:  |  Size: 41 KiB

View File

Before

Width:  |  Height:  |  Size: 26 KiB

After

Width:  |  Height:  |  Size: 26 KiB

View File

Before

Width:  |  Height:  |  Size: 45 KiB

After

Width:  |  Height:  |  Size: 45 KiB

View File

Before

Width:  |  Height:  |  Size: 6.1 KiB

After

Width:  |  Height:  |  Size: 6.1 KiB

View File

Before

Width:  |  Height:  |  Size: 89 KiB

After

Width:  |  Height:  |  Size: 89 KiB

View File

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

Before

Width:  |  Height:  |  Size: 4.1 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

View File

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 25 KiB

View File

Before

Width:  |  Height:  |  Size: 9.9 KiB

After

Width:  |  Height:  |  Size: 9.9 KiB

View File

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 28 KiB

View File

Before

Width:  |  Height:  |  Size: 7.6 KiB

After

Width:  |  Height:  |  Size: 7.6 KiB

View File

Before

Width:  |  Height:  |  Size: 75 KiB

After

Width:  |  Height:  |  Size: 75 KiB

View File

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB

View File

@ -1,8 +1,8 @@
---
content_type: magprog_mentor
title: Максим Сергеевич Долгоносов
name: Максим Сергеевич Долгоносов
id: Dolgonosov
photo: Dolgonosov.jpg
photo: images/mentors/Dolgonosov.jpg
language: ru
---

View File

@ -1,8 +1,8 @@
---
content_type: magprog_mentor
title: Константин Викторович Герценбергер
name: Константин Викторович Герценбергер
id: Gertsenberger
photo: Gertsenberger.jpg
photo: images/mentors/Gertsenberger.jpg
language: ru
---
#### Организация

View File

@ -1,8 +1,8 @@
---
content_type: magprog_mentor
title: Roland Grinis
name: Roland Grinis
id: Grinis
photo: Grinis.jpg
photo: images/mentors/Grinis.jpg
language: ru
---

View File

@ -1,8 +1,8 @@
---
content_type: magprog_mentor
title: Олег Евгеньевич Калашев
name: Олег Евгеньевич Калашев
id: Kalashev
photo: Kalashev.jpg
photo: images/mentors/Kalashev.jpg
language: ru
---

View File

@ -1,8 +1,8 @@
---
content_type: magprog_mentor
title: Igor Khokhriakov
name: Igor Khokhriakov
id: Khokhriakov
photo: Khokhriakov.jpg
photo: images/mentors/Khokhriakov.jpg
language: ru
---

View File

@ -1,8 +1,8 @@
---
content_type: magprog_mentor
title: Петр Климай
name: Петр Климай
id: Klimai
photo: Klimai.jpg
photo: images/mentors/Klimai.jpg
language: ru
---

View File

@ -1,8 +1,8 @@
---
content_type: magprog_mentor
title: Dmitriy Kostunin
name: Dmitriy Kostunin
id: Kostunin
photo: Kostunin.jpg
photo: images/mentors/Kostunin.jpg
language: ru
published: false
---

View File

@ -1,8 +1,8 @@
---
content_type: magprog_mentor
title: Александр Нозик
name: Александр Нозик
id: Nozik
photo: Nozik.jpg
photo: images/mentors/Nozik.jpg
language: ru
---

View File

@ -1,8 +1,8 @@
---
content_type: magprog_mentor
title: Олийниченко Дмитрий Робертович
name: Олийниченко Дмитрий Робертович
id: Oliinychenko
photo: Oliinychenko.jpg
photo: images/mentors/Oliinychenko.jpg
language: ru
published: false
---

View File

@ -1,8 +1,8 @@
---
content_type: magprog_mentor
title: Игорь Анатольевич Пшеничнов
name: Игорь Анатольевич Пшеничнов
id: Pshenichnov
photo: Pshenichnov.jpg
photo: images/mentors/Pshenichnov.jpg
language: ru
---

View File

@ -1,8 +1,8 @@
---
content_type: magprog_mentor
title: Лев Михайлович Шагалов
name: Лев Михайлович Шагалов
id: Shagalov
photo: Shagalov.jpg
photo: images/mentors/Shagalov.jpg
language: ru
---

View File

@ -1,8 +1,8 @@
---
content_type: magprog_mentor
title: Айно Константиновна Скасырская
name: Айно Константиновна Скасырская
id: Skasyrskaya
photo: Skasyrskaya.jpg
photo: images/mentors/Skasyrskaya.jpg
language: ru
---

View File

@ -1,8 +1,8 @@
---
content_type: magprog_mentor
title: Ильмир Усманов
name: Ильмир Усманов
id: Usmanov
photo: Usmanov.jpg
photo: images/mentors/Usmanov.jpg
language: ru
---

View File

@ -1,8 +1,8 @@
---
content_type: magprog_mentor
title: Полиграф Шариков
name: Полиграф Шариков
id: template
photo: Template.jpg
photo: images/mentors/Template.jpg
published: false
language: ru
---

View File

@ -2,34 +2,34 @@ title: Партнеры
content:
- title: ЛФИ МФТИ
link: https://mipt.ru/education/departments/lpr/
logo: /images/magprog/partners/LPI.jpg
logo: /images/partners/LPI.jpg
- title: ФПМИ МФТИ
link: https://mipt.ru/education/departments/fpmi/
logo: /images/magprog/partners/FPMI.jpg
logo: /images/partners/FPMI.jpg
- title: JetBrains Research
link: https://research.jetbrains.org/groups/npm/
logo: /images/magprog/partners/JBR.png
logo: /images/partners/JBR.png
- title: Таврида Электрик
link: https://www.tavrida.com/ter/
logo: /images/magprog/partners/Tavrida.png
logo: /images/partners/Tavrida.png
- title: ИЯИ РАН
link: https://www.inr.ru/
logo: /images/partners/inr_logo.png
- title: ИКИ РАН
link: http://www.iki.rssi.ru/
logo: /images/magprog/partners/iki.jpg
logo: /images/partners/iki.jpg
- title: ОИЯИ
link: https://bmn.jinr.ru/
logo: /images/magprog/partners/jinr.png
logo: /images/partners/jinr.png
# - title: ВШЭ
# link: https://www.hse.ru/en/
# logo: /images/magprog/partners/hse.jpg
# logo: /images/partners/hse.jpg
# - title: HZG-DESY
# link: https://www.desy.de/research/cooperations__institutes/hzg/index_eng.html
# logo: /images/magprog/partners/desy.svg
# logo: /images/partners/desy.svg
- title: Тинькофф
link: https://fintech.tinkoff.ru/
logo: /images/magprog/partners/tink.jpg
logo: /images/partners/tink.jpg
- title: ФИЦ Биотехнологии РАН
link: https://www.fbras.ru/
logo: /images/magprog/partners/biotech.png
logo: /images/partners/biotech.png

View File

@ -1,9 +1,9 @@
---
content_type: magprog_team
title: Денис Айвазов
name: Денис Айвазов
id: Aivazov
order: 4
photo: /images/magprog/team/Aivazov.jpg
photo: images/team/Aivazov.jpg
language: ru
---
**Консультант от ФПМИ**

View File

@ -1,9 +1,9 @@
---
content_type: magprog_team
title: Александр Нозик
name: Александр Нозик
id: nozik
order: 1
photo: /images/members/nozik.png
photo: images/mentors/Nozik.jpg
language: ru
---
**Руководитель программы**

View File

@ -1,9 +1,9 @@
---
content_type: magprog_team
title: Александр Светличный
name: Александр Светличный
id: svetlichnii
order: 2
photo: /images/members/svetlichny.jpeg
photo: images/members/svetlichny.jpeg
language: ru
---
**Заместитель руководителя**

View File

@ -319,21 +319,21 @@ print 'It took ' + i + ' iterations to sort the deck.';</code></pre>
<h3>Fit</h3>
<div class="box alt">
<div class="row gtr-uniform">
<div class="col-12"><span class="image fit"><img src="images/pic04.jpg" alt="" /></span></div>
<div class="col-4"><span class="image fit"><img src="images/pic01.jpg" alt="" /></span></div>
<div class="col-4"><span class="image fit"><img src="images/pic02.jpg" alt="" /></span></div>
<div class="col-4"><span class="image fit"><img src="images/pic03.jpg" alt="" /></span></div>
<div class="col-4"><span class="image fit"><img src="images/pic03.jpg" alt="" /></span></div>
<div class="col-4"><span class="image fit"><img src="images/pic01.jpg" alt="" /></span></div>
<div class="col-4"><span class="image fit"><img src="images/pic02.jpg" alt="" /></span></div>
<div class="col-4"><span class="image fit"><img src="images/pic02.jpg" alt="" /></span></div>
<div class="col-4"><span class="image fit"><img src="images/pic03.jpg" alt="" /></span></div>
<div class="col-4"><span class="image fit"><img src="images/pic01.jpg" alt="" /></span></div>
<div class="col-12"><span class="image fit"><img src="assets/images/pic04.jpg" alt="" /></span></div>
<div class="col-4"><span class="image fit"><img src="assets/images/pic01.jpg" alt="" /></span></div>
<div class="col-4"><span class="image fit"><img src="assets/images/pic02.jpg" alt="" /></span></div>
<div class="col-4"><span class="image fit"><img src="assets/images/pic03.jpg" alt="" /></span></div>
<div class="col-4"><span class="image fit"><img src="assets/images/pic03.jpg" alt="" /></span></div>
<div class="col-4"><span class="image fit"><img src="assets/images/pic01.jpg" alt="" /></span></div>
<div class="col-4"><span class="image fit"><img src="assets/images/pic02.jpg" alt="" /></span></div>
<div class="col-4"><span class="image fit"><img src="assets/images/pic02.jpg" alt="" /></span></div>
<div class="col-4"><span class="image fit"><img src="assets/images/pic03.jpg" alt="" /></span></div>
<div class="col-4"><span class="image fit"><img src="assets/images/pic01.jpg" alt="" /></span></div>
</div>
</div>
<h3>Left &amp; Right</h3>
<p><span class="image left"><img src="images/pic05.jpg" alt="" /></span>Fringilla nisl. Donec accumsan interdum nisi, quis tincidunt felis sagittis eget. tempus euismod. Vestibulum ante ipsum primis in faucibus vestibulum. Blandit adipiscing eu felis iaculis volutpat ac adipiscing accumsan eu faucibus. Integer ac pellentesque praesent tincidunt felis sagittis eget. tempus euismod. Vestibulum ante ipsum primis in faucibus vestibulum. Blandit adipiscing eu felis iaculis volutpat ac adipiscing accumsan eu faucibus. Integer ac pellentesque praesent. Donec accumsan interdum nisi, quis tincidunt felis sagittis eget. tempus euismod. Vestibulum ante ipsum primis in faucibus vestibulum. Blandit adipiscing eu felis iaculis volutpat ac adipiscing accumsan eu faucibus. Integer ac pellentesque praesent tincidunt felis sagittis eget. tempus euismod. Vestibulum ante ipsum primis in faucibus vestibulum. Blandit adipiscing eu felis iaculis volutpat ac adipiscing accumsan eu faucibus. Integer ac pellentesque praesent. Blandit adipiscing eu felis iaculis volutpat ac adipiscing accumsan eu faucibus. Integer ac pellentesque praesent tincidunt felis sagittis eget. tempus euismod. Vestibulum ante ipsum primis in faucibus vestibulum. Blandit adipiscing eu felis iaculis volutpat ac adipiscing accumsan eu faucibus. Integer ac pellentesque praesent.</p>
<p><span class="image right"><img src="images/pic06.jpg" alt="" /></span>Fringilla nisl. Donec accumsan interdum nisi, quis tincidunt felis sagittis eget. tempus euismod. Vestibulum ante ipsum primis in faucibus vestibulum. Blandit adipiscing eu felis iaculis volutpat ac adipiscing accumsan eu faucibus. Integer ac pellentesque praesent tincidunt felis sagittis eget. tempus euismod. Vestibulum ante ipsum primis in faucibus vestibulum. Blandit adipiscing eu felis iaculis volutpat ac adipiscing accumsan eu faucibus. Integer ac pellentesque praesent. Donec accumsan interdum nisi, quis tincidunt felis sagittis eget. tempus euismod. Vestibulum ante ipsum primis in faucibus vestibulum. Blandit adipiscing eu felis iaculis volutpat ac adipiscing accumsan eu faucibus. Integer ac pellentesque praesent tincidunt felis sagittis eget. tempus euismod. Vestibulum ante ipsum primis in faucibus vestibulum. Blandit adipiscing eu felis iaculis volutpat ac adipiscing accumsan eu faucibus. Integer ac pellentesque praesent. Blandit adipiscing eu felis iaculis volutpat ac adipiscing accumsan eu faucibus. Integer ac pellentesque praesent tincidunt felis sagittis eget. tempus euismod. Vestibulum ante ipsum primis in faucibus vestibulum. Blandit adipiscing eu felis iaculis volutpat ac adipiscing accumsan eu faucibus. Integer ac pellentesque praesent.</p>
<p><span class="image left"><img src="assets/images/pic05.jpg" alt="" /></span>Fringilla nisl. Donec accumsan interdum nisi, quis tincidunt felis sagittis eget. tempus euismod. Vestibulum ante ipsum primis in faucibus vestibulum. Blandit adipiscing eu felis iaculis volutpat ac adipiscing accumsan eu faucibus. Integer ac pellentesque praesent tincidunt felis sagittis eget. tempus euismod. Vestibulum ante ipsum primis in faucibus vestibulum. Blandit adipiscing eu felis iaculis volutpat ac adipiscing accumsan eu faucibus. Integer ac pellentesque praesent. Donec accumsan interdum nisi, quis tincidunt felis sagittis eget. tempus euismod. Vestibulum ante ipsum primis in faucibus vestibulum. Blandit adipiscing eu felis iaculis volutpat ac adipiscing accumsan eu faucibus. Integer ac pellentesque praesent tincidunt felis sagittis eget. tempus euismod. Vestibulum ante ipsum primis in faucibus vestibulum. Blandit adipiscing eu felis iaculis volutpat ac adipiscing accumsan eu faucibus. Integer ac pellentesque praesent. Blandit adipiscing eu felis iaculis volutpat ac adipiscing accumsan eu faucibus. Integer ac pellentesque praesent tincidunt felis sagittis eget. tempus euismod. Vestibulum ante ipsum primis in faucibus vestibulum. Blandit adipiscing eu felis iaculis volutpat ac adipiscing accumsan eu faucibus. Integer ac pellentesque praesent.</p>
<p><span class="image right"><img src="assets/images/pic06.jpg" alt="" /></span>Fringilla nisl. Donec accumsan interdum nisi, quis tincidunt felis sagittis eget. tempus euismod. Vestibulum ante ipsum primis in faucibus vestibulum. Blandit adipiscing eu felis iaculis volutpat ac adipiscing accumsan eu faucibus. Integer ac pellentesque praesent tincidunt felis sagittis eget. tempus euismod. Vestibulum ante ipsum primis in faucibus vestibulum. Blandit adipiscing eu felis iaculis volutpat ac adipiscing accumsan eu faucibus. Integer ac pellentesque praesent. Donec accumsan interdum nisi, quis tincidunt felis sagittis eget. tempus euismod. Vestibulum ante ipsum primis in faucibus vestibulum. Blandit adipiscing eu felis iaculis volutpat ac adipiscing accumsan eu faucibus. Integer ac pellentesque praesent tincidunt felis sagittis eget. tempus euismod. Vestibulum ante ipsum primis in faucibus vestibulum. Blandit adipiscing eu felis iaculis volutpat ac adipiscing accumsan eu faucibus. Integer ac pellentesque praesent. Blandit adipiscing eu felis iaculis volutpat ac adipiscing accumsan eu faucibus. Integer ac pellentesque praesent tincidunt felis sagittis eget. tempus euismod. Vestibulum ante ipsum primis in faucibus vestibulum. Blandit adipiscing eu felis iaculis volutpat ac adipiscing accumsan eu faucibus. Integer ac pellentesque praesent.</p>
</section>
</div>

View File

@ -33,7 +33,7 @@
<section id="main" class="wrapper">
<div class="inner">
<h1 class="major">A Generic Page</h1>
<span class="image fit"><img src="images/pic04.jpg" alt="" /></span>
<span class="image fit"><img src="assets/images/pic04.jpg" alt="" /></span>
<p>Donec eget ex magna. Interdum et malesuada fames ac ante ipsum primis in faucibus. Pellentesque venenatis dolor imperdiet dolor mattis sagittis. Praesent rutrum sem diam, vitae egestas enim auctor sit amet. Pellentesque leo mauris, consectetur id ipsum sit amet, fergiat. Pellentesque in mi eu massa lacinia malesuada et a elit. Donec urna ex, lacinia in purus ac, pretium pulvinar mauris. Curabitur sapien risus, commodo eget turpis at, elementum convallis elit. Pellentesque enim turpis, hendrerit tristique.</p>
<p>Interdum et malesuada fames ac ante ipsum primis in faucibus. Pellentesque venenatis dolor imperdiet dolor mattis sagittis. Praesent rutrum sem diam, vitae egestas enim auctor sit amet. Pellentesque leo mauris, consectetur id ipsum sit amet, fersapien risus, commodo eget turpis at, elementum convallis elit. Pellentesque enim turpis, hendrerit tristique lorem ipsum dolor.</p>
</div>

View File

@ -46,7 +46,7 @@
<!-- One -->
<section id="one" class="wrapper style2 spotlights">
<section>
<a href="#" class="image"><img src="images/pic01.jpg" alt="" data-position="center center" /></a>
<a href="#" class="image"><img src="assets/images/pic01.jpg" alt="" data-position="center center" /></a>
<div class="content">
<div class="inner">
<h2>Sed ipsum dolor</h2>
@ -58,7 +58,7 @@
</div>
</section>
<section>
<a href="#" class="image"><img src="images/pic02.jpg" alt="" data-position="top center" /></a>
<a href="#" class="image"><img src="assets/images/pic02.jpg" alt="" data-position="top center" /></a>
<div class="content">
<div class="inner">
<h2>Feugiat consequat</h2>
@ -70,7 +70,7 @@
</div>
</section>
<section>
<a href="#" class="image"><img src="images/pic03.jpg" alt="" data-position="25% 25%" /></a>
<a href="#" class="image"><img src="assets/images/pic03.jpg" alt="" data-position="25% 25%" /></a>
<div class="content">
<div class="inner">
<h2>Ultricies aliquam</h2>

View File

@ -1,10 +1,8 @@
package ru.mipt
import io.ktor.client.request.get
import io.ktor.client.statement.bodyAsText
import io.ktor.http.HttpStatusCode
import io.ktor.server.testing.testApplication
import ru.mipt.plugins.configureRouting
import kotlin.test.Test
import kotlin.test.assertEquals
@ -12,11 +10,11 @@ class ApplicationTest {
@Test
fun testRoot() = testApplication {
application {
configureRouting()
magProgSite()
}
client.get("/").apply {
client.get("/magprog").apply {
assertEquals(HttpStatusCode.OK, status)
assertEquals("Hello World!", bodyAsText())
// assertEquals("Hello World!", bodyAsText())
}
}
}