Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merge workshop parts in a single one #283

Merged
merged 3 commits into from
Feb 20, 2025
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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