From c8bcc3be949dcc88b4d2c927cd6233aa25807009 Mon Sep 17 00:00:00 2001 From: Elmar Kresse Date: Sun, 21 Apr 2024 11:59:20 +0200 Subject: [PATCH 01/95] fix:#25 changed course updater insert and delete procedure --- backend/service/db/dbEvents.go | 32 +++++---- backend/service/events/eventService.go | 69 +++++++++---------- .../fetch/v1/fetchSeminarEventService.go | 61 ++++++++-------- .../fetch/v1/fetchSeminarEventService_test.go | 46 +++++-------- 4 files changed, 100 insertions(+), 108 deletions(-) diff --git a/backend/service/db/dbEvents.go b/backend/service/db/dbEvents.go index 5edf53f..42869f3 100644 --- a/backend/service/db/dbEvents.go +++ b/backend/service/db/dbEvents.go @@ -27,24 +27,22 @@ import ( "github.com/pocketbase/pocketbase" ) -func SaveSeminarGroupEvents(seminarGroups []model.SeminarGroup, app *pocketbase.PocketBase) ([]model.Event, error) { +func SaveSeminarGroupEvents(seminarGroup model.SeminarGroup, app *pocketbase.PocketBase) ([]model.Event, error) { var toBeSavedEvents model.Events var savedRecords model.Events // check if event is already in database and add to toBeSavedEvents if not - for _, seminarGroup := range seminarGroups { - for _, event := range seminarGroup.Events { - event = event.SetCourse(seminarGroup.Course) - existsInDatabase, err := findEventByDayWeekStartEndNameCourse(event, seminarGroup.Course, app) - alreadyAddedToSave := toBeSavedEvents.Contains(event) + for _, event := range seminarGroup.Events { + event = event.SetCourse(seminarGroup.Course) + existsInDatabase, err := findEventByDayWeekStartEndNameCourse(event, seminarGroup.Course, app) + alreadyAddedToSave := toBeSavedEvents.Contains(event) - if err != nil { - return nil, err - } + if err != nil { + return nil, err + } - if !existsInDatabase && !alreadyAddedToSave { - toBeSavedEvents = append(toBeSavedEvents, event) - } + if !existsInDatabase && !alreadyAddedToSave { + toBeSavedEvents = append(toBeSavedEvents, event) } } @@ -366,3 +364,13 @@ func GetEventsThatStartAfterAndEndAfter(app *pocketbase.PocketBase, from types.D return events, nil } + +func DeleteEvents(list model.Events, app *pocketbase.PocketBase) error { + for _, event := range list { + err := app.Dao().Delete(&event) + if err != nil { + return err + } + } + return nil +} diff --git a/backend/service/events/eventService.go b/backend/service/events/eventService.go index 5c84efb..ce6dbbc 100644 --- a/backend/service/events/eventService.go +++ b/backend/service/events/eventService.go @@ -22,6 +22,7 @@ import ( "htwkalender/service/db" "htwkalender/service/fetch/v1" "htwkalender/service/functions" + "log/slog" ) func GetModulesForCourseDistinct(app *pocketbase.PocketBase, course string, semester string) (model.Events, error) { @@ -120,15 +121,11 @@ func DeleteAllEvents(app *pocketbase.PocketBase) error { // If the update was not successful, an error is returned func UpdateModulesForCourse(app *pocketbase.PocketBase, course string) (model.Events, error) { - //new string array with one element (course) - var courses []string - courses = append(courses, course) + seminarGroup := v1.GetSeminarGroupEventsFromHTML(course) - seminarGroups := v1.GetSeminarGroupsEventsFromHTML(courses) + seminarGroup = v1.ClearEmptySeminarGroups(seminarGroup) - seminarGroups = v1.ClearEmptySeminarGroups(seminarGroups) - - seminarGroups = v1.ReplaceEmptyEventNames(seminarGroups) + seminarGroup = v1.ReplaceEmptyEventNames(seminarGroup) //check if events in the seminarGroups Events are already in the database //if yes, keep the database as it is @@ -151,43 +148,45 @@ func UpdateModulesForCourse(app *pocketbase.PocketBase, course string) (model.Ev //if there are no events in the database, save the new events if len(events) == 0 { - events, dbError := db.SaveSeminarGroupEvents(seminarGroups, app) + events, dbError := db.SaveSeminarGroupEvents(seminarGroup, app) if dbError != nil { return nil, dbError } return events, nil } - //check if events in the seminarGroups Events are already in the database - //if yes, keep the database as it is - //if no, delete all events for the course and the semester and save the new events + // Create partial update list and delete list for the events + var insertList model.Events + var deleteList model.Events - var savedEvents model.Events - - for _, seminarGroup := range seminarGroups { - for _, event := range seminarGroup.Events { - // if the event is not in the database, delete all events for the course and the semester and save the new events - if !ContainsEvent(events, event) { - - err = DeleteAllEventsByCourseAndSemester(app, course, "ws") - if err != nil { - return nil, err - } - - err = DeleteAllEventsByCourseAndSemester(app, course, "ss") - if err != nil { - return nil, err - } - - //save the new events - savedEvent, dbError := db.SaveSeminarGroupEvents(seminarGroups, app) - if dbError != nil { - return nil, dbError - } - savedEvents = append(savedEvents, savedEvent...) - } + // check which events are not already in the database and need to be inserted/saved + for _, event := range seminarGroup.Events { + if !ContainsEvent(events, event) { + insertList = append(insertList, event) } } + + // save all events that are in the insertList + savedEvents, err := db.SaveEvents(insertList, app) + if err != nil { + slog.Error("Failed to save events: %v", err) + return nil, err + } + + // check which events are in the database but not in the seminarGroup and need to be deleted + for _, event := range events { + if !ContainsEvent(seminarGroup.Events, event) { + deleteList = append(deleteList, event) + } + } + + // delete all events that are in the deleteList + err = db.DeleteEvents(deleteList, app) + if err != nil { + slog.Error("Failed to delete events: %v", err) + return nil, err + } + return savedEvents, nil } diff --git a/backend/service/fetch/v1/fetchSeminarEventService.go b/backend/service/fetch/v1/fetchSeminarEventService.go index c592340..c6ac6e4 100644 --- a/backend/service/fetch/v1/fetchSeminarEventService.go +++ b/backend/service/fetch/v1/fetchSeminarEventService.go @@ -32,50 +32,43 @@ import ( "time" ) -func ReplaceEmptyEventNames(groups []model.SeminarGroup) []model.SeminarGroup { - for i, group := range groups { - for j, event := range group.Events { - if functions.OnlyWhitespace(event.Name) { - groups[i].Events[j].Name = "Sonderveranstaltungen" - } +func ReplaceEmptyEventNames(group model.SeminarGroup) model.SeminarGroup { + for j, event := range group.Events { + if functions.OnlyWhitespace(event.Name) { + group.Events[j].Name = "Sonderveranstaltungen" } } - return groups + return group } -func ClearEmptySeminarGroups(seminarGroups []model.SeminarGroup) []model.SeminarGroup { - var newSeminarGroups []model.SeminarGroup - for _, seminarGroup := range seminarGroups { - if len(seminarGroup.Events) > 0 && seminarGroup.Course != "" { - newSeminarGroups = append(newSeminarGroups, seminarGroup) - } +func ClearEmptySeminarGroups(seminarGroup model.SeminarGroup) model.SeminarGroup { + var newSeminarGroup = model.SeminarGroup{} + + if len(seminarGroup.Events) > 0 && seminarGroup.Course != "" { + newSeminarGroup = seminarGroup } - return newSeminarGroups + return newSeminarGroup } -func GetSeminarGroupsEventsFromHTML(seminarGroupsLabel []string) []model.SeminarGroup { - var seminarGroups []model.SeminarGroup - for _, seminarGroupLabel := range seminarGroupsLabel { +func GetSeminarGroupEventsFromHTML(seminarGroupLabel string) model.SeminarGroup { + var seminarGroup model.SeminarGroup - if (time.Now().Month() >= 3) && (time.Now().Month() <= 10) { - ssUrl := "https://stundenplan.htwk-leipzig.de/" + string("ss") + "/Berichte/Text-Listen;Studenten-Sets;name;" + seminarGroupLabel + "?template=sws_semgrp&weeks=1-65" - result, getError := fetch.GetHTML(ssUrl) - if getError == nil { - seminarGroup := parseSeminarGroup(result) - seminarGroups = append(seminarGroups, seminarGroup) - } - } - - if (time.Now().Month() >= 9) || (time.Now().Month() <= 4) { - wsUrl := "https://stundenplan.htwk-leipzig.de/" + string("ws") + "/Berichte/Text-Listen;Studenten-Sets;name;" + seminarGroupLabel + "?template=sws_semgrp&weeks=1-65" - result, getError := fetch.GetHTML(wsUrl) - if getError == nil { - seminarGroup := parseSeminarGroup(result) - seminarGroups = append(seminarGroups, seminarGroup) - } + if (time.Now().Month() >= 3) && (time.Now().Month() <= 10) { + ssUrl := "https://stundenplan.htwk-leipzig.de/" + string("ss") + "/Berichte/Text-Listen;Studenten-Sets;name;" + seminarGroupLabel + "?template=sws_semgrp&weeks=1-65" + result, getError := fetch.GetHTML(ssUrl) + if getError == nil { + seminarGroup = parseSeminarGroup(result) } } - return seminarGroups + + if (time.Now().Month() >= 9) || (time.Now().Month() <= 4) { + wsUrl := "https://stundenplan.htwk-leipzig.de/" + string("ws") + "/Berichte/Text-Listen;Studenten-Sets;name;" + seminarGroupLabel + "?template=sws_semgrp&weeks=1-65" + result, getError := fetch.GetHTML(wsUrl) + if getError == nil { + seminarGroup = parseSeminarGroup(result) + } + } + return seminarGroup } func SplitEventType(events []model.Event) ([]model.Event, error) { diff --git a/backend/service/fetch/v1/fetchSeminarEventService_test.go b/backend/service/fetch/v1/fetchSeminarEventService_test.go index 614660a..a5c103b 100644 --- a/backend/service/fetch/v1/fetchSeminarEventService_test.go +++ b/backend/service/fetch/v1/fetchSeminarEventService_test.go @@ -75,28 +75,17 @@ func Test_extractSemesterAndYear(t *testing.T) { func Test_replaceEmptyEventNames(t *testing.T) { type args struct { - groups []model.SeminarGroup + group model.SeminarGroup } tests := []struct { name string args args - want []model.SeminarGroup + want model.SeminarGroup }{ { name: "Test 1", args: args{ - groups: []model.SeminarGroup{ - { - Events: []model.Event{ - { - Name: "Test", - }, - }, - }, - }, - }, - want: []model.SeminarGroup{ - { + group: model.SeminarGroup{ Events: []model.Event{ { Name: "Test", @@ -104,26 +93,29 @@ func Test_replaceEmptyEventNames(t *testing.T) { }, }, }, + want: model.SeminarGroup{ + Events: []model.Event{ + { + Name: "Test", + }, + }, + }, }, { name: "Test 1", args: args{ - groups: []model.SeminarGroup{ - { - Events: []model.Event{ - { - Name: "", - }, + group: model.SeminarGroup{ + Events: []model.Event{ + { + Name: "", }, }, }, }, - want: []model.SeminarGroup{ - { - Events: []model.Event{ - { - Name: "Sonderveranstaltungen", - }, + want: model.SeminarGroup{ + Events: []model.Event{ + { + Name: "Sonderveranstaltungen", }, }, }, @@ -131,7 +123,7 @@ func Test_replaceEmptyEventNames(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - if got := ReplaceEmptyEventNames(tt.args.groups); !reflect.DeepEqual(got, tt.want) { + if got := ReplaceEmptyEventNames(tt.args.group); !reflect.DeepEqual(got, tt.want) { t.Errorf("ReplaceEmptyEventNames() = %v, want %v", got, tt.want) } }) From 02a43605212a9b0f40af6e2118483bd15de7fef2 Mon Sep 17 00:00:00 2001 From: Elmar Kresse Date: Mon, 22 Apr 2024 16:11:13 +0200 Subject: [PATCH 02/95] fix:#25 added tests and schedule updates --- backend/model/eventModel.go | 2 +- backend/model/eventModel_test.go | 261 ++++++++++++++++++ backend/model/feedModel_test.go | 45 +++ backend/model/moduleModel_test.go | 126 +++++++++ backend/service/addRoute.go | 26 -- backend/service/addSchedule.go | 22 +- backend/service/events/eventService_test.go | 58 ++++ backend/service/fetch/sport/sportFetcher.go | 3 +- .../fetch/v1/fetchSeminarGroupService.go | 26 +- backend/service/fetch/v2/fetcher.go | 23 +- backend/service/fetch/v2/fetcher_test.go | 48 ---- backend/service/functions/semester.go | 25 +- backend/service/functions/semester_test.go | 91 ++++++ backend/service/functions/string.go | 7 - backend/service/functions/string_test.go | 47 ++++ backend/service/ical/icalJsonMigrate.go | 74 ----- backend/service/room/roomService.go | 2 + backend/service/room/roomService_test.go | 44 +++ docker-compose.yml | 4 +- 19 files changed, 731 insertions(+), 203 deletions(-) create mode 100644 backend/model/feedModel_test.go create mode 100644 backend/model/moduleModel_test.go create mode 100644 backend/service/events/eventService_test.go create mode 100644 backend/service/functions/semester_test.go delete mode 100644 backend/service/ical/icalJsonMigrate.go diff --git a/backend/model/eventModel.go b/backend/model/eventModel.go index 142fa0b..8a492cf 100644 --- a/backend/model/eventModel.go +++ b/backend/model/eventModel.go @@ -79,7 +79,7 @@ func (e *Event) SetCourse(course string) Event { return *e } -// Creates an AnonymizedEventDTO from an Event hiding all sensitive data +// AnonymizeEvent Creates an AnonymizedEventDTO from an Event hiding all sensitive data func (e *Event) AnonymizeEvent() AnonymizedEventDTO { return AnonymizedEventDTO{ Day: e.Day, diff --git a/backend/model/eventModel_test.go b/backend/model/eventModel_test.go index 95d4954..c153f23 100644 --- a/backend/model/eventModel_test.go +++ b/backend/model/eventModel_test.go @@ -217,3 +217,264 @@ func TestEvent_AnonymizeEvent(t *testing.T) { }) } } + +func TestEvent_GetName(t *testing.T) { + type fields struct { + UUID string + Day string + Week string + Start types.DateTime + End types.DateTime + Name string + EventType string + Compulsory string + Prof string + Rooms string + Notes string + BookedAt string + Course string + Semester string + BaseModel models.BaseModel + } + tests := []struct { + name string + fields fields + want string + }{ + { + name: "empty event", + fields: fields{}, + want: "", + }, + { + name: "one event", + fields: fields{Name: "Event"}, + want: "Event", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + e := &Event{ + UUID: tt.fields.UUID, + Day: tt.fields.Day, + Week: tt.fields.Week, + Start: tt.fields.Start, + End: tt.fields.End, + Name: tt.fields.Name, + EventType: tt.fields.EventType, + Compulsory: tt.fields.Compulsory, + Prof: tt.fields.Prof, + Rooms: tt.fields.Rooms, + Notes: tt.fields.Notes, + BookedAt: tt.fields.BookedAt, + Course: tt.fields.Course, + Semester: tt.fields.Semester, + BaseModel: tt.fields.BaseModel, + } + if got := e.GetName(); got != tt.want { + t.Errorf("GetName() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestEvent_SetCourse(t *testing.T) { + type fields struct { + UUID string + Day string + Week string + Start types.DateTime + End types.DateTime + Name string + EventType string + Compulsory string + Prof string + Rooms string + Notes string + BookedAt string + Course string + Semester string + BaseModel models.BaseModel + } + type args struct { + course string + } + tests := []struct { + name string + fields fields + args args + want Event + }{ + { + name: "set course", + fields: fields{}, + args: args{course: "test"}, + want: Event{Course: "test"}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + e := &Event{ + UUID: tt.fields.UUID, + Day: tt.fields.Day, + Week: tt.fields.Week, + Start: tt.fields.Start, + End: tt.fields.End, + Name: tt.fields.Name, + EventType: tt.fields.EventType, + Compulsory: tt.fields.Compulsory, + Prof: tt.fields.Prof, + Rooms: tt.fields.Rooms, + Notes: tt.fields.Notes, + BookedAt: tt.fields.BookedAt, + Course: tt.fields.Course, + Semester: tt.fields.Semester, + BaseModel: tt.fields.BaseModel, + } + if got := e.SetCourse(tt.args.course); !reflect.DeepEqual(got, tt.want) { + t.Errorf("SetCourse() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestEvent_SetName(t *testing.T) { + type fields struct { + UUID string + Day string + Week string + Start types.DateTime + End types.DateTime + Name string + EventType string + Compulsory string + Prof string + Rooms string + Notes string + BookedAt string + Course string + Semester string + BaseModel models.BaseModel + } + type args struct { + name string + } + tests := []struct { + name string + fields fields + args args + }{ + { + name: "set name", + fields: fields{ + Name: "name", + }, + args: args{ + name: "name", + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + e := &Event{ + UUID: tt.fields.UUID, + Day: tt.fields.Day, + Week: tt.fields.Week, + Start: tt.fields.Start, + End: tt.fields.End, + Name: tt.fields.Name, + EventType: tt.fields.EventType, + Compulsory: tt.fields.Compulsory, + Prof: tt.fields.Prof, + Rooms: tt.fields.Rooms, + Notes: tt.fields.Notes, + BookedAt: tt.fields.BookedAt, + Course: tt.fields.Course, + Semester: tt.fields.Semester, + BaseModel: tt.fields.BaseModel, + } + e.SetName(tt.args.name) + }) + } +} + +func TestEvent_TableName(t *testing.T) { + type fields struct { + UUID string + Day string + Week string + Start types.DateTime + End types.DateTime + Name string + EventType string + Compulsory string + Prof string + Rooms string + Notes string + BookedAt string + Course string + Semester string + BaseModel models.BaseModel + } + tests := []struct { + name string + fields fields + want string + }{ + { + name: "table name", + fields: fields{}, + want: "events", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + e := &Event{ + UUID: tt.fields.UUID, + Day: tt.fields.Day, + Week: tt.fields.Week, + Start: tt.fields.Start, + End: tt.fields.End, + Name: tt.fields.Name, + EventType: tt.fields.EventType, + Compulsory: tt.fields.Compulsory, + Prof: tt.fields.Prof, + Rooms: tt.fields.Rooms, + Notes: tt.fields.Notes, + BookedAt: tt.fields.BookedAt, + Course: tt.fields.Course, + Semester: tt.fields.Semester, + BaseModel: tt.fields.BaseModel, + } + if got := e.TableName(); got != tt.want { + t.Errorf("TableName() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestEvents_Contains1(t *testing.T) { + type args struct { + event Event + } + tests := []struct { + name string + m Events + args args + want bool + }{ + { + name: "empty events", + m: Events{}, + args: args{event: Event{}}, + want: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := tt.m.Contains(tt.args.event); got != tt.want { + t.Errorf("Contains() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/backend/model/feedModel_test.go b/backend/model/feedModel_test.go new file mode 100644 index 0000000..111d8d1 --- /dev/null +++ b/backend/model/feedModel_test.go @@ -0,0 +1,45 @@ +package model + +import ( + "github.com/pocketbase/pocketbase/models" + "github.com/pocketbase/pocketbase/tools/types" + "testing" +) + +func TestFeed_SetModules(t *testing.T) { + type fields struct { + Modules string + Retrieved types.DateTime + BaseModel models.BaseModel + } + type args struct { + modules string + } + tests := []struct { + name string + fields fields + args args + }{ + { + name: "set modules", + fields: fields{ + Modules: "", + Retrieved: types.DateTime{}, + BaseModel: models.BaseModel{}, + }, + args: args{ + modules: "modules", + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + f := &Feed{ + Modules: tt.fields.Modules, + Retrieved: tt.fields.Retrieved, + BaseModel: tt.fields.BaseModel, + } + f.SetModules(tt.args.modules) + }) + } +} diff --git a/backend/model/moduleModel_test.go b/backend/model/moduleModel_test.go new file mode 100644 index 0000000..b2ebe5d --- /dev/null +++ b/backend/model/moduleModel_test.go @@ -0,0 +1,126 @@ +package model + +import "testing" + +func TestModuleDTO_GetName(t *testing.T) { + type fields struct { + UUID string + Name string + Prof string + Course string + Semester string + EventType string + } + tests := []struct { + name string + fields fields + want string + }{ + { + name: "get name", + fields: fields{ + Name: "name", + }, + want: "name", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + m := &ModuleDTO{ + UUID: tt.fields.UUID, + Name: tt.fields.Name, + Prof: tt.fields.Prof, + Course: tt.fields.Course, + Semester: tt.fields.Semester, + EventType: tt.fields.EventType, + } + if got := m.GetName(); got != tt.want { + t.Errorf("GetName() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestModuleDTO_SetName(t *testing.T) { + type fields struct { + UUID string + Name string + Prof string + Course string + Semester string + EventType string + } + type args struct { + name string + } + tests := []struct { + name string + fields fields + args args + }{ + { + name: "set name", + fields: fields{ + Name: "name", + }, + args: args{ + name: "name", + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + m := &ModuleDTO{ + UUID: tt.fields.UUID, + Name: tt.fields.Name, + Prof: tt.fields.Prof, + Course: tt.fields.Course, + Semester: tt.fields.Semester, + EventType: tt.fields.EventType, + } + m.SetName(tt.args.name) + }) + } +} + +func TestModule_SetName(t *testing.T) { + type fields struct { + UUID string + Name string + Prof string + Course string + Semester string + Events Events + } + type args struct { + name string + } + tests := []struct { + name string + fields fields + args args + }{ + { + name: "set name", + fields: fields{ + Name: "name", + }, + args: args{ + name: "name", + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + m := &Module{ + UUID: tt.fields.UUID, + Name: tt.fields.Name, + Prof: tt.fields.Prof, + Course: tt.fields.Course, + Semester: tt.fields.Semester, + Events: tt.fields.Events, + } + m.SetName(tt.args.name) + }) + } +} diff --git a/backend/service/addRoute.go b/backend/service/addRoute.go index 9938aca..24991c3 100644 --- a/backend/service/addRoute.go +++ b/backend/service/addRoute.go @@ -22,7 +22,6 @@ import ( v1 "htwkalender/service/fetch/v1" v2 "htwkalender/service/fetch/v2" "htwkalender/service/functions/time" - "htwkalender/service/ical" "htwkalender/service/room" "log/slog" "net/http" @@ -380,29 +379,4 @@ func AddRoutes(app *pocketbase.PocketBase) { } return nil }) - - app.OnBeforeServe().Add(func(e *core.ServeEvent) error { - _, err := e.Router.AddRoute(echo.Route{ - Method: http.MethodGet, - Path: "/api/feeds/migrate", - Handler: func(c echo.Context) error { - err := ical.MigrateFeedJson(app) - - if err != nil { - slog.Error("Failed to migrate feeds: %v", err) - return c.JSON(http.StatusInternalServerError, "Failed to migrate feeds") - } else { - return c.JSON(http.StatusOK, "Migrated") - } - }, - Middlewares: []echo.MiddlewareFunc{ - apis.ActivityLogger(app), - apis.RequireAdminAuth(), - }, - }) - if err != nil { - return err - } - return nil - }) } diff --git a/backend/service/addSchedule.go b/backend/service/addSchedule.go index 444429e..3c38153 100644 --- a/backend/service/addSchedule.go +++ b/backend/service/addSchedule.go @@ -23,6 +23,7 @@ import ( "htwkalender/service/course" "htwkalender/service/feed" "htwkalender/service/fetch/sport" + v1 "htwkalender/service/fetch/v1" v2 "htwkalender/service/fetch/v2" "htwkalender/service/functions/time" "log/slog" @@ -34,10 +35,21 @@ func AddSchedules(app *pocketbase.PocketBase) { app.OnBeforeServe().Add(func(e *core.ServeEvent) error { scheduler := cron.New() - // Every hour update all courses (5 segments - minute, hour, day, month, weekday) "0 * * * *" - // Every three hours update all courses (5 segments - minute, hour, day, month, weekday) "0 */3 * * *" - // Every 10 minutes update all courses (5 segments - minute, hour, day, month, weekday) "*/10 * * * *" - scheduler.MustAdd("updateCourse", "0 */3 * * *", func() { + // !! IMPORTANT !! CRON is based on UTC time zone so in Germany it is UTC+2 in summer and UTC+1 in winter + + // Every sunday at 10pm update all courses (5 segments - minute, hour, day, month, weekday) "0 22 * * 0" + scheduler.MustAdd("updateCourses", "0 22 * * 0", func() { + slog.Info("Started updating courses schedule") + groups, err := v1.FetchSeminarGroups(app) + if err != nil { + slog.Warn("Failed to fetch seminar groups: %v", err) + } + slog.Info("Successfully fetched " + strconv.FormatInt(int64(len(groups)), 10) + " seminar groups") + }) + + // Every day at 5am and 5pm update all courses (5 segments - minute, hour, day, month, weekday) "0 5,17 * * *" + // In Germany it is 7am and 7pm, syllabus gets updated twice a day at German 5:00 Uhr and 17:00 Uhr + scheduler.MustAdd("updateEventsByCourse", "0 5,17 * * *", func() { slog.Info("Started updating courses schedule") course.UpdateCourse(app) }) @@ -60,7 +72,7 @@ func AddSchedules(app *pocketbase.PocketBase) { }) //fetch all events for semester and delete from remote this should be done every sunday at 2am - scheduler.MustAdd("fetchEvents", "0 2 * * 0", func() { + scheduler.MustAdd("fetchEvents", "0 22 * * 6", func() { savedEvents, err := v2.FetchAllEventsAndSave(app, time.RealClock{}) if err != nil { slog.Error("Failed to fetch and save events: %v", err) diff --git a/backend/service/events/eventService_test.go b/backend/service/events/eventService_test.go new file mode 100644 index 0000000..6c5c23e --- /dev/null +++ b/backend/service/events/eventService_test.go @@ -0,0 +1,58 @@ +package events + +import ( + "htwkalender/model" + "testing" +) + +func TestContainsEvent(t *testing.T) { + type args struct { + events model.Events + event model.Event + } + tests := []struct { + name string + args args + want bool + }{ + { + name: "contains event", + args: args{ + events: model.Events{ + { + UUID: "934807509832475", + Name: "name", + }, + }, + event: model.Event{ + UUID: "934807509832475", + Name: "name", + }, + }, + want: true, + }, + { + name: "contains no event", + args: args{ + events: model.Events{ + { + UUID: "9991929292921912343534", + Name: "Name1", + }, + }, + event: model.Event{ + UUID: "1111112312312312", + Name: "Name2", + }, + }, + want: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := ContainsEvent(tt.args.events, tt.args.event); got != tt.want { + t.Errorf("ContainsEvent() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/backend/service/fetch/sport/sportFetcher.go b/backend/service/fetch/sport/sportFetcher.go index f17f41c..583815c 100644 --- a/backend/service/fetch/sport/sportFetcher.go +++ b/backend/service/fetch/sport/sportFetcher.go @@ -24,6 +24,7 @@ import ( "htwkalender/model" "htwkalender/service/db" "htwkalender/service/functions" + clock "htwkalender/service/functions/time" "io" "log/slog" "net/http" @@ -81,7 +82,7 @@ func FetchAndUpdateSportEvents(app *pocketbase.PocketBase) ([]model.Event, error } // @TODO: delete and save events in one transaction and it only should delete events that are not in the new events list and save events that are not in the database - err = db.DeleteAllEventsByCourse(app, "Sport", functions.GetCurrentSemesterString()) + err = db.DeleteAllEventsByCourse(app, "Sport", functions.GetCurrentSemesterString(clock.RealClock{})) if err != nil { return nil, err } diff --git a/backend/service/fetch/v1/fetchSeminarGroupService.go b/backend/service/fetch/v1/fetchSeminarGroupService.go index 45abf67..7809ec9 100644 --- a/backend/service/fetch/v1/fetchSeminarGroupService.go +++ b/backend/service/fetch/v1/fetchSeminarGroupService.go @@ -23,6 +23,8 @@ import ( "github.com/pocketbase/pocketbase/models" "htwkalender/model" "htwkalender/service/db" + "htwkalender/service/functions" + "htwkalender/service/functions/time" "io" "log/slog" "net/http" @@ -59,23 +61,19 @@ func getSeminarHTML(semester string) (string, error) { func FetchSeminarGroups(app *pocketbase.PocketBase) ([]*models.Record, error) { var groups []model.SeminarGroup - resultSummer, err := getSeminarHTML("ss") + semesterString := functions.CalculateSemesterList(time.RealClock{}) + var results [2]string + var err error - if err != nil { - slog.Error("Error while fetching seminar groups for winter semester", err) - return nil, err + for i, semester := range semesterString { + results[i], err = getSeminarHTML(semester) + if err != nil { + slog.Error("Error while fetching seminar groups for: "+semester, err) + return nil, err + } + groups = append(groups, parseSeminarGroups(results[i], semester)...) } - resultWinter, _ := getSeminarHTML("ws") - - if err != nil { - slog.Error("Error while fetching seminar groups for summer semester", err) - return nil, err - } - - groups = parseSeminarGroups(resultSummer, "ss") - groups = append(groups, parseSeminarGroups(resultWinter, "ws")...) - // filter duplicates groups = removeDuplicates(groups) diff --git a/backend/service/fetch/v2/fetcher.go b/backend/service/fetch/v2/fetcher.go index 57a9aef..34dd2ad 100644 --- a/backend/service/fetch/v2/fetcher.go +++ b/backend/service/fetch/v2/fetcher.go @@ -25,10 +25,10 @@ import ( "htwkalender/service/db" "htwkalender/service/fetch" v1 "htwkalender/service/fetch/v1" + "htwkalender/service/functions" localTime "htwkalender/service/functions/time" "log/slog" "strings" - "time" ) func ParseEventsFromRemote(app *pocketbase.PocketBase) (model.Events, error) { @@ -70,7 +70,7 @@ func FetchAllEventsAndSave(app *pocketbase.PocketBase, clock localTime.Clock) ([ } // Fetch and save events for all semesters - for _, semester := range calculateSemesterList(clock) { + for _, semester := range functions.CalculateSemesterList(clock) { events, fetchErr := fetchAndSaveAllEventsForSemester(app, semester, stubUrl) if fetchErr != nil { return nil, fmt.Errorf("failed to fetch and save events for "+semester+": %w", err) @@ -104,25 +104,6 @@ func fetchAndSaveAllEventsForSemester( return savedRecords, err } -func calculateSemesterList(clock localTime.Clock) []string { - summerSemester := clock.Now().Month() >= time.March && clock.Now().Month() <= time.September - winterSemester := clock.Now().Month() <= time.March || clock.Now().Month() >= time.September - - if summerSemester && winterSemester { - return []string{"ss", "ws"} - } - - if summerSemester { - return []string{"ss"} - } - - if winterSemester { - return []string{"ws"} - } - - return []string{"ss", "ws"} -} - func parseEventForOneSemester(url string) ([]model.Event, error) { // Fetch Webpage from URL webpage, err := fetch.GetHTML(url) diff --git a/backend/service/fetch/v2/fetcher_test.go b/backend/service/fetch/v2/fetcher_test.go index 2223549..9b2dec1 100644 --- a/backend/service/fetch/v2/fetcher_test.go +++ b/backend/service/fetch/v2/fetcher_test.go @@ -18,10 +18,8 @@ package v2 import ( "htwkalender/model" - mockTime "htwkalender/service/functions/time" "reflect" "testing" - "time" ) func Test_switchNameAndNotesForExam(t *testing.T) { @@ -99,49 +97,3 @@ func Test_switchNameAndNotesForExam(t *testing.T) { }) } } - -func Test_calculateSemesterList(t *testing.T) { - type args struct { - clock mockTime.Clock - } - tests := []struct { - name string - args args - want []string - }{ - { - name: "is summer semester", - args: args{ - clock: mockTime.MockClock{ - NowTime: time.Date(2024, 6, 1, 0, 0, 0, 0, time.UTC), - }, - }, - want: []string{"ss"}, - }, - { - name: "is winter semester", - args: args{ - clock: mockTime.MockClock{ - NowTime: time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC), - }, - }, - want: []string{"ws"}, - }, - { - name: "is in both", - args: args{ - clock: mockTime.MockClock{ - NowTime: time.Date(2024, 3, 22, 0, 0, 0, 0, time.UTC), - }, - }, - want: []string{"ss", "ws"}, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if got := calculateSemesterList(tt.args.clock); !reflect.DeepEqual(got, tt.want) { - t.Errorf("calculateSemesterList() = %v, want %v", got, tt.want) - } - }) - } -} diff --git a/backend/service/functions/semester.go b/backend/service/functions/semester.go index b2f3b6a..2ac4ea6 100644 --- a/backend/service/functions/semester.go +++ b/backend/service/functions/semester.go @@ -16,15 +16,32 @@ package functions -import "time" +import ( + localTime "htwkalender/service/functions/time" + "time" +) // 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 { +func GetCurrentSemesterString(localeTime localTime.Clock) string { + if localeTime.Now().Month() >= 10 || localeTime.Now().Month() <= 3 { return "ws" } else { return "ss" } } + +func CalculateSemesterList(clock localTime.Clock) []string { + summerSemester := clock.Now().Month() >= time.March && clock.Now().Month() <= time.September + winterSemester := clock.Now().Month() <= time.March || clock.Now().Month() >= time.September + + if summerSemester && !winterSemester { + return []string{"ss"} + } + + if !summerSemester && winterSemester { + return []string{"ws"} + } + + return []string{"ss", "ws"} +} diff --git a/backend/service/functions/semester_test.go b/backend/service/functions/semester_test.go new file mode 100644 index 0000000..eb71898 --- /dev/null +++ b/backend/service/functions/semester_test.go @@ -0,0 +1,91 @@ +package functions + +import ( + mockTime "htwkalender/service/functions/time" + "reflect" + "testing" + "time" +) + +func Test_calculateSemesterList(t *testing.T) { + type args struct { + clock mockTime.Clock + } + tests := []struct { + name string + args args + want []string + }{ + { + name: "is summer semester", + args: args{ + clock: mockTime.MockClock{ + NowTime: time.Date(2024, 6, 1, 0, 0, 0, 0, time.UTC), + }, + }, + want: []string{"ss"}, + }, + { + name: "is winter semester", + args: args{ + clock: mockTime.MockClock{ + NowTime: time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC), + }, + }, + want: []string{"ws"}, + }, + { + name: "is in both", + args: args{ + clock: mockTime.MockClock{ + NowTime: time.Date(2024, 3, 22, 0, 0, 0, 0, time.UTC), + }, + }, + want: []string{"ss", "ws"}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := CalculateSemesterList(tt.args.clock); !reflect.DeepEqual(got, tt.want) { + t.Errorf("calculateSemesterList() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestGetCurrentSemesterString(t *testing.T) { + type args struct { + localeTime mockTime.Clock + } + tests := []struct { + name string + args args + want string + }{ + { + name: "is winter semester", + args: args{ + localeTime: mockTime.MockClock{ + NowTime: time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC), + }, + }, + want: "ws", + }, + { + name: "is summer semester", + args: args{ + localeTime: mockTime.MockClock{ + NowTime: time.Date(2024, 6, 1, 0, 0, 0, 0, time.UTC), + }, + }, + want: "ss", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := GetCurrentSemesterString(tt.args.localeTime); got != tt.want { + t.Errorf("GetCurrentSemesterString() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/backend/service/functions/string.go b/backend/service/functions/string.go index a0a74c0..77984a4 100644 --- a/backend/service/functions/string.go +++ b/backend/service/functions/string.go @@ -48,13 +48,6 @@ func Contains(s []string, e string) bool { return false } -func ReplaceEmptyString(word string, replacement string) string { - if OnlyWhitespace(word) { - return replacement - } - return word -} - func HashString(s string) string { hash := sha256.New() hash.Write([]byte(s)) diff --git a/backend/service/functions/string_test.go b/backend/service/functions/string_test.go index 7075f0a..ee8316a 100644 --- a/backend/service/functions/string_test.go +++ b/backend/service/functions/string_test.go @@ -17,6 +17,7 @@ package functions import ( + "reflect" "testing" ) @@ -96,3 +97,49 @@ func TestIsSeparator(t *testing.T) { }) } } + +func TestContains(t *testing.T) { + type args struct { + s []string + e string + } + tests := []struct { + name string + args args + want bool + }{ + {"empty slice", args{[]string{}, "a"}, false}, + {"slice with one element equal", args{[]string{"a"}, "a"}, true}, + {"slice with one element different", args{[]string{"a"}, "b"}, false}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := Contains(tt.args.s, tt.args.e); got != tt.want { + t.Errorf("Contains() = %v, want %v", got, tt.want) + } + }) + } +} + +func TestSeperateRoomString(t *testing.T) { + type args struct { + rooms string + } + tests := []struct { + name string + args args + want []string + }{ + {"empty string", args{""}, []string{}}, + {"one room", args{"a"}, []string{"a"}}, + {"two rooms", args{"a,b"}, []string{"a", "b"}}, + {"two rooms with whitespace", args{"a, b"}, []string{"a", "b"}}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := SeperateRoomString(tt.args.rooms); !reflect.DeepEqual(got, tt.want) { + t.Errorf("SeperateRoomString() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/backend/service/ical/icalJsonMigrate.go b/backend/service/ical/icalJsonMigrate.go deleted file mode 100644 index 328f6ae..0000000 --- a/backend/service/ical/icalJsonMigrate.go +++ /dev/null @@ -1,74 +0,0 @@ -//Calendar implementation for the HTWK Leipzig timetable. Evaluation and display of the individual dates in iCal format. -//Copyright (C) 2024 HTWKalender support@htwkalender.de - -//This program is free software: you can redistribute it and/or modify -//it under the terms of the GNU Affero General Public License as published by -//the Free Software Foundation, either version 3 of the License, or -//(at your option) any later version. - -//This program is distributed in the hope that it will be useful, -//but WITHOUT ANY WARRANTY; without even the implied warranty of -//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -//GNU Affero General Public License for more details. - -//You should have received a copy of the GNU Affero General Public License -//along with this program. If not, see . - -package ical - -import ( - "encoding/json" - "github.com/pocketbase/dbx" - "github.com/pocketbase/pocketbase" - "htwkalender/model" -) - -//update ical feed json -//add uuid field -//remove module name field - -func MigrateFeedJson(app *pocketbase.PocketBase) error { - - records, err := app.Dao().FindRecordsByFilter("feeds", "1=1", "-created", 0, 0) - if err != nil { - return err - } - - for _, feed := range records { - - var modules []model.FeedCollection - - err := json.Unmarshal([]byte(feed.GetString("modules")), &modules) - if err != nil { - return err - } - - var uuidFeedCollections []model.FeedCollection - - for _, module := range modules { - uuid := searchUUIDForModule(app, module) - - if uuid != "" { - uuidFeedCollections = append(uuidFeedCollections, model.FeedCollection{UUID: uuid, Name: module.Name, Course: module.Course, UserDefinedName: module.UserDefinedName}) - } - } - - jsonModules, _ := json.Marshal(uuidFeedCollections) - feed.Set("modules", string(jsonModules)) - - err = app.Dao().SaveRecord(feed) - if err != nil { - return err - } - } - return nil -} - -func searchUUIDForModule(app *pocketbase.PocketBase, module model.FeedCollection) string { - var event model.Event - err := app.Dao().DB().Select("*").From("events").Where(dbx.NewExp("Name = {:name} AND course = {:course}", dbx.Params{"name": module.Name, "course": module.Course})).One(&event) - if err != nil { - return "" - } - return event.UUID -} diff --git a/backend/service/room/roomService.go b/backend/service/room/roomService.go index e8a18c5..fdf9ab9 100644 --- a/backend/service/room/roomService.go +++ b/backend/service/room/roomService.go @@ -74,6 +74,7 @@ func GetFreeRooms(app *pocketbase.PocketBase, from time.Time, to time.Time) ([]s return freeRooms, nil } +// Remove all rooms from the list that have events in the given time range func removeRoomsThatHaveEvents(rooms []string, schedule []model.Event) []string { var freeRooms []string for _, room := range rooms { @@ -84,6 +85,7 @@ func removeRoomsThatHaveEvents(rooms []string, schedule []model.Event) []string return freeRooms } +// Check if a room is in the schedule func isRoomInSchedule(room string, schedule []model.Event) bool { for _, event := range schedule { if event.Course != "Sport" { diff --git a/backend/service/room/roomService_test.go b/backend/service/room/roomService_test.go index 10a6b41..4f0f91f 100644 --- a/backend/service/room/roomService_test.go +++ b/backend/service/room/roomService_test.go @@ -191,6 +191,50 @@ func Test_isRoomInSchedule(t *testing.T) { }, want: false, }, + { + name: "schedule event.Course is sport", + args: args{ + room: "Klettergerüst", + schedule: []model.Event{ + { + UUID: "903784265784639527", + Day: "Montag", + Week: "52", + Start: types.DateTime{}, + End: types.DateTime{}, + Name: "Hampelmann", + EventType: "S", + Prof: "Prof. Dr. Bewegung", + Rooms: "Klettergerüst", + Notes: "A apple a day keeps the doctor away", + Course: "Sport", + }, + }, + }, + want: true, + }, + { + name: "schedule event.Course is sport with different room", + args: args{ + room: "HTWK Sportplatz", + schedule: []model.Event{ + { + UUID: "903784265784639527", + Day: "Montag", + Week: "52", + Start: types.DateTime{}, + End: types.DateTime{}, + Name: "Hampelmann", + EventType: "S", + Prof: "Prof. Dr. Bewegung", + Rooms: "Klettergerüst", + Notes: "A apple a day keeps the doctor away", + Course: "Sport", + }, + }, + }, + want: false, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { diff --git a/docker-compose.yml b/docker-compose.yml index 7c77c23..5b2ce9b 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -23,8 +23,8 @@ services: context: ./backend target: dev # prod command: "--http=0.0.0.0:8090 --dir=/htwkalender/data/pb_data" - #ports: - # - "8090:8090" + ports: + - "8090:8090" volumes: - pb_data:/htwkalender/data # for production with volume # - ./backend:/htwkalender/data # for development with bind mount from project directory From ad197f366d20e08f10c375a8ef0e90e9fb33457d Mon Sep 17 00:00:00 2001 From: Elmar Kresse Date: Fri, 17 May 2024 18:30:35 +0000 Subject: [PATCH 03/95] test:#324 change eventService and fetch routine --- backend/main.go | 2 +- backend/service/addRoute.go | 58 +++++++++++++------ backend/service/addSchedule.go | 6 +- backend/service/course/courseFunctions.go | 7 +-- backend/service/date/dateFormat.go | 2 +- backend/service/db/dbEvents.go | 17 +++++- backend/service/db/dbGroups.go | 6 +- backend/service/events/eventService.go | 38 +++++------- backend/service/feed/feedFunctions.go | 6 +- backend/service/fetch/sport/sportFetcher.go | 13 +++-- .../fetch/v1/fetchSeminarEventService.go | 10 ++-- .../fetch/v1/fetchSeminarGroupService.go | 6 +- backend/service/fetch/v2/fetcher.go | 2 +- backend/service/functions/time/parse.go | 2 +- 14 files changed, 102 insertions(+), 73 deletions(-) diff --git a/backend/main.go b/backend/main.go index d39757a..8b78ba1 100644 --- a/backend/main.go +++ b/backend/main.go @@ -41,6 +41,6 @@ func main() { service.AddSchedules(app) if err := app.Start(); err != nil { - slog.Error("Failed to start app: %v", err) + slog.Error("Failed to start app: ", "error", err) } } diff --git a/backend/service/addRoute.go b/backend/service/addRoute.go index 24991c3..eeb6884 100644 --- a/backend/service/addRoute.go +++ b/backend/service/addRoute.go @@ -17,6 +17,7 @@ package service import ( + "htwkalender/service/course" "htwkalender/service/events" "htwkalender/service/fetch/sport" v1 "htwkalender/service/fetch/v1" @@ -41,7 +42,7 @@ func AddRoutes(app *pocketbase.PocketBase) { Handler: func(c echo.Context) error { savedEvents, err := v2.ParseEventsFromRemote(app) if err != nil { - slog.Error("Failed to parse events from remote: %v", err) + slog.Error("Failed to parse events from remote: ", "error", err) return c.JSON(http.StatusBadRequest, "Failed to parse events from remote") } else { return c.JSON(http.StatusOK, savedEvents) @@ -58,6 +59,25 @@ func AddRoutes(app *pocketbase.PocketBase) { return nil }) + app.OnBeforeServe().Add(func(e *core.ServeEvent) error { + _, err := e.Router.AddRoute(echo.Route{ + Method: http.MethodGet, + Path: "/api/fetch/daily/events", + Handler: func(c echo.Context) error { + course.UpdateCourse(app) + return c.JSON(http.StatusOK, "Daily events fetched") + }, + Middlewares: []echo.MiddlewareFunc{ + apis.ActivityLogger(app), + apis.RequireAdminAuth(), + }, + }) + if err != nil { + return err + } + return nil + }) + app.OnBeforeServe().Add(func(e *core.ServeEvent) error { _, err := e.Router.AddRoute(echo.Route{ Method: http.MethodGet, @@ -156,7 +176,7 @@ func AddRoutes(app *pocketbase.PocketBase) { date := c.QueryParam("date") roomSchedule, err := room.GetRoomScheduleForDay(app, roomParam, date) if err != nil { - slog.Error("Failed to get room schedule for day: %v", err) + slog.Error("Failed to get room schedule for day: ", "error", err) return c.JSON(http.StatusBadRequest, "Failed to get room schedule for day") } return c.JSON(http.StatusOK, roomSchedule) @@ -182,7 +202,7 @@ func AddRoutes(app *pocketbase.PocketBase) { from := c.QueryParam("from") roomSchedule, err := room.GetRoomSchedule(app, roomParam, from, to) if err != nil { - slog.Error("Failed to get room schedule: %v", err) + slog.Error("Failed to get room schedule:", "error", err) return c.JSON(http.StatusBadRequest, "Failed to get room schedule") } return c.JSON(http.StatusOK, roomSchedule) @@ -205,17 +225,17 @@ func AddRoutes(app *pocketbase.PocketBase) { Handler: func(c echo.Context) error { from, err := time.ParseTime(c.QueryParam("from")) if err != nil { - slog.Error("Failed to parse time: %v", err) + slog.Error("Failed to parse time: ", "error", err) return c.JSON(http.StatusBadRequest, "Failed to parse time") } to, err := time.ParseTime(c.QueryParam("to")) if err != nil { - slog.Error("Failed to parse time: %v", err) + slog.Error("Failed to parse time: ", "error", err) return c.JSON(http.StatusBadRequest, "Failed to parse time") } rooms, err := room.GetFreeRooms(app, from, to) if err != nil { - slog.Error("Failed to get free rooms: %v", err) + slog.Error("Failed to get free rooms: ", "error", err) return c.JSON(http.StatusBadRequest, "Failed to get free rooms") } return c.JSON(http.StatusOK, rooms) @@ -237,12 +257,14 @@ func AddRoutes(app *pocketbase.PocketBase) { Method: http.MethodGet, Path: "/api/course/modules", Handler: func(c echo.Context) error { - course := c.QueryParam("course") - semester := c.QueryParam("semester") - modules, err := events.GetModulesForCourseDistinct(app, course, semester) + modules, err := events.GetModulesForCourseDistinct( + app, + c.QueryParam("course"), + c.QueryParam("semester"), + ) if err != nil { - slog.Error("Failed to get modules for course and semester: %v", err) + slog.Error("Failed to get modules for course and semester: ", "error", err) return c.JSON(http.StatusBadRequest, "Failed to get modules for course and semester") } else { return c.JSON(http.StatusOK, modules) @@ -265,7 +287,7 @@ func AddRoutes(app *pocketbase.PocketBase) { Handler: func(c echo.Context) error { modules, err := events.GetAllModulesDistinct(app) if err != nil { - slog.Error("Failed to get modules: %v", err) + slog.Error("Failed to get modules: ", "error", err) return c.JSON(http.StatusBadRequest, "Failed to get modules") } return c.JSON(http.StatusOK, modules) @@ -288,7 +310,7 @@ func AddRoutes(app *pocketbase.PocketBase) { requestModule := c.QueryParam("uuid") module, err := events.GetModuleByUUID(app, requestModule) if err != nil { - slog.Error("Failed to get module: %v", err) + slog.Error("Failed to get module: ", "error", err) return c.JSON(http.StatusBadRequest, "Failed to get module") } else { return c.JSON(http.StatusOK, module) @@ -338,7 +360,7 @@ func AddRoutes(app *pocketbase.PocketBase) { courses, err := events.GetAllCoursesForSemesterWithEvents(app, semester) if err != nil { - slog.Error("Failed to get courses for semester with events: %v", err) + slog.Error("Failed to get courses for semester with events: ", "error", err) return c.JSON(http.StatusBadRequest, "Failed to get courses for semester with events") } else { return c.JSON(200, courses) @@ -359,11 +381,13 @@ func AddRoutes(app *pocketbase.PocketBase) { Method: http.MethodDelete, Path: "/api/events", Handler: func(c echo.Context) error { - course := c.QueryParam("course") - semester := c.QueryParam("semester") - err := events.DeleteAllEventsByCourseAndSemester(app, course, semester) + err := events.DeleteAllEventsByCourseAndSemester( + app, + c.QueryParam("course"), + c.QueryParam("semester"), + ) if err != nil { - slog.Error("Failed to delete events: %v", err) + slog.Error("Failed to delete events: ", "error", err) return c.JSON(http.StatusBadRequest, "Failed to delete events") } else { return c.JSON(http.StatusBadRequest, "Events deleted") diff --git a/backend/service/addSchedule.go b/backend/service/addSchedule.go index 3c38153..9d2b901 100644 --- a/backend/service/addSchedule.go +++ b/backend/service/addSchedule.go @@ -42,7 +42,7 @@ func AddSchedules(app *pocketbase.PocketBase) { slog.Info("Started updating courses schedule") groups, err := v1.FetchSeminarGroups(app) if err != nil { - slog.Warn("Failed to fetch seminar groups: %v", err) + slog.Warn("Failed to fetch seminar groups: ", "error", err) } slog.Info("Successfully fetched " + strconv.FormatInt(int64(len(groups)), 10) + " seminar groups") }) @@ -66,7 +66,7 @@ func AddSchedules(app *pocketbase.PocketBase) { slog.Info("Started fetching sport events schedule") sportEvents, err := sport.FetchAndUpdateSportEvents(app) if err != nil { - slog.Error("Failed to fetch and save sport events: %v", err) + slog.Error("Failed to fetch and save sport events:", "error", err) } slog.Info("Successfully fetched " + strconv.FormatInt(int64(len(sportEvents)), 10) + " sport events") }) @@ -75,7 +75,7 @@ func AddSchedules(app *pocketbase.PocketBase) { scheduler.MustAdd("fetchEvents", "0 22 * * 6", func() { savedEvents, err := v2.FetchAllEventsAndSave(app, time.RealClock{}) if err != nil { - slog.Error("Failed to fetch and save events: %v", err) + slog.Error("Failed to fetch and save events: ", "error", err) } else { slog.Info("Successfully fetched " + strconv.FormatInt(int64(len(savedEvents)), 10) + " events") } diff --git a/backend/service/course/courseFunctions.go b/backend/service/course/courseFunctions.go index 43720b7..d51ad00 100644 --- a/backend/service/course/courseFunctions.go +++ b/backend/service/course/courseFunctions.go @@ -20,17 +20,14 @@ import ( "github.com/pocketbase/pocketbase" "htwkalender/service/events" "log/slog" - "strconv" ) func UpdateCourse(app *pocketbase.PocketBase) { courses := events.GetAllCourses(app) for _, course := range courses { - savedEvents, err := events.UpdateModulesForCourse(app, course) + _, err := events.UpdateModulesForCourse(app, course) if err != nil { - slog.Warn("Update Course: "+course+" failed: %v", err) - } else { - slog.Info("Updated Course: " + course + " with " + strconv.FormatInt(int64(len(savedEvents)), 10) + " events") + slog.Warn("Update Course: "+course+" failed:", "error", err) } } } diff --git a/backend/service/date/dateFormat.go b/backend/service/date/dateFormat.go index a8e9c16..8eee644 100644 --- a/backend/service/date/dateFormat.go +++ b/backend/service/date/dateFormat.go @@ -29,7 +29,7 @@ func GetDateFromWeekNumber(year int, weekNumber int, dayName string) (time.Time, europeTime, err := time.LoadLocation("Europe/Berlin") if err != nil { - slog.Error("Failed to load location: ", err) + slog.Error("Failed to load location: ", "error", err) return time.Time{}, err } diff --git a/backend/service/db/dbEvents.go b/backend/service/db/dbEvents.go index 42869f3..0d1fe04 100644 --- a/backend/service/db/dbEvents.go +++ b/backend/service/db/dbEvents.go @@ -192,13 +192,26 @@ func GetPlanForModules(app *pocketbase.PocketBase, modules map[string]model.Feed return events, nil } +func GetAllEventsForCourse(app *pocketbase.PocketBase, course string) (model.Events, error) { + var events model.Events + + // get all events from event records in the events collection + err := app.Dao().DB().Select("*").From("events").Where(dbx.NewExp("course = {:course}", dbx.Params{"course": course})).All(&events) + if err != nil { + slog.Error("Error while getting events from database: ", "error", err) + return nil, fmt.Errorf("error while getting events from database for course %s", course) + } + + return events, nil +} + func GetAllModulesForCourse(app *pocketbase.PocketBase, course string, semester string) (model.Events, error) { var events model.Events // get all events from event records in the events collection err := app.Dao().DB().Select("*").From("events").Where(dbx.NewExp("course = {:course} AND semester = {:semester}", dbx.Params{"course": course, "semester": semester})).GroupBy("Name").Distinct(true).All(&events) if err != nil { - slog.Error("Error while getting events from database: ", err) + slog.Error("Error while getting events from database: ", "error", err) return nil, fmt.Errorf("error while getting events from database for course %s and semester %s", course, semester) } @@ -210,7 +223,7 @@ func GetAllModulesDistinctByNameAndCourse(app *pocketbase.PocketBase) ([]model.M err := app.Dao().DB().Select("Name", "EventType", "Prof", "course", "semester", "uuid").From("events").GroupBy("Name", "Course").Distinct(true).All(&modules) if err != nil { - slog.Error("Error while getting events from database: ", err) + slog.Error("Error while getting events from database: ", "error", err) return nil, fmt.Errorf("error while getting events distinct by name and course from data") } diff --git a/backend/service/db/dbGroups.go b/backend/service/db/dbGroups.go index 1f51c15..1e5d02c 100644 --- a/backend/service/db/dbGroups.go +++ b/backend/service/db/dbGroups.go @@ -85,7 +85,7 @@ func GetAllCourses(app *pocketbase.PocketBase) []string { // get all rooms from event records in the events collection err := app.Dao().DB().Select("course").From("groups").All(&courses) if err != nil { - slog.Error("Error while getting groups from database: ", err) + slog.Error("Error while getting groups from database: ", "error", err) return []string{} } @@ -107,7 +107,7 @@ func GetAllCoursesForSemester(app *pocketbase.PocketBase, semester string) []str // get all courses for a specific semester err := app.Dao().DB().Select("course").From("groups").Where(dbx.NewExp("semester = {:semester}", dbx.Params{"semester": semester})).All(&courses) if err != nil { - slog.Error("Error while getting groups from database: ", err) + slog.Error("Error while getting groups from database: ", "error", err) return []string{} } @@ -130,7 +130,7 @@ func GetAllCoursesForSemesterWithEvents(app *pocketbase.PocketBase, semester str // get all courses from events distinct for a specific semester err := app.Dao().DB().Select("course").From("events").Where(dbx.NewExp("semester = {:semester}", dbx.Params{"semester": semester})).Distinct(true).All(&courses) if err != nil { - slog.Error("Error while getting groups from database: ", err) + slog.Error("Error while getting groups from database: ", "error", err) return nil, err } diff --git a/backend/service/events/eventService.go b/backend/service/events/eventService.go index ce6dbbc..fedfa51 100644 --- a/backend/service/events/eventService.go +++ b/backend/service/events/eventService.go @@ -23,6 +23,7 @@ import ( "htwkalender/service/fetch/v1" "htwkalender/service/functions" "log/slog" + "strconv" ) func GetModulesForCourseDistinct(app *pocketbase.PocketBase, course string, semester string) (model.Events, error) { @@ -133,21 +134,13 @@ func UpdateModulesForCourse(app *pocketbase.PocketBase, course string) (model.Ev //if there are no events in the database, save the new events //get all events for the course and the semester - events, err := db.GetAllModulesForCourse(app, course, "ws") + dbEvents, err := db.GetAllEventsForCourse(app, course) if err != nil { return nil, err } - // append all events for the course and the semester to the events array for ss - summerEvents, err := db.GetAllModulesForCourse(app, course, "ss") - if err != nil { - return nil, err - } - - events = append(events, summerEvents...) - //if there are no events in the database, save the new events - if len(events) == 0 { + if len(dbEvents) == 0 { events, dbError := db.SaveSeminarGroupEvents(seminarGroup, app) if dbError != nil { return nil, dbError @@ -161,32 +154,33 @@ func UpdateModulesForCourse(app *pocketbase.PocketBase, course string) (model.Ev // check which events are not already in the database and need to be inserted/saved for _, event := range seminarGroup.Events { - if !ContainsEvent(events, event) { + if !ContainsEvent(dbEvents, event) { insertList = append(insertList, event) } } - // save all events that are in the insertList - savedEvents, err := db.SaveEvents(insertList, app) - if err != nil { - slog.Error("Failed to save events: %v", err) - return nil, err - } - // check which events are in the database but not in the seminarGroup and need to be deleted - for _, event := range events { - if !ContainsEvent(seminarGroup.Events, event) { - deleteList = append(deleteList, event) + for _, dbEvent := range dbEvents { + if !ContainsEvent(seminarGroup.Events, dbEvent) { + deleteList = append(deleteList, dbEvent) } } // delete all events that are in the deleteList err = db.DeleteEvents(deleteList, app) if err != nil { - slog.Error("Failed to delete events: %v", err) + slog.Error("Failed to delete events:", "error", err) return nil, err } + // save all events that are in the insertList + savedEvents, err := db.SaveEvents(insertList, app) + if err != nil { + slog.Error("Failed to save events: ", "error", err) + return nil, err + } + + slog.Info("Course: " + course + " - Event changes: " + strconv.FormatInt(int64(len(insertList)), 10) + " new events, " + strconv.FormatInt(int64(len(deleteList)), 10) + " deleted events") return savedEvents, nil } diff --git a/backend/service/feed/feedFunctions.go b/backend/service/feed/feedFunctions.go index 050c5e2..894f322 100644 --- a/backend/service/feed/feedFunctions.go +++ b/backend/service/feed/feedFunctions.go @@ -30,7 +30,7 @@ import ( func ClearFeeds(db *daos.Dao, months int, clock localTime.Clock) { feeds, err := database.GetAllFeeds(db) if err != nil { - slog.Error("CleanFeeds: failed to get all feeds", err) + slog.Error("CleanFeeds: failed to get all feeds", "error", err) return } for _, feed := range feeds { @@ -44,8 +44,8 @@ func ClearFeeds(db *daos.Dao, months int, clock localTime.Clock) { var sqlResult sql.Result sqlResult, err = db.DB().Delete("feeds", dbx.NewExp("id = {:id}", dbx.Params{"id": feed.GetId()})).Execute() if err != nil { - slog.Error("CleanFeeds: delete feed "+feed.GetId()+" failed", err) - slog.Error("SQL Result: ", sqlResult) + slog.Error("CleanFeeds: delete feed "+feed.GetId()+" failed", "error", err) + slog.Error("SQL Result: ", "error", sqlResult) } else { slog.Info("CleanFeeds: delete feed " + feed.GetId() + " successful") } diff --git a/backend/service/fetch/sport/sportFetcher.go b/backend/service/fetch/sport/sportFetcher.go index 583815c..c2dac05 100644 --- a/backend/service/fetch/sport/sportFetcher.go +++ b/backend/service/fetch/sport/sportFetcher.go @@ -209,7 +209,7 @@ func getWeekEvents(start time.Time, end time.Time, cycle string) ([]time.Time, [ for _, day := range days { weekDay, err := getDayInt(day) if err != nil { - slog.Error("Error while getting day int: "+day+" ", err) + slog.Error("Error while getting day int: "+day+" ", "error", err) } else { weekEvents = append(weekEvents, model.SportDayStartEnd{ Start: time.Date(start.Year(), start.Month(), start.Day(), startHour, startMinute, 0, 0, start.Location()), @@ -234,7 +234,8 @@ func getWeekEvents(start time.Time, end time.Time, cycle string) ([]time.Time, [ endI, endIErr = getDayInt(days[1]) if endIErr != nil || startIErr != nil { - slog.Error("Error while getting day int: "+days[0]+" - "+days[1]+" :", startIErr, endIErr) + slog.Error("StartError while getting day int: "+days[0]+" - "+days[1]+" :", "error", startIErr) + slog.Error("EndError while getting day int: "+days[0]+" - "+days[1]+" :", "error", endIErr) } else { //create a int array with all days from start to end day var daysBetween []int @@ -259,7 +260,7 @@ func getWeekEvents(start time.Time, end time.Time, cycle string) ([]time.Time, [ dayInt, err := getDayInt(day) if err != nil { - slog.Error("Error while getting day int: "+day+" ", err) + slog.Error("Error while getting day int: "+day+" ", "error", err) } else { dayNumbers = append(dayNumbers, dayInt) } @@ -271,7 +272,7 @@ func getWeekEvents(start time.Time, end time.Time, cycle string) ([]time.Time, [ weekDay, err := getDayInt(day) if err != nil { - slog.Error("Error while getting day int: "+day+" ", err) + slog.Error("Error while getting day int: "+day+" ", "error", err) } else { weekEvents = append(weekEvents, model.SportDayStartEnd{ Start: time.Date(start.Year(), start.Month(), start.Day(), startHour, startMinute, 0, 0, start.Location()), @@ -378,7 +379,7 @@ func fetchAllAvailableSportCourses() ([]string, error) { var doc, err = htmlRequest(url) if err != nil { - slog.Error("Error while fetching sport courses from webpage", err) + slog.Error("Error while fetching sport courses from webpage", "error", err) return nil, err } @@ -443,7 +444,7 @@ func htmlRequest(url string) (*goquery.Document, error) { defer func(Body io.ReadCloser) { readErr := Body.Close() if readErr != nil { - slog.Error("Error while closing response body from html request", readErr) + slog.Error("Error while closing response body from html request", "error", readErr) return } }(resp.Body) diff --git a/backend/service/fetch/v1/fetchSeminarEventService.go b/backend/service/fetch/v1/fetchSeminarEventService.go index c6ac6e4..ca02906 100644 --- a/backend/service/fetch/v1/fetchSeminarEventService.go +++ b/backend/service/fetch/v1/fetchSeminarEventService.go @@ -103,19 +103,18 @@ func parseSeminarGroup(result string) model.SeminarGroup { if eventTables == nil || allDayLabels == nil { return model.SeminarGroup{} } - - eventsWithCombinedWeeks := toEvents(eventTables, allDayLabels) + course := findFirstSpanWithClass(table, "header-2-0-1").FirstChild.Data + eventsWithCombinedWeeks := toEvents(eventTables, allDayLabels, course) splitEventsByWeekVal := splitEventsByWeek(eventsWithCombinedWeeks) events := splitEventsBySingleWeek(splitEventsByWeekVal) semesterString := findFirstSpanWithClass(table, "header-0-2-0").FirstChild.Data - course := findFirstSpanWithClass(table, "header-2-0-1").FirstChild.Data semester, year := extractSemesterAndYear(semesterString) events = convertWeeksToDates(events, semester, year) events = generateUUIDs(events, course) events, err = SplitEventType(events) if err != nil { - slog.Error("Error occurred while splitting event types: %s", err) + slog.Error("Error occurred while splitting event types:", "error", err) return model.SeminarGroup{} } @@ -204,7 +203,7 @@ func extractSemesterAndYear(semesterString string) (string, string) { return semesterShortcut, year } -func toEvents(tables [][]*html.Node, days []string) []model.Event { +func toEvents(tables [][]*html.Node, days []string, course string) []model.Event { var events []model.Event for table := range tables { @@ -225,6 +224,7 @@ func toEvents(tables [][]*html.Node, days []string) []model.Event { Rooms: getTextContent(tableData[6]), Notes: getTextContent(tableData[7]), BookedAt: getTextContent(tableData[8]), + Course: course, }) } } diff --git a/backend/service/fetch/v1/fetchSeminarGroupService.go b/backend/service/fetch/v1/fetchSeminarGroupService.go index 7809ec9..9e909e5 100644 --- a/backend/service/fetch/v1/fetchSeminarGroupService.go +++ b/backend/service/fetch/v1/fetchSeminarGroupService.go @@ -68,7 +68,7 @@ func FetchSeminarGroups(app *pocketbase.PocketBase) ([]*models.Record, error) { for i, semester := range semesterString { results[i], err = getSeminarHTML(semester) if err != nil { - slog.Error("Error while fetching seminar groups for: "+semester, err) + slog.Error("Error while fetching seminar groups for: "+semester, "error", err) return nil, err } groups = append(groups, parseSeminarGroups(results[i], semester)...) @@ -79,14 +79,14 @@ func FetchSeminarGroups(app *pocketbase.PocketBase) ([]*models.Record, error) { collection, dbError := db.FindCollection(app, "groups") if dbError != nil { - slog.Error("Error while searching collection groups", dbError) + slog.Error("Error while searching collection groups", "error", dbError) return nil, err } var insertedGroups []*models.Record insertedGroups, dbError = db.SaveGroups(groups, collection, app) if dbError != nil { - slog.Error("Error while saving groups", dbError) + slog.Error("Error while saving groups", "error", dbError) return nil, err } diff --git a/backend/service/fetch/v2/fetcher.go b/backend/service/fetch/v2/fetcher.go index 34dd2ad..b26fe8c 100644 --- a/backend/service/fetch/v2/fetcher.go +++ b/backend/service/fetch/v2/fetcher.go @@ -141,7 +141,7 @@ func parseEventForOneSemester(url string) ([]model.Event, error) { events = convertWeeksToDates(events, semester, year) events, err = v1.SplitEventType(events) if err != nil { - slog.Error("Error occurred while splitting event types: %s", err) + slog.Error("Error occurred while splitting event types: ", "error", err) return nil, err } events = switchNameAndNotesForExam(events) diff --git a/backend/service/functions/time/parse.go b/backend/service/functions/time/parse.go index 9a5fd93..46523bc 100644 --- a/backend/service/functions/time/parse.go +++ b/backend/service/functions/time/parse.go @@ -29,7 +29,7 @@ func ParseTime(timeString string) (time.Time, error) { func ParseAsTypesDatetime(time time.Time) types.DateTime { dateTime, err := types.ParseDateTime(time) if err != nil { - slog.Error("Failed to parse time as types.DateTime: %v", err) + slog.Error("Failed to parse time as types.DateTime", "error", err) return types.DateTime{} } return dateTime From 0c71a0ce4933f32d85a615ec2dc2f26b6273bae0 Mon Sep 17 00:00:00 2001 From: Elmar Kresse Date: Sat, 18 May 2024 12:44:27 +0200 Subject: [PATCH 04/95] feat:#28 added api for eventTypes --- backend/model/eventModel.go | 4 ++++ backend/service/addRoute.go | 28 ++++++++++++++++++++++++-- backend/service/db/dbEvents.go | 11 ++++++++++ backend/service/events/eventService.go | 15 ++++++++++++++ reverseproxy.conf | 18 +++++++++++++++++ 5 files changed, 74 insertions(+), 2 deletions(-) diff --git a/backend/model/eventModel.go b/backend/model/eventModel.go index 8a492cf..2debe6d 100644 --- a/backend/model/eventModel.go +++ b/backend/model/eventModel.go @@ -57,6 +57,10 @@ type Event struct { models.BaseModel } +type EventType struct { + EventType string `db:"EventType" json:"eventType"` +} + func (e *Event) Equals(event Event) bool { return e.Day == event.Day && e.Week == event.Week && diff --git a/backend/service/addRoute.go b/backend/service/addRoute.go index eeb6884..0f131bf 100644 --- a/backend/service/addRoute.go +++ b/backend/service/addRoute.go @@ -363,7 +363,31 @@ func AddRoutes(app *pocketbase.PocketBase) { slog.Error("Failed to get courses for semester with events: ", "error", err) return c.JSON(http.StatusBadRequest, "Failed to get courses for semester with events") } else { - return c.JSON(200, courses) + return c.JSON(http.StatusOK, courses) + } + }, + Middlewares: []echo.MiddlewareFunc{ + apis.ActivityLogger(app), + }, + }) + if err != nil { + return err + } + return nil + }) + + // API Endpoint to get all eventTypes from the database distinct + app.OnBeforeServe().Add(func(e *core.ServeEvent) error { + _, err := e.Router.AddRoute(echo.Route{ + Method: http.MethodGet, + Path: "/api/events/types", + Handler: func(c echo.Context) error { + eventTypes, err := events.GetEventTypes(app) + if err != nil { + slog.Error("Failed to get event types", "error", err) + return c.JSON(http.StatusBadRequest, "Failed to get event types") + } else { + return c.JSON(http.StatusOK, eventTypes) } }, Middlewares: []echo.MiddlewareFunc{ @@ -390,7 +414,7 @@ func AddRoutes(app *pocketbase.PocketBase) { slog.Error("Failed to delete events: ", "error", err) return c.JSON(http.StatusBadRequest, "Failed to delete events") } else { - return c.JSON(http.StatusBadRequest, "Events deleted") + return c.JSON(http.StatusOK, "Events deleted") } }, Middlewares: []echo.MiddlewareFunc{ diff --git a/backend/service/db/dbEvents.go b/backend/service/db/dbEvents.go index 0d1fe04..8db7b64 100644 --- a/backend/service/db/dbEvents.go +++ b/backend/service/db/dbEvents.go @@ -367,6 +367,17 @@ func GetEventsThatStartBeforeAndEndBefore(app *pocketbase.PocketBase, from types return events, nil } +func GetAllEventTypes(app *pocketbase.PocketBase) ([]model.EventType, error) { + var eventTypes []model.EventType + + err := app.Dao().DB().Select("EventType").From("events").GroupBy("EventType").Distinct(true).All(&eventTypes) + if err != nil { + return nil, err + } + + return eventTypes, nil +} + func GetEventsThatStartAfterAndEndAfter(app *pocketbase.PocketBase, from types.DateTime, to types.DateTime) (model.Events, error) { var events model.Events err := app.Dao().DB().Select("*").From("events").Where(dbx.NewExp("Start >= {:startDate} AND End >= {:endDate} AND Start <= {:endDate} AND End >= {:startDate}", dbx.Params{"startDate": from, "endDate": to})).All(&events) diff --git a/backend/service/events/eventService.go b/backend/service/events/eventService.go index fedfa51..77b8bb1 100644 --- a/backend/service/events/eventService.go +++ b/backend/service/events/eventService.go @@ -198,3 +198,18 @@ func ContainsEvent(events model.Events, event model.Event) bool { } return false } + +func GetEventTypes(app *pocketbase.PocketBase) ([]string, error) { + dbEventTypes, err := db.GetAllEventTypes(app) + if err != nil { + return nil, err + } + + // Convert the []model.EventType to []string + var eventTypes []string + for _, eventType := range dbEventTypes { + eventTypes = append(eventTypes, eventType.EventType) + } + + return eventTypes, nil +} diff --git a/reverseproxy.conf b/reverseproxy.conf index ff64ab2..0a6d63f 100644 --- a/reverseproxy.conf +++ b/reverseproxy.conf @@ -196,6 +196,24 @@ http { limit_req zone=modules burst=5 nodelay; } + location /api/events/types { + proxy_pass http://htwkalender-backend:8090; + client_max_body_size 20m; + proxy_connect_timeout 600s; + proxy_read_timeout 600s; + proxy_send_timeout 600s; + send_timeout 600s; + proxy_cache_bypass 0; + proxy_no_cache 0; + proxy_cache mcache; # mcache=RAM + proxy_cache_valid 200 301 302 10m; + proxy_cache_valid 403 404 5m; + proxy_cache_lock on; + proxy_cache_use_stale timeout updating; + add_header X-Proxy-Cache $upstream_cache_status; + limit_req zone=modules burst=10 nodelay; + } + location /api/rooms { proxy_pass http://htwkalender-backend:8090; client_max_body_size 20m; From bb613dc5c4cf0192387c8c251acde40c468aeec9 Mon Sep 17 00:00:00 2001 From: Elmar Kresse Date: Sat, 18 May 2024 12:44:41 +0200 Subject: [PATCH 05/95] feat:#28 rewrote frontend input for filter eventTypes --- frontend/src/api/fetchEvents.ts | 33 +++++++++++++++++++ .../src/components/AdditionalModuleTable.vue | 22 ++++++++++--- 2 files changed, 50 insertions(+), 5 deletions(-) create mode 100644 frontend/src/api/fetchEvents.ts diff --git a/frontend/src/api/fetchEvents.ts b/frontend/src/api/fetchEvents.ts new file mode 100644 index 0000000..7738036 --- /dev/null +++ b/frontend/src/api/fetchEvents.ts @@ -0,0 +1,33 @@ +//Calendar implementation for the HTWK Leipzig timetable. Evaluation and display of the individual dates in iCal format. +//Copyright (C) 2024 HTWKalender support@htwkalender.de + +//This program is free software: you can redistribute it and/or modify +//it under the terms of the GNU Affero General Public License as published by +//the Free Software Foundation, either version 3 of the License, or +//(at your option) any later version. + +//This program is distributed in the hope that it will be useful, +//but WITHOUT ANY WARRANTY; without even the implied warranty of +//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +//GNU Affero General Public License for more details. + +//You should have received a copy of the GNU Affero General Public License +//along with this program. If not, see . + +// function to fetch course data from the API + +export async function fetchEventTypes(): Promise { + const eventTypes: string[] = []; + await fetch("/api/events/types") + .then((response) => { + return response.json() as Promise; + }) + .then((responseModules: string[]) => { + responseModules.forEach((eventType: string) => { + eventTypes.push( + eventType, + ); + }); + }); + return eventTypes; +} \ No newline at end of file diff --git a/frontend/src/components/AdditionalModuleTable.vue b/frontend/src/components/AdditionalModuleTable.vue index e62e436..d437567 100644 --- a/frontend/src/components/AdditionalModuleTable.vue +++ b/frontend/src/components/AdditionalModuleTable.vue @@ -30,6 +30,7 @@ import { useDialog } from "primevue/usedialog"; import router from "../router"; import { fetchModule } from "../api/fetchModule.ts"; import { useI18n } from "vue-i18n"; +import { fetchEventTypes } from "../api/fetchEvents.ts"; const dialog = useDialog(); const { t } = useI18n({ useScope: "global" }); @@ -39,6 +40,9 @@ if (store.isEmpty()) { router.replace("/"); } +const eventTypes: Ref = ref([]); + + const mobilePage = inject("mobilePage") as Ref; const filters = ref({ course: { @@ -51,7 +55,7 @@ const filters = ref({ }, eventType: { value: null, - matchMode: FilterMatchMode.CONTAINS, + matchMode: FilterMatchMode.IN, }, prof: { value: null, @@ -63,7 +67,7 @@ const loadedModules: Ref = ref(new Array(10)); const loadingData = ref(true); -onMounted(() => { +onMounted( () => { fetchAllModules() .then( (data) => @@ -74,6 +78,10 @@ onMounted(() => { .finally(() => { loadingData.value = false; }); + + fetchEventTypes().then((data) => { + eventTypes.value = data; + }); }); const ModuleInformation = defineAsyncComponent( @@ -184,16 +192,20 @@ function unselectModule(event: DataTableRowUnselectEvent) {