From 4fcfa76e1cf294385d6953c1394bdc4b69fe993f Mon Sep 17 00:00:00 2001 From: survellow <59056368+survellow@users.noreply.github.com> Date: Fri, 2 Aug 2024 23:55:58 +0200 Subject: [PATCH 1/4] feat:#22 refactor to cache room schedule and webworker config --- backend/model/roomOccupancyModel.go | 1 + backend/openapi.yml | 24 +++------ backend/service/addRoute.go | 4 +- backend/service/functions/semester.go | 30 ++++++++++- backend/service/room/roomService.go | 16 +++--- frontend/src/api/fetchRoomOccupancy.ts | 22 ++------ .../src/components/RoomOccupationOffline.vue | 20 +++---- frontend/src/i18n/index.ts | 50 +++++++++++++++++ frontend/src/i18n/translations/de.json | 4 +- frontend/src/i18n/translations/en.json | 4 +- frontend/src/i18n/translations/ja.json | 4 +- frontend/src/model/roomOccupancyList.ts | 2 + frontend/src/view/RoomFinderOffline.vue | 53 ++++++++++++------- frontend/vite.config.ts | 13 ++++- 14 files changed, 164 insertions(+), 83 deletions(-) diff --git a/backend/model/roomOccupancyModel.go b/backend/model/roomOccupancyModel.go index 09c7404..b61b905 100644 --- a/backend/model/roomOccupancyModel.go +++ b/backend/model/roomOccupancyModel.go @@ -29,6 +29,7 @@ type RoomOccupancy struct { type RoomOccupancyList struct { Start time.Time `bson:"start"` + Updated time.Time `bson:"updated"` Granularity int `bson:"granularity"` Blocks int `bson:"blocks"` Rooms []RoomOccupancy `bson:"rooms"` diff --git a/backend/openapi.yml b/backend/openapi.yml index 5ef272d..0c30de7 100644 --- a/backend/openapi.yml +++ b/backend/openapi.yml @@ -119,21 +119,6 @@ paths: /api/schedule/rooms: get: summary: Get Room Occupancy - parameters: - - name: from - in: query - description: date - example: "2024-12-24T00:00:00.000Z" - required: true - schema: - type: string - - name: to - in: query - description: date - example: "2024-12-25T00:00:00.000Z" - required: true - schema: - type: string responses: '200': description: Successful response @@ -294,6 +279,9 @@ components: start: type: string format: date-time + updated: + type: string + format: date-time granularity: type: integer blocks: @@ -306,9 +294,9 @@ components: occupancy: type: string format: binary - required: - - name - - occupancy + required: + - name + - occupancy required: - start - granularity diff --git a/backend/service/addRoute.go b/backend/service/addRoute.go index d28070e..ca27d9a 100644 --- a/backend/service/addRoute.go +++ b/backend/service/addRoute.go @@ -181,9 +181,7 @@ func AddRoutes(app *pocketbase.PocketBase) { Method: http.MethodGet, Path: "/api/schedule/rooms", Handler: func(c echo.Context) error { - from := c.QueryParam("from") - to := c.QueryParam("to") - rooms, err := room.GetRoomOccupancyList(app, from, to, RoomOccupancyGranularity) + rooms, err := room.GetRoomOccupancyList(app, RoomOccupancyGranularity) if err != nil { slog.Error("Failed to get room occupancy: %v", "error", err) diff --git a/backend/service/functions/semester.go b/backend/service/functions/semester.go index b2f3b6a..442855e 100644 --- a/backend/service/functions/semester.go +++ b/backend/service/functions/semester.go @@ -16,15 +16,41 @@ package functions -import "time" +import ( + "time" +) + +const START_OF_SUMMER_SEMESTER_MONTH = time.April +const START_OF_WINTER_SEMESTER_MONTH = time.October // GetCurrentSemesterString returns the current semester as string // if current month is between 10 and 03 -> winter semester "ws" func GetCurrentSemesterString() string { - if time.Now().Month() >= 10 || time.Now().Month() <= 3 { + if now := time.Now(); isBeforeSummerSemester(now) || isAfterSummerSemester(now) { return "ws" } else { return "ss" } } + +// GetSemesterStart gibt das Startdatum des aktuellen Semesters zurück +func GetSemesterStart(date time.Time) time.Time { + if isBeforeSummerSemester(date) { + return time.Date(date.Year()-1, START_OF_WINTER_SEMESTER_MONTH, 1, 0, 0, 0, 0, date.Location()) + } else if isAfterSummerSemester(date) { + return time.Date(date.Year(), START_OF_WINTER_SEMESTER_MONTH, 1, 0, 0, 0, 0, date.Location()) + } else { + return time.Date(date.Year(), START_OF_SUMMER_SEMESTER_MONTH, 1, 0, 0, 0, 0, date.Location()) + } +} + +// Check if the given date is before the start of summer semester +func isBeforeSummerSemester(date time.Time) bool { + return date.Month() < START_OF_SUMMER_SEMESTER_MONTH +} + +// Check if the given date is after the end of summer semester +func isAfterSummerSemester(date time.Time) bool { + return date.Month() >= START_OF_WINTER_SEMESTER_MONTH +} diff --git a/backend/service/room/roomService.go b/backend/service/room/roomService.go index 2571711..67619db 100644 --- a/backend/service/room/roomService.go +++ b/backend/service/room/roomService.go @@ -68,16 +68,11 @@ func GetRoomSchedule(app *pocketbase.PocketBase, room string, from string, to st * @return room occupancy list * @return error if the database query fails */ -func GetRoomOccupancyList(app *pocketbase.PocketBase, from string, to string, granularity int) (model.RoomOccupancyList, error) { - // try parsing the time strings - fromTime, err := time.Parse(time.RFC3339, from) - if err != nil { - return model.RoomOccupancyList{}, err - } - toTime, err := time.Parse(time.RFC3339, to) - if err != nil { - return model.RoomOccupancyList{}, err - } +func GetRoomOccupancyList(app *pocketbase.PocketBase, granularity int) (model.RoomOccupancyList, error) { + + now := time.Now() + fromTime := functions.GetSemesterStart(now) + toTime := functions.GetSemesterStart(now.AddDate(0, 6, 0)) // calculate the number of blocks for the given time range and granularity timeDifference := toTime.Sub(fromTime) @@ -142,6 +137,7 @@ func getRelevantRooms(app *pocketbase.PocketBase) ([]string, error) { func emptyRoomOccupancyList(from time.Time, granularity int, blockCount int) model.RoomOccupancyList { return model.RoomOccupancyList{ Start: from, + Updated: time.Now(), Granularity: granularity, Blocks: blockCount, Rooms: []model.RoomOccupancy{}, diff --git a/frontend/src/api/fetchRoomOccupancy.ts b/frontend/src/api/fetchRoomOccupancy.ts index 59b787b..28432c7 100644 --- a/frontend/src/api/fetchRoomOccupancy.ts +++ b/frontend/src/api/fetchRoomOccupancy.ts @@ -16,7 +16,6 @@ import { BSON } from "bson"; import { RoomOccupancyList } from "@/model/roomOccupancyList.ts"; -import { addMonths } from "date-fns"; import { formatYearMonthDay } from "@/helpers/dates"; const END_OF_SUMMER_SEMESTER = "0930"; @@ -59,32 +58,19 @@ export function getSemesterStart(date: Date): Date { /** * Fetches the room occupancy for a given date range. - * @param from_date the start date of the date range - * @param to_date the end date of the date range - * @returns RoomOccupancyList - the room occupancy list + * @returns RoomOccupancyList - the room occupancy list containing all rooms */ -export async function fetchRoomOccupancy( - from_date?: string, - to_date?: string, -): Promise { - if (from_date == undefined) { - const new_from_date = getSemesterStart(new Date()); - from_date = new_from_date.toISOString(); - } - if (to_date == undefined) { - const new_to_date = getSemesterStart(addMonths(new Date(), 6)); - to_date = new_to_date.toISOString(); - } - +export async function fetchRoomOccupancy(): Promise { let roomOccupancyList: RoomOccupancyList = new RoomOccupancyList( new Date(), + new Date(2000, 0, 1), 0, 0, [], ); - await fetch("/api/schedule/rooms?from=" + from_date + "&to=" + to_date) + await fetch("/api/schedule/rooms") .then((response) => { return response.arrayBuffer(); }) diff --git a/frontend/src/components/RoomOccupationOffline.vue b/frontend/src/components/RoomOccupationOffline.vue index a9ba72f..c67a562 100644 --- a/frontend/src/components/RoomOccupationOffline.vue +++ b/frontend/src/components/RoomOccupationOffline.vue @@ -27,8 +27,6 @@ import {useI18n} from "vue-i18n"; import allLocales from "@fullcalendar/core/locales-all"; import router from "@/router"; import {formatYearMonthDay} from "@/helpers/dates"; -import {useQuery} from "@tanstack/vue-query"; -import {fetchRoomOccupancy} from "@/api/fetchRoomOccupancy"; import {isValid} from "date-fns"; import {RoomOccupancyList} from "@/model/roomOccupancyList"; @@ -39,6 +37,10 @@ const props = defineProps({ type: String, required: true, }, + occupancy: { + type: RoomOccupancyList, + required: true, + }, }); const date: Ref = ref(new Date()); @@ -56,6 +58,7 @@ function setDateFromQuery() { const day = queryDate.substring(6, 8); date.value = new Date(`${year}-${month}-${day}`); if (!isValid(date.value)) { + console.error("Invalid date in URL, using current date instead."); date.value = new Date(); } } @@ -77,6 +80,9 @@ const selectedRoom = computed(() => props.room); * @returns Anonymized occupancy events */ function transformData(data: RoomOccupancyList) { + if (!data || !selectedRoom.value || !currentDateFrom.value || !currentDateTo.value) { + return []; + } return data .decodeOccupancy( selectedRoom.value, @@ -89,15 +95,9 @@ function transformData(data: RoomOccupancyList) { })); } -const { data: occupancy } = useQuery({ - queryKey: ["roomOccupancy"], //, selectedRoom, currentDateFrom, currentDateTo], - queryFn: () => fetchRoomOccupancy(), - staleTime: 12 * 3600000, // 12 hours -}); - const occupations = computed(() => { - if (!occupancy.value) return; - return transformData(occupancy.value); + if (!props.occupancy) return; + return transformData(props.occupancy); }); watch(occupations, () => fullCalendar.value?.getApi().refetchEvents()); diff --git a/frontend/src/i18n/index.ts b/frontend/src/i18n/index.ts index 4db4b2f..3531804 100644 --- a/frontend/src/i18n/index.ts +++ b/frontend/src/i18n/index.ts @@ -34,6 +34,56 @@ function setup() { de, ja, }, + datetimeFormats: { + en: { + short: { + year: "numeric", + month: "short", + day: "numeric", + }, + long: { + year: "numeric", + month: "short", + day: "numeric", + weekday: "short", + hour: "numeric", + minute: "numeric", + hour12: true, + }, + }, + de: { + short: { + year: "numeric", + month: "short", + day: "numeric", + }, + long: { + year: "numeric", + month: "short", + day: "numeric", + weekday: "short", + hour: "numeric", + minute: "numeric", + hour12: false, + }, + }, + ja: { + short: { + year: "numeric", + month: "short", + day: "numeric", + }, + long: { + year: "numeric", + month: "short", + day: "numeric", + weekday: "short", + hour: "numeric", + minute: "numeric", + hour12: true, + }, + }, + }, }); return _i18n; } diff --git a/frontend/src/i18n/translations/de.json b/frontend/src/i18n/translations/de.json index b8953be..1dc3422 100644 --- a/frontend/src/i18n/translations/de.json +++ b/frontend/src/i18n/translations/de.json @@ -33,7 +33,9 @@ "noRoomsAvailable": "Keine Räume verfügbar", "available": "verfügbar", "occupied": "belegt", - "stub": "bitte online prüfen" + "stub": "bitte online prüfen", + "lastUpdate": "Zeitpunkt der letzten Aktualisierung", + "noData": "Es konnten keine Daten geladen werden." }, "freeRooms": { "freeRooms": "Freie Räume", diff --git a/frontend/src/i18n/translations/en.json b/frontend/src/i18n/translations/en.json index 5f24aa8..6e36f03 100644 --- a/frontend/src/i18n/translations/en.json +++ b/frontend/src/i18n/translations/en.json @@ -33,7 +33,9 @@ "noRoomsAvailable": "no rooms listed", "available": "available", "occupied": "occupied", - "stub": "please check online" + "stub": "please check online", + "lastUpdate": "time of last update", + "noData": "could not load data" }, "freeRooms": { "freeRooms": "free rooms", diff --git a/frontend/src/i18n/translations/ja.json b/frontend/src/i18n/translations/ja.json index 75c3b15..ee3fc41 100644 --- a/frontend/src/i18n/translations/ja.json +++ b/frontend/src/i18n/translations/ja.json @@ -33,7 +33,9 @@ "noRoomsAvailable": "利用可能な部屋がありません", "available": "利用可能", "occupied": "占有中", - "stub": "オンラインでご確認ください。" + "stub": "オンラインでご確認ください。", + "lastUpdate": "最終更新時刻", + "noData": "データをロードできませんでした。" }, "freeRooms": { "freeRooms": "空いている部屋", diff --git a/frontend/src/model/roomOccupancyList.ts b/frontend/src/model/roomOccupancyList.ts index c58fc90..f7e6369 100644 --- a/frontend/src/model/roomOccupancyList.ts +++ b/frontend/src/model/roomOccupancyList.ts @@ -65,6 +65,7 @@ class RoomOccupancy { export class RoomOccupancyList { constructor( public start: Date, + public updated: Date, public granularity: number, public blocks: number, public rooms: RoomOccupancy[], @@ -312,6 +313,7 @@ export class RoomOccupancyList { public static fromJSON(json: Document): RoomOccupancyList { return new RoomOccupancyList( json.start, + json.updated, json.granularity, json.blocks, json.rooms.map( diff --git a/frontend/src/view/RoomFinderOffline.vue b/frontend/src/view/RoomFinderOffline.vue index dc698c6..5487f32 100644 --- a/frontend/src/view/RoomFinderOffline.vue +++ b/frontend/src/view/RoomFinderOffline.vue @@ -18,16 +18,29 @@ along with this program. If not, see .