[#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:
title: Your API
version: 1.0.0

View File

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

View File

@ -1,11 +1,13 @@
package db
import (
"fmt"
"github.com/pocketbase/dbx"
"github.com/pocketbase/pocketbase"
"htwkalender/model"
"htwkalender/service/functions"
"strings"
"time"
)
func GetRooms(app *pocketbase.PocketBase) []string {
@ -40,7 +42,10 @@ func GetRoomScheduleForDay(app *pocketbase.PocketBase, room string, date string)
var events []model.Event
// 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 {
print("Error while getting events from database: ", err)
return nil
@ -48,11 +53,27 @@ func GetRoomScheduleForDay(app *pocketbase.PocketBase, room string, date string)
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
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
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 {
print("Error while getting events from database: ", err)
return nil

View File

@ -17,7 +17,7 @@ func GetRoomScheduleForDay(c echo.Context, app *pocketbase.PocketBase, room stri
return c.JSON(http.StatusOK, events)
}
func GetRoomScheduleForWeek(c echo.Context, app *pocketbase.PocketBase, room string, week string) error {
events := db.GetRoomScheduleForWeek(app, room, week)
func GetRoomSchedule(c echo.Context, app *pocketbase.PocketBase, room string, from string, to string) error {
events := db.GetRoomSchedule(app, room, from, to)
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-plugin-vue": "^9.17.0",
"prettier": "3.0.2",
"sass": "^1.69.5",
"sass-loader": "^13.3.2",
"typescript": "^5.0.2",
"vite": "^4.4.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[]> {
const rooms: string[] = [];
@ -12,19 +12,45 @@ export async function fetchRoom(): Promise<string[]> {
return rooms;
}
export async function fetchEventsByRoomAndWeek(
export async function fetchEventsByRoomAndDuration(
room: string,
week: string,
from_date: string,
to_date: string,
): Promise<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) => {
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;
}
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;
}

View File

@ -1,32 +1,14 @@
<script lang="ts" setup>
import {Ref, ref} from "vue";
import {fetchEventsByRoomAndWeek, fetchRoom} from "../api/fetchRoom.ts";
import InputNumber from 'primevue/inputnumber';
import {fetchRoom} from "../api/fetchRoom.ts";
import RoomOccupation from "./RoomOccupation.vue";
import {Event} from "../model/event.ts";
const rooms = async () => {
return await fetchRoom();
};
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: ""});
rooms().then(
@ -35,16 +17,6 @@ rooms().then(
return {name: room};
})),
);
const occupations: Ref<Event[]> = ref([]);
async function getOccupation() {
occupations.value = await fetchEventsByRoomAndWeek(
selectedRoom.value.name,
selectedWeekNumber.value,
);
}
</script>
<template>
@ -67,18 +39,8 @@ async function getOccupation() {
filter
option-label="name"
placeholder="Select a Room"
@change="getOccupation()"
/>
</div>
<div
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"/>
<RoomOccupation :room="selectedRoom.name"/>
</div>
</template>

View File

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