V3 von Brett her + constraints update

This commit is contained in:
Lars Westermann 2019-06-10 16:05:24 +02:00
parent afbced61e3
commit 23e042b90c
Signed by: lars.westermann
GPG key ID: 9D417FA5BB9D5E1D
16 changed files with 323 additions and 78 deletions

View file

@ -5,7 +5,7 @@ prefix = ""
debug = false debug = false
[schedule] [schedule]
reference = "2019-06-12" reference = "2019-06-10"
[general] [general]
wiki_url = "https://wiki.kif.rocks/w/index.php?title=KIF470:Arbeitskreise&action=raw" wiki_url = "https://wiki.kif.rocks/w/index.php?title=KIF470:Arbeitskreise&action=raw"

View file

@ -46,6 +46,7 @@ fun checkConstraints(
for (leader in schedule.workGroup.leader) { for (leader in schedule.workGroup.leader) {
for (s in against) { for (s in against) {
if ( if (
schedule != s &&
leader in s.workGroup.leader && leader in s.workGroup.leader &&
start <= s.getAbsoluteEndTime() && start <= s.getAbsoluteEndTime() &&
s.getAbsoluteStartTime() <= end s.getAbsoluteStartTime() <= end
@ -59,7 +60,7 @@ fun checkConstraints(
when (type) { when (type) {
ConstraintType.OnlyOnDay -> { ConstraintType.OnlyOnDay -> {
val onlyOnDay = constraints.map { it.day == schedule.day } val onlyOnDay = constraints.map { it.day == schedule.day }
if (!onlyOnDay.any()) { if (onlyOnDay.none()) {
val dayList = constraints.mapNotNull { it.day }.distinct().sorted() val dayList = constraints.mapNotNull { it.day }.distinct().sorted()
errors += ConstraintError("Work group requires days $dayList, but is on ${schedule.day}!") errors += ConstraintError("Work group requires days $dayList, but is on ${schedule.day}!")
} }
@ -77,11 +78,11 @@ fun checkConstraints(
for (it in constraints) { for (it in constraints) {
if (it.time == null) continue if (it.time == null) continue
if (it.day == null) { if (it.day == null) {
if (it.time > schedule.time) { if (it.time < schedule.time) {
errors += ConstraintError("Work group requires before time ${it.time}, but is on ${schedule.time}!") errors += ConstraintError("Work group requires before time ${it.time}, but is on ${schedule.time}!")
} }
} else { } else {
if (it.day == schedule.day && it.time > schedule.time) { if (it.day == schedule.day && it.time < schedule.time) {
errors += ConstraintError("Work group requires before time ${it.time} on day ${it.day}, but is on ${schedule.time}!") errors += ConstraintError("Work group requires before time ${it.time} on day ${it.day}, but is on ${schedule.time}!")
} }
} }
@ -92,11 +93,11 @@ fun checkConstraints(
for (it in constraints) { for (it in constraints) {
if (it.time == null) continue if (it.time == null) continue
if (it.day == null) { if (it.day == null) {
if (it.time < schedule.time) { if (it.time > schedule.time) {
errors += ConstraintError("Work group requires after time ${it.time}, but is on ${schedule.time}!") errors += ConstraintError("Work group requires after time ${it.time}, but is on ${schedule.time}!")
} }
} else { } else {
if (it.day == schedule.day && it.time < schedule.time) { if (it.day == schedule.day && it.time > schedule.time) {
errors += ConstraintError("Work group requires after time ${it.time} on day ${it.day}, but is on ${schedule.time}!") errors += ConstraintError("Work group requires after time ${it.time} on day ${it.day}, but is on ${schedule.time}!")
} }
} }

View file

@ -4,8 +4,37 @@ import com.soywiz.klock.DateFormat
import com.soywiz.klock.KlockLocale import com.soywiz.klock.KlockLocale
import com.soywiz.klock.format import com.soywiz.klock.format
import com.soywiz.klock.locale.german import com.soywiz.klock.locale.german
import com.soywiz.klock.min
fun formatDateTime(unix: Long) = fun formatDateTime(unix: Long) =
DateFormat("EEEE, d. MMMM y HH:mm") DateFormat("EEEE, d. MMMM y HH:mm")
.withLocale(KlockLocale.german) .withLocale(KlockLocale.german)
.format(unix) .format(unix)
fun formatTimeDiff(diff: Long): String {
var time = diff / 1000
val seconds = time % 60
time /= 60
val minutes = time % 60
time /= 60
val hours = time % 24
time /= 24
val days = time
return when {
days > 0L -> {
if (days == 1L) "1 Tag" else "$days Tagen"
}
hours > 0L -> {
hours.toString().padStart(2, '0')+":"+ (minutes + if (seconds > 0) 1 else 0).toString().padStart(2,'0')
}
minutes > 0L -> {
"00:"+ (minutes+ if (seconds > 0) 1 else 0).toString().padStart(2,'0')
}
seconds > 0L -> {
"> 1 Minute"
}
else -> "vor $minutes Minuten"
}
}

View file

@ -25,6 +25,11 @@ object ScheduleRepository : Repository<Schedule> {
return parser.parse(json, Schedule.serializer()) return parser.parse(json, Schedule.serializer())
} }
suspend fun getUpcoming(count: Int = 8): List<Schedule> {
val json = repositoryGet("$prefix/api/schedule/upcoming?count=$count") ?: return emptyList()
return parser.parse(json, Schedule.serializer().list)
}
override suspend fun create(model: Schedule): Long { override suspend fun create(model: Schedule): Long {
return repositoryPost("$prefix/api/schedules", Message.json.stringify(Schedule.serializer(), model)) return repositoryPost("$prefix/api/schedules", Message.json.stringify(Schedule.serializer(), model))
?: throw IllegalStateException("Cannot create model!") ?: throw IllegalStateException("Cannot create model!")

View file

@ -1,38 +1,51 @@
package de.kif.frontend.views.board package de.kif.frontend.views.board
import com.soywiz.klock.DateFormat import com.soywiz.klock.DateFormat
import com.soywiz.klock.DateTimeTz
import com.soywiz.klock.KlockLocale import com.soywiz.klock.KlockLocale
import com.soywiz.klock.format
import com.soywiz.klock.locale.german import com.soywiz.klock.locale.german
import de.kif.frontend.views.overview.getByClassOrCreate
import de.westermann.kwebview.interval import de.westermann.kwebview.interval
import de.westermann.kwebview.iterator
import org.w3c.dom.HTMLElement import org.w3c.dom.HTMLElement
import org.w3c.dom.HTMLSpanElement
import org.w3c.dom.get import org.w3c.dom.get
import kotlin.browser.document import kotlin.browser.document
import kotlin.js.Date import kotlin.js.Date
fun initBoard() { fun initBoard() {
//val boardSchedules = document.getElementsByClassName("board-schedules")[0] as HTMLElement val dateContainer = document.getElementsByClassName("board-header-date")[0] as HTMLElement
val dateView = document.getElementsByClassName("board-header-date")[0] as HTMLElement
//val referenceTime = boardSchedules.dataset["reference"]?.toLongOrNull() ?: 0L
/* val timeView = dateContainer.getByClassOrCreate<HTMLSpanElement>("board-header-date-time")
val dateView = dateContainer.getByClassOrCreate<HTMLSpanElement>("board-header-date-date")
val initTime = Date.now().toLong()
val referenceInitTime = dateContainer.dataset["now"]?.toLongOrNull() ?: initTime
val diff = initTime - referenceInitTime
val boardRunning = document.getElementsByClassName("board-running")[0] as HTMLElement
val scheduleList = mutableListOf<BoardSchedule>() val scheduleList = mutableListOf<BoardSchedule>()
for (bs in boardSchedules.getElementsByClassName("board-schedule").iterator()) { for (bs in boardRunning.getElementsByClassName("board-schedule").iterator()) {
scheduleList += BoardSchedule(bs) scheduleList += BoardSchedule(bs)
} }
*/
interval(1000) { interval(1000) {
val now = Date.now().toLong() + diff
val currentTime = Date().let { val dt = DateTimeTz.fromUnixLocal(now)
it.getHours().toString().padStart(2, '0') + ":" + it.getMinutes().toString().padStart(2, '0')
} timeView.textContent = DateFormat("HH:mm")
.withLocale(KlockLocale.german)
.format(dt)
dateView.textContent = DateFormat("EEEE, d. MMMM y")
.withLocale(KlockLocale.german)
.format(dt)
dateView.innerText = currentTime
/*
val now = referenceTime - currentTime / 1000
scheduleList.forEach { it.updateTime(now) } scheduleList.forEach { it.updateTime(now) }
*/
} }
} }

View file

@ -1,24 +1,36 @@
package de.kif.frontend.views.board package de.kif.frontend.views.board
import de.kif.common.formatDateTime
import de.kif.common.formatTimeDiff
import de.kif.common.model.Schedule import de.kif.common.model.Schedule
import de.kif.frontend.views.overview.getByClassOrCreate import de.kif.frontend.views.overview.getByClassOrCreate
import de.westermann.kwebview.View import de.westermann.kwebview.View
import org.w3c.dom.HTMLDivElement
import org.w3c.dom.HTMLElement import org.w3c.dom.HTMLElement
import org.w3c.dom.HTMLSpanElement import org.w3c.dom.HTMLSpanElement
import org.w3c.dom.get import org.w3c.dom.get
import kotlin.js.Date
class BoardSchedule( class BoardSchedule(
view: HTMLElement view: HTMLElement
) : View(view) { ) : View(view) {
private val roomView = view.getByClassOrCreate<HTMLSpanElement>("board-schedule-room") private val colorView = view.getByClassOrCreate<HTMLDivElement>("board-schedule-color")
private val timeView = view.getByClassOrCreate<HTMLSpanElement>("board-schedule-time") private val timeView = view.getByClassOrCreate<HTMLDivElement>("board-schedule-time")
private val colorView = view.getByClassOrCreate<HTMLSpanElement>("board-schedule-color") private val nameView = view.getByClassOrCreate<HTMLDivElement>("board-schedule-name")
private val nameView = view.getByClassOrCreate<HTMLSpanElement>("board-schedule-name") private val roomView = view.getByClassOrCreate<HTMLDivElement>("board-schedule-room")
private var time: Long = timeView.dataset["time"]?.toLongOrNull() ?: 0L private val schedule: Long = view.dataset["id"]?.toLongOrNull() ?: 0L
private val startTime: Long = timeView.dataset["startTime"]?.toLongOrNull() ?: 0L
private val endTime: Long = timeView.dataset["endTime"]?.toLongOrNull() ?: 0L
fun updateTime(now: Long) { fun updateTime(now: Long) {
//console.log(time.toString(), now.toString()) timeView.textContent = if (startTime >= now) {
timeView.textContent = Schedule.timeDifferenceToString(time + now) "Start in ${formatTimeDiff(startTime - now)}"
} else {
"Ende in ${formatTimeDiff(endTime - now)}"
}
}
init {
} }
} }

View file

@ -64,7 +64,7 @@ class Calendar(calendar: HTMLElement) : View(calendar) {
for ((s, l) in errors.map) { for ((s, l) in errors.map) {
for (entry in body.calendarEntries) { for (entry in body.calendarEntries) {
if (entry.scheduleId == s) { if (entry.scheduleId == s) {
entry.error = l.isNotEmpty() entry.setError(l)
} }
} }
} }

View file

@ -1,6 +1,7 @@
package de.kif.frontend.views.calendar package de.kif.frontend.views.calendar
import de.kif.common.CALENDAR_GRID_WIDTH import de.kif.common.CALENDAR_GRID_WIDTH
import de.kif.common.ConstraintError
import de.kif.common.model.Schedule import de.kif.common.model.Schedule
import de.kif.common.model.WorkGroup import de.kif.common.model.WorkGroup
import de.westermann.kwebview.iterator import de.westermann.kwebview.iterator
@ -38,7 +39,7 @@ class CalendarEntry(private val calendar: CalendarBody, view: HTMLElement) : Vie
var length = dataset["length"]?.toIntOrNull() ?: 0 var length = dataset["length"]?.toIntOrNull() ?: 0
var pending by classList.property("pending") var pending by classList.property("pending")
var error by classList.property("error") private var error by classList.property("error")
private var nextScroll = 0.0 private var nextScroll = 0.0
private val editable: Boolean private val editable: Boolean
@ -172,6 +173,7 @@ class CalendarEntry(private val calendar: CalendarBody, view: HTMLElement) : Vie
} }
private val calendarTools = if (editable) CalendarTools(this) else null private val calendarTools = if (editable) CalendarTools(this) else null
private val calendarErrors = if (editable) CalendarErrors() else null
init { init {
onMouseDown { event -> onMouseDown { event ->
@ -207,6 +209,9 @@ class CalendarEntry(private val calendar: CalendarBody, view: HTMLElement) : Vie
calendarTools.update(s) calendarTools.update(s)
} }
} }
if (calendarErrors != null) {
html.appendChild(calendarErrors.html)
}
} }
fun load(schedule: Schedule) { fun load(schedule: Schedule) {
@ -263,6 +268,11 @@ class CalendarEntry(private val calendar: CalendarBody, view: HTMLElement) : Vie
nameView.textContent = workGroup.name nameView.textContent = workGroup.name
} }
fun setError(errors: List<ConstraintError>) {
error = errors.isNotEmpty()
calendarErrors?.setErrors(errors)
}
companion object { companion object {
fun create(calendar: CalendarBody, schedule: Schedule): CalendarEntry { fun create(calendar: CalendarBody, schedule: Schedule): CalendarEntry {
val entry = CalendarEntry(calendar, createHtmlView()) val entry = CalendarEntry(calendar, createHtmlView())

View file

@ -0,0 +1,21 @@
package de.kif.frontend.views.calendar
import de.kif.common.ConstraintError
import de.kif.common.model.Room
import de.kif.common.model.Schedule
import de.kif.frontend.launch
import de.kif.frontend.repository.ScheduleRepository
import de.westermann.kwebview.View
import de.westermann.kwebview.ViewCollection
import de.westermann.kwebview.components.*
class CalendarErrors() : ViewCollection<TextView>() {
fun setErrors(errors: List<ConstraintError>) {
clear()
for (error in errors) {
textView(error.reason)
}
}
}

View file

@ -25,7 +25,7 @@
} }
&:last-child { &:last-child {
width: 30%; width: 29%;
float: right; float: right;
} }
} }
@ -41,7 +41,7 @@
} }
.board-content { .board-content {
top: 8rem !important; top: 9rem !important;
bottom: 0; bottom: 0;
height: auto !important; height: auto !important;
} }
@ -59,15 +59,22 @@
} }
.board-running { .board-running {
column-count: 2; display: flex;
flex-wrap: wrap;
} }
.board-schedule { .board-schedule {
line-height: 1.3rem; line-height: 2rem;
height: 2rem; height: 2rem;
min-width: 10rem; min-width: 10rem;
display: flex; display: flex;
border-bottom: solid 1px var(--table-border-color) border-bottom: solid 1px var(--table-border-color);
width: calc(50% - 1rem);
margin-left: 1rem;
&:nth-last-child(1), &:nth-last-child(2) {
border-bottom: none;
}
} }
.board-schedule-color { .board-schedule-color {
@ -76,25 +83,33 @@
span { span {
display: block; display: block;
background-color: var(--primary-color); background-color: var(--primary-color);
width: 0.8rem; width: 0.5rem;
height: 0.8rem; height: 1rem;
border-radius: 100%; margin-top: 0.5rem;
margin-top: 0.1rem; margin-left: 0.3rem;
} }
} }
.board-schedule-time { .board-schedule-time {
width: 7rem; width: 8rem;
color: var(--text-secondary-color) color: var(--text-secondary-color);
text-align: center;
overflow: hidden;
text-overflow: ellipsis;
} }
.board-schedule-name { .board-schedule-name {
flex-grow: 1;
overflow: hidden;
text-overflow: ellipsis;
} }
.board-schedule-room { .board-schedule-room {
width: 4rem; width: 8rem;
text-align: right; text-align: right;
color: var(--text-secondary-color) color: var(--text-secondary-color);
overflow: hidden;
text-overflow: ellipsis;
} }
.board-calendar { .board-calendar {
@ -108,10 +123,12 @@
.calendar-table-box { .calendar-table-box {
width: 100%; width: 100%;
} }
.calendar-row { .calendar-row {
width: 100% !important; width: 100% !important;
height: 0.7rem !important; height: 0.7rem !important;
} }
.calendar-cell { .calendar-cell {
&:not(:first-child) { &:not(:first-child) {
flex-grow: 1; flex-grow: 1;
@ -119,6 +136,7 @@
flex-basis: 0; flex-basis: 0;
} }
} }
.calendar-entry::after { .calendar-entry::after {
content: none; content: none;
} }
@ -128,13 +146,25 @@
position: absolute; position: absolute;
line-height: 2rem; line-height: 2rem;
font-size: 2rem; font-size: 2rem;
top: 50%; top: 0;
margin-top: -1rem;
left: 8rem; left: 8rem;
padding: 1.8rem 0;
span {
display: block;
}
.board-header-date-time {
font-size: 2rem;
}
.board-header-date-date {
font-size: 1.2rem;
}
} }
.board-twitter { .board-twitter {
& > * { & > * {
margin-top: -1px !important; margin-top: -1px !important;
} }
} }

View file

@ -611,3 +611,30 @@
} }
} }
} }
.calendar-errors {
position: absolute;
top: 100%;
margin-top: 0.2rem;
left: 0;
background-color: var(--background-primary-color);
color: var(--text-secondary-color);
border-radius: $border-radius;
display: none;
z-index: 10;
width: max-content;
border: solid 1px var(--input-border-color);
box-shadow: 0 0.1rem 0.2rem var(--shadow-color);
span {
display: block;
padding: 0 0.5rem;
}
}
.calendar-entry.error {
.calendar-errors {
display: block;
}
}

View file

@ -75,7 +75,7 @@
display: none; display: none;
position: absolute; position: absolute;
background-color: var(--background-secondary-color); background-color: var(--background-secondary-color);
z-index: 5; z-index: 10;
left: 0; left: 0;
right: 0; right: 0;
@ -106,6 +106,7 @@
&:hover { &:hover {
.menu-content { .menu-content {
display: block; display: block;
border-bottom: solid 1px var(--table-border-color);
} }
} }
} }
@ -121,6 +122,7 @@
position: static; position: static;
background-color: transparent; background-color: transparent;
z-index: unset; z-index: unset;
border-bottom: none !important;
a { a {
display: inline-block; display: inline-block;

View file

@ -14,6 +14,7 @@
height: calc(33.3333% - 1rem); height: calc(33.3333% - 1rem);
overflow: hidden; overflow: hidden;
border-bottom: solid 2px var(--input-border-color); border-bottom: solid 2px var(--input-border-color);
position: relative;
&:first-child { &:first-child {
height: calc(33.3333% + 2rem); height: calc(33.3333% + 2rem);
@ -50,6 +51,7 @@
.calendar-body { .calendar-body {
display: flex; display: flex;
width: calc(100vw - 9.6rem);
} }
.calendar-row { .calendar-row {
@ -64,12 +66,27 @@
flex-grow: 1; flex-grow: 1;
flex-shrink: 1; flex-shrink: 1;
line-height: 2rem; line-height: 2rem;
width: 100%; width: 100% !important;
&:first-child {
padding-left: 0 !important;
span {
padding-left: 0.2rem;
position: absolute;
top: 0;
left: 0;
}
}
} }
.calendar-header { .calendar-header {
.calendar-cell { .calendar-cell {
overflow: hidden; overflow: hidden;
text-overflow: ellipsis;
span {
padding: 0 0.2rem;
}
} }
} }
} }
@ -77,9 +94,34 @@
.wall-calendar { .wall-calendar {
margin-top: 0 !important; margin-top: 0 !important;
height: 100% !important; height: 100% !important;
width: calc(100% - 2.4rem);
margin-left: 2.4rem;
.calendar-table { .calendar-table {
overflow: hidden; overflow: hidden;
padding: 0; padding: 0;
} }
} }
.wall-name {
position: absolute;
left: 0;
top: 0;
width: 2.4rem;
height: 100%;
border-right: solid 1px var(--table-border-color);
span {
display: block;
position: absolute;
transform: rotate(-90deg);
width: 10rem;
line-height: 2.4rem;
text-align: center;
transform-origin: center;
left: 50%;
top: 50%;
margin-left: -5rem;
margin-top: -1.2rem;
}
}

View file

@ -4,6 +4,8 @@ import de.kif.backend.Configuration
import de.kif.backend.repository.RoomRepository import de.kif.backend.repository.RoomRepository
import de.kif.backend.repository.ScheduleRepository import de.kif.backend.repository.ScheduleRepository
import de.kif.backend.view.respondMain import de.kif.backend.view.respondMain
import de.kif.common.formatTimeDiff
import de.kif.common.model.Schedule
import io.ktor.routing.Route import io.ktor.routing.Route
import io.ktor.routing.get import io.ktor.routing.get
import kotlinx.css.CSSBuilder import kotlinx.css.CSSBuilder
@ -16,20 +18,40 @@ import java.util.*
import kotlin.math.max import kotlin.math.max
import kotlin.math.min import kotlin.math.min
data class BoardSchedule(
val schedule: Schedule,
val startTime: Int,
val endTime: Int
)
suspend fun getUpcoming(limit: Int = 8): List<Schedule> {
val now = (Date().time / (1000 * 60)).toInt() - (Configuration.Schedule.referenceDate.time / (1000 * 60))
return ScheduleRepository.all().asSequence()
.map {
BoardSchedule(
it,
it.getAbsoluteStartTime(),
it.getAbsoluteEndTime()
)
}
.filter { it.endTime > now }
.sortedBy { it.startTime }
.take(limit)
.map { it.schedule }
.toList()
}
fun Route.board() { fun Route.board() {
get("/brett") { get("/brett") {
val scheduleList = ScheduleRepository.all().map { val scheduleList = getUpcoming()
it to it.getAbsoluteStartTime() * 60
}.sortedBy { it.second }
val referenceTime = Configuration.Schedule.referenceDate.time / 1000 val now = Date()
val now = referenceTime - (Date().time / 1000) val referenceTime = Configuration.Schedule.referenceDate.time
val refDate = Configuration.Schedule.referenceDate val refDate = Configuration.Schedule.referenceDate
val todayDate = Date()
val refDay = refDate.time / (1000 * 60 * 60 * 24) val refDay = refDate.time / (1000 * 60 * 60 * 24)
val todayDay = todayDate.time / (1000 * 60 * 60 * 24) val todayDay = now.time / (1000 * 60 * 60 * 24)
val day = (todayDay - refDay).toInt() val day = (todayDay - refDay).toInt()
val list = ScheduleRepository.getByDay(day) val list = ScheduleRepository.getByDay(day)
@ -66,7 +88,7 @@ fun Route.board() {
div("board") { div("board") {
div("board-header") { div("board-header") {
div("board-running") { div("board-running") {
for ((schedule, time) in scheduleList) { for (schedule in scheduleList) {
div("board-schedule") { div("board-schedule") {
attributes["data-id"] = schedule.id.toString() attributes["data-id"] = schedule.id.toString()
@ -82,25 +104,17 @@ fun Route.board() {
} }
div("board-schedule-time") { div("board-schedule-time") {
attributes["data-time"] = time.toString() val startTime = ((schedule.getAbsoluteStartTime() * 60 * 1000) + referenceTime)
attributes["data-duration"] = schedule.workGroup.length.toString() val endTime = ((schedule.getAbsoluteEndTime() * 60 * 1000) + referenceTime)
attributes["data-start-time"] = startTime.toString()
attributes["data-end-time"] = endTime.toString()
val startTime = (time % MINUTES_OF_DAY).let { if (startTime >= now.time) {
if (it < 0) it + MINUTES_OF_DAY else it +"Start in ${formatTimeDiff(startTime - now.time)}"
} else {
+"Ende in ${formatTimeDiff(endTime - now.time)}"
} }
val sm = (startTime % 60).toString().padStart(2, '0')
val sh = (startTime / 60).toString().padStart(2, '0')
val startTimeString = "$sh:$sm"
val endTime = ((time + schedule.workGroup.length) % MINUTES_OF_DAY).let {
if (it < 0) it + MINUTES_OF_DAY else it
}
val em = (endTime % 60).toString().padStart(2, '0')
val eh = (endTime / 60).toString().padStart(2, '0')
val endTimeString = "$eh:$em"
+"$startTimeString - $endTimeString"
} }
div("board-schedule-name") { div("board-schedule-name") {
@ -116,6 +130,8 @@ fun Route.board() {
div("board-logo") { div("board-logo") {
img("KIF 47.0", "/static/images/logo.svg") img("KIF 47.0", "/static/images/logo.svg")
div("board-header-date") { div("board-header-date") {
attributes["data-now"] =
(now.time - 2 * 60 * 60 * 1000).toString()
} }
} }
} }

View file

@ -1,5 +1,8 @@
package de.kif.backend.route package de.kif.backend.route
import com.soywiz.klock.*
import com.soywiz.klock.locale.german
import de.kif.backend.Configuration
import de.kif.backend.repository.RoomRepository import de.kif.backend.repository.RoomRepository
import de.kif.backend.repository.ScheduleRepository import de.kif.backend.repository.ScheduleRepository
import de.kif.backend.view.respondMain import de.kif.backend.view.respondMain
@ -8,20 +11,25 @@ import de.kif.common.model.Schedule
import io.ktor.routing.Route import io.ktor.routing.Route
import io.ktor.routing.get import io.ktor.routing.get
import kotlinx.html.div import kotlinx.html.div
import kotlinx.html.span
import kotlin.math.max import kotlin.math.max
import kotlin.math.min import kotlin.math.min
data class WallData( data class WallData(
val number: Int, val number: Int,
val schedules: Map<Room, Map<Int, Schedule>>, val schedules: Map<Room, Map<Int, Schedule>>,
val max: Int, val max: Int?,
val min: Int val min: Int?
) )
suspend fun genWallData(day: Int): WallData { suspend fun genWallData(day: Int): WallData {
val list = ScheduleRepository.getByDay(day) val list = ScheduleRepository.getByDay(day)
val rooms = RoomRepository.all()
if (list.isEmpty()) return WallData(day, rooms.associateWith { emptyMap<Int, Schedule>() }, null, null)
val schedules = val schedules =
RoomRepository.all().associateWith { emptyMap<Int, Schedule>() } + list.groupBy { it.room }.mapValues { (_, it) -> rooms.associateWith { emptyMap<Int, Schedule>() } + list.groupBy { it.room }.mapValues { (_, it) ->
it.associateBy { it.associateBy {
it.time it.time
} }
@ -51,14 +59,32 @@ fun Route.wall() {
val days = (0..2).map { genWallData(it) } val days = (0..2).map { genWallData(it) }
val min = days.map { it.min }.min() ?: days.first().min var min = days.map { it.min }.filterNotNull().min() ?: 12 * 60
val max = days.map { it.max }.max() ?: days.first().max val max = days.map { it.max }.filterNotNull().max() ?: 12 * 60
if (min > max) {
min = max
}
val refDate = DateTime(Configuration.Schedule.referenceDate.time)
respondMain(true, true) { respondMain(true, true) {
content { content {
div("wall") { div("wall") {
for (day in days) { for (day in days) {
val date = refDate + day.number.days
val dateString = DateFormat("EEEE, d. MMMM")
.withLocale(KlockLocale.german)
.format(date)
div("wall-box") { div("wall-box") {
div("wall-name") {
span {
+dateString
}
}
div("wall-calendar calendar") { div("wall-calendar calendar") {
renderCalendar( renderCalendar(
CalendarOrientation.TIME_TO_ROOM, CalendarOrientation.TIME_TO_ROOM,

View file

@ -3,6 +3,7 @@ package de.kif.backend.route.api
import de.kif.backend.authenticate import de.kif.backend.authenticate
import de.kif.backend.repository.RoomRepository import de.kif.backend.repository.RoomRepository
import de.kif.backend.repository.ScheduleRepository import de.kif.backend.repository.ScheduleRepository
import de.kif.backend.route.getUpcoming
import de.kif.common.model.Permission import de.kif.common.model.Permission
import de.kif.common.model.Schedule import de.kif.common.model.Schedule
import io.ktor.application.call import io.ktor.application.call
@ -24,6 +25,16 @@ fun Route.scheduleApi() {
} }
} }
get("/api/schedules/upcoming") {
try {
val count = call.parameters["count"]?.toIntOrNull() ?: 8
val schedules = getUpcoming(count)
call.success(schedules)
} catch (_: Exception) {
call.error(HttpStatusCode.InternalServerError)
}
}
post("/api/schedules") { post("/api/schedules") {
try { try {
authenticate(Permission.SCHEDULE) { authenticate(Permission.SCHEDULE) {