[#10] Move to new endpoint (room schedule for given duration)

This commit is contained in:
Tom Wahl
2023-10-28 13:21:23 +02:00
parent f9ef791d67
commit f09f952ca3
9 changed files with 1171 additions and 263 deletions

View File

@ -1,4 +1,4 @@
openapi: 3.0.0 xopenapi: 3.0.0
info: info:
title: Your API title: Your API
version: 1.0.0 version: 1.0.0

View File

@ -93,11 +93,12 @@ func AddRoutes(app *pocketbase.PocketBase) {
app.OnBeforeServe().Add(func(e *core.ServeEvent) error { app.OnBeforeServe().Add(func(e *core.ServeEvent) error {
_, err := e.Router.AddRoute(echo.Route{ _, err := e.Router.AddRoute(echo.Route{
Method: http.MethodGet, Method: http.MethodGet,
Path: "/api/schedule/week", Path: "/api/schedule",
Handler: func(c echo.Context) error { Handler: func(c echo.Context) error {
roomParam := c.QueryParam("room") roomParam := c.QueryParam("room")
week := c.QueryParam("week") to := c.QueryParam("to")
return room.GetRoomScheduleForWeek(c, app, roomParam, week) from := c.QueryParam("from")
return room.GetRoomSchedule(c, app, roomParam, from, to)
}, },
Middlewares: []echo.MiddlewareFunc{ Middlewares: []echo.MiddlewareFunc{
apis.ActivityLogger(app), apis.ActivityLogger(app),

View File

@ -1,11 +1,13 @@
package db package db
import ( import (
"fmt"
"github.com/pocketbase/dbx" "github.com/pocketbase/dbx"
"github.com/pocketbase/pocketbase" "github.com/pocketbase/pocketbase"
"htwkalender/model" "htwkalender/model"
"htwkalender/service/functions" "htwkalender/service/functions"
"strings" "strings"
"time"
) )
func GetRooms(app *pocketbase.PocketBase) []string { func GetRooms(app *pocketbase.PocketBase) []string {
@ -40,7 +42,10 @@ func GetRoomScheduleForDay(app *pocketbase.PocketBase, room string, date string)
var events []model.Event var events []model.Event
// get all events from event records in the events collection // get all events from event records in the events collection
err := app.Dao().DB().Select("*").From("events").Where(dbx.Like("Rooms", room)).AndWhere(dbx.Like("Start", date)).All(&events) err := app.Dao().DB().Select("*").From("events").
Where(dbx.Like("Rooms", room)).
AndWhere(dbx.Like("Start", date)).
GroupBy("Start", "End", "Rooms").All(&events)
if err != nil { if err != nil {
print("Error while getting events from database: ", err) print("Error while getting events from database: ", err)
return nil return nil
@ -48,11 +53,27 @@ func GetRoomScheduleForDay(app *pocketbase.PocketBase, room string, date string)
return events return events
} }
func GetRoomScheduleForWeek(app *pocketbase.PocketBase, room string, week string) []model.Event { func GetRoomSchedule(app *pocketbase.PocketBase, room string, from string, to string) []model.Event {
var events []model.Event var events []model.Event
fromDate, err := time.Parse("2000-01-02", from)
if err != nil {
fmt.Println("Error parsing date 'from':", err)
return nil
}
toDate, err := time.Parse("2000-01-02", to)
if err != nil {
fmt.Println("Error parsing date 'to':", err)
return nil
}
// get all events from event records in the events collection // get all events from event records in the events collection
err := app.Dao().DB().Select("*").From("events").Where(dbx.Like("Rooms", room)).AndWhere(dbx.Like("Week", week)).GroupBy("week, start, end, room").All(&events) err = app.Dao().DB().Select("*").From("events").
Where(dbx.Like("Rooms", room)).
AndWhere(dbx.Between("Start", fromDate, toDate)).
GroupBy("Week", "Start", "End", "Rooms").
All(&events)
if err != nil { if err != nil {
print("Error while getting events from database: ", err) print("Error while getting events from database: ", err)
return nil return nil

View File

@ -17,7 +17,7 @@ func GetRoomScheduleForDay(c echo.Context, app *pocketbase.PocketBase, room stri
return c.JSON(http.StatusOK, events) return c.JSON(http.StatusOK, events)
} }
func GetRoomScheduleForWeek(c echo.Context, app *pocketbase.PocketBase, room string, week string) error { func GetRoomSchedule(c echo.Context, app *pocketbase.PocketBase, room string, from string, to string) error {
events := db.GetRoomScheduleForWeek(app, room, week) events := db.GetRoomSchedule(app, room, from, to)
return c.JSON(http.StatusOK, events) return c.JSON(http.StatusOK, events)
} }

File diff suppressed because it is too large Load Diff

View File

@ -31,6 +31,8 @@
"eslint-config-prettier": "^9.0.0", "eslint-config-prettier": "^9.0.0",
"eslint-plugin-vue": "^9.17.0", "eslint-plugin-vue": "^9.17.0",
"prettier": "3.0.2", "prettier": "3.0.2",
"sass": "^1.69.5",
"sass-loader": "^13.3.2",
"typescript": "^5.0.2", "typescript": "^5.0.2",
"vite": "^4.4.5", "vite": "^4.4.5",
"vue-tsc": "^1.8.5" "vue-tsc": "^1.8.5"

View File

@ -1,4 +1,4 @@
import {Module, Event} from "../model/event.ts"; import {Event} from "../model/event.ts";
export async function fetchRoom(): Promise<string[]> { export async function fetchRoom(): Promise<string[]> {
const rooms: string[] = []; const rooms: string[] = [];
@ -12,19 +12,45 @@ export async function fetchRoom(): Promise<string[]> {
return rooms; return rooms;
} }
export async function fetchEventsByRoomAndWeek( export async function fetchEventsByRoomAndDuration(
room: string, room: string,
week: string, from_date: string,
to_date: string,
): Promise<Event[]> { ): Promise<Event[]> {
const occupations: Event[] = []; const occupations: Event[] = [];
await fetch("/api/schedule/week?room=" + room + "&week=" + week) await fetch("/api/schedule?room=" + room + "&from=" + from_date + "&to=" + to_date)
.then((response) => { .then((response) => {
console.log(response);
return response.json(); return response.json();
}) })
.then((eventsResponse) => { .then((eventsResponse) => {
eventsResponse.forEach((event: Event) => eventsResponse.forEach((event: Event) =>
occupations.push(new Event(event.Days, event.Week, event.Start, event.End, event.Name, event.EventType, event.Prof, event.Rooms, event.Notes, event.BookedAt, event.Course, event.Semester)), occupations.push(new Event(event.Days, event.Week, event.Start, event.End, event.Name, event.EventType, event.Prof, event.Rooms, event.Notes, event.BookedAt, event.Course, event.Semester)),
); );
}); }).catch((error) => {
console.log("Error fetching events: ", error);
return null;
})
return occupations;
}
export async function fetchEventsByRoomAndDay(
room: string,
date: string,
): Promise<Event[]> {
const occupations: Event[] = [];
await fetch("/api/schedule/day?room=" + room + "&sdate=" + date)
.then((response) => {
console.log(response);
return response.json();
})
.then((eventsResponse) => {
eventsResponse.forEach((event: Event) =>
occupations.push(new Event(event.Days, event.Week, event.Start, event.End, event.Name, event.EventType, event.Prof, event.Rooms, event.Notes, event.BookedAt, event.Course, event.Semester)),
);
}).catch((error) => {
console.log("Error fetching events: ", error);
return null;
})
return occupations; return occupations;
} }

View File

@ -1,32 +1,14 @@
<script lang="ts" setup> <script lang="ts" setup>
import {Ref, ref} from "vue"; import {Ref, ref} from "vue";
import {fetchEventsByRoomAndWeek, fetchRoom} from "../api/fetchRoom.ts"; import {fetchRoom} from "../api/fetchRoom.ts";
import InputNumber from 'primevue/inputnumber';
import RoomOccupation from "./RoomOccupation.vue"; import RoomOccupation from "./RoomOccupation.vue";
import {Event} from "../model/event.ts";
const rooms = async () => { const rooms = async () => {
return await fetchRoom(); return await fetchRoom();
}; };
const roomsList: Ref<{ name: string }[]> = ref([]); const roomsList: Ref<{ name: string }[]> = ref([]);
const days: Ref<{ name: string; value: string }[]> = ref([
{name: "Montag", value: "mo"},
{name: "Dienstag", value: "di"},
{name: "Mittwoch", value: "mi"},
{name: "Donnerstag", value: "do"},
{name: "Donnerstag", value: "fr"},
{name: "Donnerstag", value: "sa"},
]);
const selectedDay: Ref<{ name: string; value: string }> = ref(
days.value[0],
);
const selectedWeekNumber: Ref<number> = ref(
1
);
const selectedRoom: Ref<{ name: string }> = ref({name: ""}); const selectedRoom: Ref<{ name: string }> = ref({name: ""});
rooms().then( rooms().then(
@ -35,16 +17,6 @@ rooms().then(
return {name: room}; return {name: room};
})), })),
); );
const occupations: Ref<Event[]> = ref([]);
async function getOccupation() {
occupations.value = await fetchEventsByRoomAndWeek(
selectedRoom.value.name,
selectedWeekNumber.value,
);
}
</script> </script>
<template> <template>
@ -67,18 +39,8 @@ async function getOccupation() {
filter filter
option-label="name" option-label="name"
placeholder="Select a Room" placeholder="Select a Room"
@change="getOccupation()"
/> />
</div> </div>
<div <RoomOccupation :room="selectedRoom.name"/>
class="flex align-items-center justify-content-center border-round m-2"
>
<div class="flex flex-column gap-2">
<InputNumber id="week-number-input" v-model="selectedWeekNumber" inputId="minmax" :min="0" :max="100"
showButtons/>
<small id="username-help">Planning Week</small>
</div>
</div>
<RoomOccupation :occupations="occupations"/>
</div> </div>
</template> </template>

View File

@ -3,230 +3,186 @@ import FullCalendar from '@fullcalendar/vue3'
import dayGridPlugin from '@fullcalendar/daygrid' import dayGridPlugin from '@fullcalendar/daygrid'
import interactionPlugin from '@fullcalendar/interaction' import interactionPlugin from '@fullcalendar/interaction'
import timeGridPlugin from '@fullcalendar/timegrid' import timeGridPlugin from '@fullcalendar/timegrid'
import {computed, PropType, ref, Ref, watch} from "vue"; import {computed, ref, Ref, watch} from "vue";
import {Event} from "../model/event.ts"; import {CalendarOptions, EventInput} from "@fullcalendar/core";
import {Module} from "../model/module.ts";
const props = defineProps({ const props = defineProps({
occupations: { room: {
type: Array as PropType<Event[]>, type: String,
required: true, required: true,
}, },
}); });
type CalenderEvent = {
id: number;
start: string;
end: string;
};
const currentDateFrom: Ref<string> = ref("");
const currentDateTo: Ref<string> = ref("");
const occupations: Ref<CalenderEvent[]> = ref([]);
// array of modules with boolean if selected with getter and setter const selectedRoom = computed(() => props.room);
const events: Ref<{ id: string; start: string, end: string }[]> = ref(
props.occupations?.map((event, index) => {
return {
id: index,
start: event.start.replace(/\s\+\d{4}\s\w+$/, '').replace(' ', 'T'),
end: event.End.replace(/\s\+\d{4}\s\w+$/, '').replace(' ', 'T')
};
}),
);
watch(selectedRoom, (newValue: string) => {
const currentOccupations = computed(() => props.occupations); getOccupation();
console.log("change room: " + newValue);
watch(currentOccupations, (newValue: Event[]) => {
events.value = newValue.map((event, index) => {
return {
id: index,
start: event.Start.replace(/\s\+\d{4}\s\w+$/, '').replace(' ', 'T'),
end: event.End.replace(/\s\+\d{4}\s\w+$/, '').replace(' ', 'T'),
};
});
console.log(events.value);
}); });
const fullCalendar = ref<InstanceType<typeof FullCalendar>>()
async function getOccupation() {
if (selectedRoom.value === "") {
return;
}
console.log("fetching events", selectedRoom.value, currentDateFrom.value, currentDateTo.value);
/*occupations.value = await fetchEventsByRoomAndDuration(
selectedRoom.value,
currentDateFrom.value,
currentDateTo.value
);*/
const calendarOptions = { /*
plugins: [dayGridPlugin, interactionPlugin, timeGridPlugin], const events: Ref<{ id: number; start: string, end: string }[]> = ref(
initialView: 'timeGridFourDay', props.occupations?.map((event, index) => {
dayHeaderFormat: { weekday: 'short', omitCommas: true}, return {
slotDuration: "00:15:00", id: index,
views: { start: event.Start.replace(/\s\+\d{4}\s\w+$/, '').replace(' ', 'T'),
timeGridFourDay: { end: event.End.replace(/\s\+\d{4}\s\w+$/, '').replace(' ', 'T')
type: 'timeGrid', };
slotLabelFormat: { }),
hour: 'numeric', );*/
minute: '2-digit', let calendar = fullCalendar.value?.getApi()
omitZeroMinute: false, calendar?.refetchEvents()
meridiem: false console.log("events: ", calendar?.getEvents())
},
dateAlignment: "week",
titleFormat: { month: 'short', day: 'numeric' },
slotMinTime: "06:00:00",
slotMaxTime: "22:00:00",
duration: {days: 7}, }
firstDay: 1,
allDaySlot: false, function getEvents(){
hiddenDays:[0] const mock = [
} {
"id": 0,
"start": "2023-10-16T09:30:00",
"end": "2023-10-16T11:00:00"
},
{
"id": 1,
"start": "2023-10-16T11:15:00",
"end": "2023-10-16T12:45:00"
},
{
"id": 2,
"start": "2023-10-17T13:45:00",
"end": "2023-10-17T15:15:00"
},
{
"id": 3,
"start": "2023-10-16T07:30:00",
"end": "2023-10-16T9:00:00"
},
{
"id": 4,
"start": "2023-10-20T15:30:00",
"end": "2023-10-20T17:00:00"
},
{
"id": 5,
"start": "2023-10-16T17:15:00",
"end": "2023-10-16T18:45:00"
}]
console.log(currentDateFrom.value, currentDateTo.value)
const events = mock.map((event) => {
const randomInt = Math.floor(Math.random() * 2);
if (randomInt)
return {
id: event.id,
start: currentDateFrom.value + event.start.substring(10, event.start.length),
end: currentDateFrom.value + event.end.substring(10, event.end.length),
};
else
return {
id: event.id,
start: currentDateTo.value + event.start.substring(10, event.start.length),
end: currentDateTo.value + event.end.substring(10, event.end.length),
};
});
console.log("generated events: ", events)
return events;
}
const calendarOptions: CalendarOptions = {
plugins: [dayGridPlugin, interactionPlugin, timeGridPlugin],
initialView: 'week',
dayHeaderFormat: {weekday: 'short', omitCommas: true},
slotDuration: "00:15:00",
views: {
week: {
type: 'timeGrid',
slotLabelFormat: {
hour: 'numeric',
minute: '2-digit',
omitZeroMinute: false,
meridiem: false
}, },
events: [ dateAlignment: "week",
{ titleFormat: {month: 'short', day: 'numeric'},
"id": 0, slotMinTime: "06:00:00",
"start": "2023-10-16T09:30:00", slotMaxTime: "22:00:00",
"end": "2023-10-16T11:00:00"
}, duration: {days: 7},
{ firstDay: 1,
"id": 1, allDaySlot: false,
"start": "2023-10-16T11:15:00", hiddenDays: [0]
"end": "2023-10-16T12:45:00" },
}, Day: {
{ type: 'timeGrid',
"id": 2, slotLabelFormat: {
"start": "2023-10-17T13:45:00", hour: 'numeric',
"end": "2023-10-17T15:15:00" minute: '2-digit',
}, omitZeroMinute: false,
{ meridiem: false
"id": 3, },
"start": "2023-10-16T09:30:00", titleFormat: {month: 'short', day: 'numeric'},
"end": "2023-10-16T11:00:00" slotMinTime: "06:00:00",
}, slotMaxTime: "22:00:00",
{ duration: {days: 1},
"id": 4, allDaySlot: false,
"start": "2023-10-20T09:30:00", hiddenDays: [0]
"end": "2023-10-20T11:00:00" },
}, },
{ headerToolbar: {
"id": 5, end: 'prev,next today',
"start": "2023-10-16T09:30:00", center: 'title',
"end": "2023-10-16T11:00:00" start: 'week,Day'
}, },
{ datesSet: function (dateInfo) {
"id": 6, const view = dateInfo.view;
"start": "2023-10-20T09:30:00", const startDate = view.activeStart;
"end": "2023-10-20T11:00:00" const endDate = view.activeEnd;
}, endDate.setDate(endDate.getDate() - 1);
{ currentDateFrom.value = startDate.getFullYear() + "-" + (startDate.getMonth() + 1) + "-" + startDate.getDate();
"id": 7, currentDateTo.value = endDate.getFullYear() + "-" + (endDate.getMonth() + 1) + "-" + endDate.getDate();
"start": "2023-10-18T13:45:00", getOccupation();
"end": "2023-10-18T17:00:00" },
}, events: function(_info, successCallback, failureCallback) {
{ if (!getEvents()){
"id": 8, failureCallback(new Error("no events"))
"start": "2023-10-18T13:45:00",
"end": "2023-10-18T17:00:00"
},
{
"id": 9,
"start": "2023-10-16T13:45:00",
"end": "2023-10-16T15:15:00"
},
{
"id": 10,
"start": "2023-10-17T11:15:00",
"end": "2023-10-17T12:45:00"
},
{
"id": 11,
"start": "2023-10-16T13:45:00",
"end": "2023-10-16T15:15:00"
},
{
"id": 12,
"start": "2023-10-17T11:15:00",
"end": "2023-10-17T12:45:00"
},
{
"id": 13,
"start": "2023-10-18T09:30:00",
"end": "2023-10-18T12:45:00"
},
{
"id": 14,
"start": "2023-10-16T13:45:00",
"end": "2023-10-16T15:15:00"
},
{
"id": 15,
"start": "2023-10-17T11:15:00",
"end": "2023-10-17T12:45:00"
},
{
"id": 16,
"start": "2023-10-18T09:30:00",
"end": "2023-10-18T12:45:00"
},
{
"id": 17,
"start": "2023-10-16T13:45:00",
"end": "2023-10-16T15:15:00"
},
{
"id": 18,
"start": "2023-10-17T11:15:00",
"end": "2023-10-17T12:45:00"
},
{
"id": 19,
"start": "2023-10-18T09:30:00",
"end": "2023-10-18T12:45:00"
},
{
"id": 20,
"start": "2023-10-16T13:45:00",
"end": "2023-10-16T15:15:00"
},
{
"id": 21,
"start": "2023-10-17T11:15:00",
"end": "2023-10-17T12:45:00"
},
{
"id": 22,
"start": "2023-10-18T09:30:00",
"end": "2023-10-18T12:45:00"
},
{
"id": 23,
"start": "2023-10-17T09:30:00",
"end": "2023-10-17T11:00:00"
},
{
"id": 24,
"start": "2023-10-20T11:15:00",
"end": "2023-10-20T12:45:00"
},
{
"id": 25,
"start": "2023-10-20T11:15:00",
"end": "2023-10-20T12:45:00"
},
{
"id": 26,
"start": "2023-10-16T11:15:00",
"end": "2023-10-16T12:45:00"
},
{
"id": 27,
"start": "2023-10-17T13:45:00",
"end": "2023-10-17T15:15:00"
},
{
"id": 28,
"start": "2023-10-17T15:30:00",
"end": "2023-10-17T17:00:00"
},
{
"id": 29,
"start": "2023-10-19T11:15:00",
"end": "2023-10-19T12:45:00"
},
{
"id": 30,
"start": "2023-10-19T13:45:00",
"end": "2023-10-19T15:15:00"
}
]
} }
successCallback(
getEvents().map((event) => {
return {
id: event.id.toString(),
start: event.start,
end: event.end,
} as EventInput;
})
)
}
}
</script> </script>
<template> <template>
<FullCalendar :options="calendarOptions"/> <FullCalendar ref="fullCalendar" :options="calendarOptions"/>
</template> </template>