Skip to content

Commit

Permalink
Merge workshop into a single session. Fix #259
Browse files Browse the repository at this point in the history
  • Loading branch information
e5l committed Feb 19, 2025
1 parent 59fa988 commit 6a4037a
Show file tree
Hide file tree
Showing 10 changed files with 179 additions and 34 deletions.
19 changes: 0 additions & 19 deletions .fleet/run.json

This file was deleted.

3 changes: 0 additions & 3 deletions .fleet/settings.json

This file was deleted.

11 changes: 8 additions & 3 deletions backend/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
plugins {
alias(libs.plugins.kotlinJvm)
alias(libs.plugins.kotlinSerialization)
application
id("io.ktor.plugin") version "2.3.11"
alias(libs.plugins.ktor)
}

kotlin {
Expand All @@ -13,6 +12,7 @@ application {
mainClass.set("org.jetbrains.kotlinconf.backend.MainKt")
}


dependencies {
implementation(projects.shared)
implementation(libs.ktor.server.netty)
Expand Down Expand Up @@ -40,6 +40,11 @@ dependencies {
implementation(libs.postgresql)

implementation(libs.hikaricp)

implementation(libs.logback.classic)

testImplementation(libs.ktor.server.test.host)
}

tasks.test {
useJUnitPlatform()
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,15 @@ import io.ktor.server.application.log
import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch
import org.jetbrains.kotlinconf.Conference
import org.jetbrains.kotlinconf.Session
import org.jetbrains.kotlinconf.Speaker
import java.util.concurrent.TimeUnit

@Volatile
private var conference: Conference? = null
private val conference = MutableSharedFlow<Conference>()
val comeBackLater = HttpStatusCode(477, "Come Back Later")
val GMT_TIME_OFFSET = 2 * 60 * 60 * 1000

Expand All @@ -38,9 +39,11 @@ fun Application.launchSyncJob(
suspend fun synchronizeWithSessionize(
sessionizeUrl: String,
) {
conference = client.get(sessionizeUrl)
val updatedValue = client.get(sessionizeUrl)
.body<SessionizeData>()
.toConference()

conference.emit(updatedValue)
}

suspend fun fetchSessionizeImage(
Expand All @@ -50,7 +53,7 @@ suspend fun fetchSessionizeImage(
return client.get("$imagesUrl/$imageId").body<ByteArray>()
}

fun getSessionizeData(): Conference = conference ?: throw ServiceUnavailable()
suspend fun getSessionizeData(): Conference = conference.first()

fun SessionizeData.toConference(): Conference {
val tags: Map<Int, CategoryItemData> = categories
Expand All @@ -73,7 +76,7 @@ fun SessionizeData.toConference(): Conference {
endsAt,
tags
)
}
}.mergeWorkshops()

val speakers = speakers.map {
Speaker(
Expand All @@ -86,4 +89,35 @@ fun SessionizeData.toConference(): Conference {
}

return Conference(sessions, speakers)
}

internal fun List<Session>.mergeWorkshops(): List<Session> {
val (workshopParts, nonWorkshop) = partition { it.tags?.contains("Workshop") == true }

// Group by room
val workshopPartsByLocations = workshopParts.groupBy { it.location }
val workshops = workshopPartsByLocations.values.mapNotNull { parts ->
val startTime = parts.minOf { it.startsAt }
val endTime = parts.maxOf { it.endsAt }
val first = parts.find { it.title.contains("Part 1") } ?: return@mapNotNull null
val title = first.title.dropAfter(". Part").trim()

Session(
first.id,
title,
parts.joinToString("\n") { it.description },
parts.flatMap { it.speakerIds },
first.location,
startTime,
endTime,
first.tags
)
}

return nonWorkshop + workshops
}

internal fun String.dropAfter(chunk: String): String {
val index = indexOf(chunk)
return if (index == -1) this else substring(0, index)
}
39 changes: 39 additions & 0 deletions backend/src/test/kotlin/ApiTest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import io.ktor.client.call.body
import io.ktor.client.plugins.HttpRequestRetry
import io.ktor.client.plugins.contentnegotiation.ContentNegotiation
import io.ktor.client.request.get
import io.ktor.serialization.kotlinx.json.json
import io.ktor.server.config.ApplicationConfig
import io.ktor.server.testing.TestApplication
import kotlinx.coroutines.test.runTest
import org.jetbrains.kotlinconf.Conference
import org.junit.jupiter.api.Assertions.assertFalse
import org.junit.jupiter.api.Test


class ApiTest {

val app = TestApplication {
environment {
config = ApplicationConfig("test-application.yaml")
}
}

val client = app.createClient {
install(HttpRequestRetry) {
}
install(ContentNegotiation) {
json()
}
}

@Test
fun testWorkshopPartsAreMerged() = runTest {
val conference = client.get("/conference").body<Conference>()
val workshops = conference.sessions.filter { it.tags?.contains("Workshop") == true }

for (workshop in workshops) {
assertFalse("Part" in workshop.title)
}
}
}
59 changes: 59 additions & 0 deletions backend/src/test/kotlin/SessionizeTest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import org.jetbrains.kotlinconf.backend.dropAfter
import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Test

class StringExtTest {

@Test
fun `dropAfter should drop everything after the chunk`() {
val input = "This is a test string. Part 1"
val chunk = ". Part"
val expected = "This is a test string"
assertEquals(expected, input.dropAfter(chunk))
}

@Test
fun `dropAfter should return the original string if the chunk is not found`() {
val input = "This is a test string"
val chunk = ". Part"
assertEquals(input, input.dropAfter(chunk))
}

@Test
fun `dropAfter should handle empty string`() {
val input = ""
val chunk = ". Part"
assertEquals("", input.dropAfter(chunk))
}

@Test
fun `dropAfter should handle empty chunk`() {
val input = "This is a test string"
val chunk = ""
assertEquals("", input.dropAfter(chunk))
}

@Test
fun `dropAfter should handle chunk at the beginning`() {
val input = ". Part 1 This is a test string"
val chunk = ". Part"
val expected = ""
assertEquals(expected, input.dropAfter(chunk))
}

@Test
fun `dropAfter should handle chunk at the end`() {
val input = "This is a test string. Part"
val chunk = ". Part"
val expected = "This is a test string"
assertEquals(expected, input.dropAfter(chunk))
}

@Test
fun `dropAfter should handle multiple occurrences of the chunk`() {
val input = "This is a test string. Part 1. Part 2"
val chunk = ". Part"
val expected = "This is a test string"
assertEquals(expected, input.dropAfter(chunk))
}
}
25 changes: 25 additions & 0 deletions backend/src/test/resources/test-application.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
ktor:
deployment:
port: 8080

application:
modules:
- org.jetbrains.kotlinconf.backend.MainKt.conferenceBackend


sessionize:
url: "https://sessionize.com/api/v2/cdftbl11/view/All"
imagesUrl: "https://sessionize.com/image/"
interval: 60

database:
host: "$DB0_HOST:"
user: "$DB0_USERNAME:"
password: "$DB0_PASSWORD:"
database: "$DB0_NAME:"

poolSize: 5

service:
environment: test
secret: "$ADMIN_SECRET:"
6 changes: 4 additions & 2 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,12 @@ compose-hot-reload = "1.0.0-dev-39"
coroutines = "1.10.1"
coil = "3.0.3"
koin = "4.1.0-Beta5"
exposed = "0.55.0"
exposed = "0.59.0"
h2 = "2.2.224"
hikaricp = "5.1.0"
junit = "4.13.2"
kotlinx-datetime = "0.6.1"
ktor = "3.0.1"
ktor = "3.1.0"
android-svg = "1.4"
logbackClassic = "1.5.6"
markdown = "0.29.0"
Expand Down Expand Up @@ -89,6 +89,7 @@ ktor-server-netty = { module = "io.ktor:ktor-server-netty", version.ref = "ktor"
ktor-server-partial-content = { module = "io.ktor:ktor-server-partial-content", version.ref = "ktor" }
ktor-server-status-pages = { module = "io.ktor:ktor-server-status-pages", version.ref = "ktor" }
ktor-server-swagger = { module = "io.ktor:ktor-server-swagger", version.ref = "ktor" }
ktor-server-test-host = { module = "io.ktor:ktor-server-test-host", version.ref = "ktor" }

# utility
kotlinx-coroutines-swing = { module = "org.jetbrains.kotlinx:kotlinx-coroutines-swing", version.ref = "coroutines" }
Expand Down Expand Up @@ -119,5 +120,6 @@ kotlinMultiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref =
kotlinSerialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" }
kotlinParcelize = { id = "org.jetbrains.kotlin.plugin.parcelize", version.ref = "kotlin" }
kotlinJvm = { id = "org.jetbrains.kotlin.jvm", version.ref = "kotlin" }
ktor = { id = "io.ktor.plugin", version.ref = "ktor" }
compose-compiler = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
composeHotReload = { id = "org.jetbrains.compose-hot-reload", version.ref = "compose-hot-reload" }
5 changes: 5 additions & 0 deletions kotlin-js-store/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2761,6 +2761,11 @@ wrappy@1:
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==

[email protected]:
version "8.18.0"
resolved "https://registry.yarnpkg.com/ws/-/ws-8.18.0.tgz#0d7505a6eafe2b0e712d232b42279f53bc289bbc"
integrity sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==

[email protected]:
version "8.5.0"
resolved "https://registry.yarnpkg.com/ws/-/ws-8.5.0.tgz#bfb4be96600757fe5382de12c670dab984a1ed4f"
Expand Down
2 changes: 0 additions & 2 deletions settings.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS")
pluginManagement {
repositories {
maven("https://maven.pkg.jetbrains.space/public/p/compose/dev")
maven("https://maven.pkg.jetbrains.space/public/p/ktor/eap")
google()
gradlePluginPortal()
mavenCentral()
Expand All @@ -16,7 +15,6 @@ pluginManagement {
dependencyResolutionManagement {
repositories {
maven("https://maven.pkg.jetbrains.space/public/p/compose/dev")
maven("https://maven.pkg.jetbrains.space/public/p/ktor/eap")
google()
mavenCentral()
maven("https://packages.jetbrains.team/maven/p/firework/dev")
Expand Down

0 comments on commit 6a4037a

Please sign in to comment.