2022-08-29 20:58:19 +00:00
|
|
|
// timetable.go
|
|
|
|
package controllers
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"fmt"
|
|
|
|
"html/template"
|
2022-09-21 20:24:08 +00:00
|
|
|
"log"
|
2022-09-20 10:21:01 +00:00
|
|
|
"officeHours/models"
|
2022-09-24 13:01:33 +00:00
|
|
|
"officeHours/templating"
|
2022-08-29 20:58:19 +00:00
|
|
|
)
|
|
|
|
|
2023-07-12 06:35:05 +00:00
|
|
|
// A function that takes a slice of office hours and
|
|
|
|
// constructs a timetable t such that t[date][slot] is an
|
|
|
|
// office hour that takes place at this time and no such cell has more than one
|
|
|
|
// office hour and each officehour is in exactly one slot.
|
|
|
|
// It also indicates how many slots exist per day.
|
|
|
|
func (b *BaseHandler) GetTimetable(officeHours []models.OfficeHour) (map[models.Date]map[int]models.OfficeHour, []int) {
|
|
|
|
var timetable = make(map[models.Date]map[int]models.OfficeHour)
|
2022-08-29 20:58:19 +00:00
|
|
|
for _, officeHour := range officeHours {
|
|
|
|
var slot int = 0
|
2022-09-19 12:46:16 +00:00
|
|
|
for minute := 0; minute < officeHour.Duration; minute += b.config.Date.MinuteGranularity { // find slot id
|
2023-07-12 06:35:05 +00:00
|
|
|
_, exists := timetable[models.GetEndDate(officeHour.Date, minute, true)]
|
2022-08-29 20:58:19 +00:00
|
|
|
if exists {
|
2023-07-12 06:35:05 +00:00
|
|
|
_, exists := timetable[models.GetEndDate(officeHour.Date, minute, true)][slot]
|
2022-08-29 20:58:19 +00:00
|
|
|
if exists {
|
|
|
|
slot += 1
|
|
|
|
minute = 0
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
} else {
|
2023-07-12 06:35:05 +00:00
|
|
|
timetable[models.GetEndDate(officeHour.Date, minute, true)] = make(map[int]models.OfficeHour)
|
2022-08-29 20:58:19 +00:00
|
|
|
}
|
|
|
|
}
|
2022-09-19 12:46:16 +00:00
|
|
|
for minute := 0; minute < officeHour.Duration; minute += b.config.Date.MinuteGranularity { // write officeHour id to timetable
|
2023-07-12 06:35:05 +00:00
|
|
|
timetable[models.GetEndDate(officeHour.Date, minute, true)][slot] = officeHour
|
2022-08-29 20:58:19 +00:00
|
|
|
}
|
|
|
|
}
|
2023-07-12 06:35:05 +00:00
|
|
|
slots := []int{1, 1, 1, 1, 1}
|
2024-01-03 16:04:38 +00:00
|
|
|
for date := range timetable {
|
2023-07-12 06:35:05 +00:00
|
|
|
if slots[date.Day] < len(timetable[date]) {
|
|
|
|
slots[date.Day] = len(timetable[date])
|
2022-08-29 20:58:19 +00:00
|
|
|
}
|
|
|
|
}
|
2023-07-12 06:35:05 +00:00
|
|
|
return timetable, slots
|
2022-08-29 20:58:19 +00:00
|
|
|
}
|
|
|
|
|
2023-07-12 06:35:05 +00:00
|
|
|
// A function to generate the HTML code for the table of office hours from a timetable.
|
2022-09-19 12:46:16 +00:00
|
|
|
func (b *BaseHandler) printTimetable(timetable map[models.Date]map[int]models.OfficeHour, slots []int, deleteIcons bool) template.HTML {
|
2023-07-09 13:26:58 +00:00
|
|
|
if len(timetable) == 0 { // no office hours to display
|
|
|
|
return template.HTML("<p class=\"text-center\">Aktuell sind keine passenden Sprechstunden eingetragen.</p>")
|
|
|
|
}
|
2022-08-29 20:58:19 +00:00
|
|
|
var tableBody string
|
2023-10-26 09:29:42 +00:00
|
|
|
for hour := b.config.Date.EarliestStartTime.Hour; hour < b.config.Date.LatestStartTime.Hour+b.config.Date.MaxDuration/60; hour += 1 {
|
2022-09-19 12:46:16 +00:00
|
|
|
for minute := 0; minute < 60; minute += b.config.Date.MinuteGranularity {
|
2022-08-29 20:58:19 +00:00
|
|
|
tableBody += "<tr>"
|
|
|
|
if minute == 0 {
|
2023-07-09 11:01:01 +00:00
|
|
|
tableBody += fmt.Sprintf("<td class=\"timetableRightBorder\">%d Uhr</td>\n", hour)
|
2022-08-29 20:58:19 +00:00
|
|
|
} else {
|
2023-07-09 11:01:01 +00:00
|
|
|
tableBody += "<td class=\"timetableRightBorder\"></td>\n"
|
2022-08-29 20:58:19 +00:00
|
|
|
}
|
|
|
|
for day := 0; day < 5; day += 1 {
|
|
|
|
for slot := 0; slot < slots[day]; slot += 1 {
|
2022-09-19 16:51:51 +00:00
|
|
|
current, currentExists := timetable[models.Date{Week: 0, Day: day, Hour: hour, Minute: minute}][slot]
|
2022-08-29 20:58:19 +00:00
|
|
|
|
|
|
|
if currentExists { // This slot is taken by some office hour
|
|
|
|
var continued bool = false // is this slot occupied by the same office hour the previous minute?
|
|
|
|
var predecessorExists bool
|
|
|
|
var predecessor models.OfficeHour
|
2022-09-19 12:46:16 +00:00
|
|
|
if hour > 0 && minute < b.config.Date.MinuteGranularity {
|
2022-09-19 16:51:51 +00:00
|
|
|
predecessor, predecessorExists = timetable[models.Date{Week: 0, Day: day, Hour: hour - 1, Minute: 60 - b.config.Date.MinuteGranularity}][slot]
|
2022-08-29 20:58:19 +00:00
|
|
|
} else {
|
2022-09-19 16:51:51 +00:00
|
|
|
predecessor, predecessorExists = timetable[models.Date{Week: 0, Day: day, Hour: hour, Minute: minute - b.config.Date.MinuteGranularity}][slot]
|
2022-08-29 20:58:19 +00:00
|
|
|
}
|
|
|
|
if predecessorExists {
|
|
|
|
continued = (predecessor == current)
|
|
|
|
} else {
|
|
|
|
continued = false
|
|
|
|
}
|
|
|
|
if continued {
|
|
|
|
continue
|
|
|
|
} else {
|
|
|
|
var celldata bytes.Buffer
|
|
|
|
data := struct {
|
2022-09-05 18:10:35 +00:00
|
|
|
OfficeHour models.OfficeHour
|
|
|
|
MinuteGranularity int
|
|
|
|
DeleteIcons bool
|
2022-09-19 16:51:51 +00:00
|
|
|
}{OfficeHour: current, MinuteGranularity: b.config.Date.MinuteGranularity, DeleteIcons: deleteIcons}
|
2022-09-24 13:01:33 +00:00
|
|
|
err := templating.WriteTemplate(&celldata, "td", data)
|
|
|
|
if err != nil {
|
|
|
|
err = fmt.Errorf("writing table cell failed:\n%w", err)
|
|
|
|
log.Println(err.Error())
|
|
|
|
// TODO: better error wrapping up to top-level request handler
|
2022-09-21 20:24:08 +00:00
|
|
|
}
|
2022-08-29 20:58:19 +00:00
|
|
|
tableBody += celldata.String()
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if slot+1 == slots[day] {
|
2023-07-09 11:01:01 +00:00
|
|
|
tableBody += "<td class=\"timetableRightBorder\"></td>\n"
|
2022-08-29 20:58:19 +00:00
|
|
|
} else {
|
|
|
|
tableBody += "<td></td>\n"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
tableBody += "</tr>\n"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
var table bytes.Buffer
|
|
|
|
|
|
|
|
tableData := struct {
|
|
|
|
ColspanMon int
|
|
|
|
ColspanTue int
|
|
|
|
ColspanWed int
|
|
|
|
ColspanThu int
|
|
|
|
ColspanFri int
|
|
|
|
TableBody template.HTML
|
|
|
|
}{
|
|
|
|
slots[0],
|
|
|
|
slots[1],
|
|
|
|
slots[2],
|
|
|
|
slots[3],
|
|
|
|
slots[4],
|
|
|
|
template.HTML(tableBody),
|
|
|
|
}
|
2022-09-24 13:01:33 +00:00
|
|
|
err := templating.WriteTemplate(&table, "officeHourTable", tableData)
|
|
|
|
if err != nil {
|
|
|
|
err = fmt.Errorf("writing table failed:\n%w", err)
|
|
|
|
log.Println(err.Error())
|
|
|
|
// TODO: better error wrapping up to top-level request handler
|
2022-09-21 20:24:08 +00:00
|
|
|
}
|
2022-08-29 20:58:19 +00:00
|
|
|
return template.HTML(table.String())
|
|
|
|
}
|