diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..547f3f9 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,27 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "Launch file", + "type": "go", + "request": "launch", + "mode": "debug", + "program": "backend/main.go", + "args": ["serve"], + "showLog": true + }, + { + "name": "Launch Package", + "type": "go", + "request": "launch", + "mode": "auto", + "program": "backend", + "env": {}, + "args": ["serve"], + "showLog": true + } + ] +} diff --git a/backend/model/feedModel.go b/backend/model/feedModel.go index 0f2cb35..e3920a2 100644 --- a/backend/model/feedModel.go +++ b/backend/model/feedModel.go @@ -1,8 +1,13 @@ package model +import "github.com/pocketbase/pocketbase/models" + type Feed struct { - Id string `db:"id" json:"id"` Modules string `db:"modules" json:"modules"` - Created string `db:"created" json:"created"` - Updated string `db:"updated" json:"updated"` + models.BaseModel +} + +// SetModules set modules field +func (f *Feed) SetModules(modules string) { + f.Modules = modules } diff --git a/backend/model/icalModel.go b/backend/model/icalModel.go index 192a528..2060712 100644 --- a/backend/model/icalModel.go +++ b/backend/model/icalModel.go @@ -23,6 +23,7 @@ type Entry struct { type Entries []*Entry type FeedCollection struct { + UUID string `db:"uuid" json:"uuid"` Name string `db:"Name" json:"name"` Course string `db:"course" json:"course"` UserDefinedName string `db:"userDefinedName" json:"userDefinedName"` diff --git a/backend/model/moduleModel.go b/backend/model/moduleModel.go index ab0d17f..422613d 100644 --- a/backend/model/moduleModel.go +++ b/backend/model/moduleModel.go @@ -1,6 +1,7 @@ package model type Module struct { + UUID string `json:"uuid"` Name string `json:"name"` Prof string `json:"prof"` Course string `json:"course"` diff --git a/backend/pb_schema.json b/backend/pb_schema.json index d3536f9..2054fd0 100644 --- a/backend/pb_schema.json +++ b/backend/pb_schema.json @@ -1,172 +1,4 @@ [ - { - "id": "_pb_users_auth_", - "name": "users", - "type": "auth", - "system": false, - "schema": [ - { - "id": "users_name", - "name": "name", - "type": "text", - "system": false, - "required": false, - "options": { - "min": null, - "max": null, - "pattern": "" - } - }, - { - "id": "users_avatar", - "name": "avatar", - "type": "file", - "system": false, - "required": false, - "options": { - "maxSelect": 1, - "maxSize": 5242880, - "mimeTypes": [ - "image/jpeg", - "image/png", - "image/svg+xml", - "image/gif", - "image/webp" - ], - "thumbs": null, - "protected": false - } - } - ], - "indexes": [], - "listRule": "id = @request.auth.id", - "viewRule": "id = @request.auth.id", - "createRule": "", - "updateRule": "id = @request.auth.id", - "deleteRule": "id = @request.auth.id", - "options": { - "allowEmailAuth": true, - "allowOAuth2Auth": true, - "allowUsernameAuth": true, - "exceptEmailDomains": null, - "manageRule": null, - "minPasswordLength": 8, - "onlyEmailDomains": null, - "requireEmail": false - } - }, - { - "id": "cfq9mqlmd97v8z5", - "name": "groups", - "type": "base", - "system": false, - "schema": [ - { - "id": "85msl21p", - "name": "university", - "type": "text", - "system": false, - "required": false, - "options": { - "min": null, - "max": null, - "pattern": "" - } - }, - { - "id": "2sii4dtp", - "name": "shortcut", - "type": "text", - "system": false, - "required": false, - "options": { - "min": null, - "max": null, - "pattern": "" - } - }, - { - "id": "uiwgo28f", - "name": "groupId", - "type": "text", - "system": false, - "required": false, - "options": { - "min": null, - "max": null, - "pattern": "" - } - }, - { - "id": "y0l1lrzs", - "name": "course", - "type": "text", - "system": false, - "required": false, - "options": { - "min": null, - "max": null, - "pattern": "" - } - }, - { - "id": "kr62mhbz", - "name": "faculty", - "type": "text", - "system": false, - "required": false, - "options": { - "min": null, - "max": null, - "pattern": "" - } - }, - { - "id": "ya6znpez", - "name": "facultyId", - "type": "text", - "system": false, - "required": false, - "options": { - "min": null, - "max": null, - "pattern": "" - } - } - ], - "indexes": [ - "CREATE UNIQUE INDEX `idx_rcaN2Oq` ON `groups` (`course`)" - ], - "listRule": null, - "viewRule": null, - "createRule": null, - "updateRule": null, - "deleteRule": null, - "options": {} - }, - { - "id": "d65h4wh7zk13gxp", - "name": "feeds", - "type": "base", - "system": false, - "schema": [ - { - "id": "cowxjfmc", - "name": "modules", - "type": "json", - "system": false, - "required": true, - "options": {} - } - ], - "indexes": [], - "listRule": null, - "viewRule": null, - "createRule": null, - "updateRule": null, - "deleteRule": null, - "options": {} - }, { "id": "7her4515qsmrxe8", "name": "events", diff --git a/backend/service/addRoute.go b/backend/service/addRoute.go index d5066bf..795d91d 100644 --- a/backend/service/addRoute.go +++ b/backend/service/addRoute.go @@ -12,16 +12,10 @@ import ( "io" "net/http" "net/url" - "os" ) func AddRoutes(app *pocketbase.PocketBase) { - app.OnBeforeServe().Add(func(e *core.ServeEvent) error { - e.Router.GET("/*", apis.StaticDirectoryHandler(os.DirFS("./pb_public"), false)) - return nil - }) - app.OnBeforeServe().Add(func(e *core.ServeEvent) error { _, err := e.Router.AddRoute(echo.Route{ Method: http.MethodGet, @@ -39,6 +33,24 @@ func AddRoutes(app *pocketbase.PocketBase) { return nil }) + app.OnBeforeServe().Add(func(e *core.ServeEvent) error { + _, err := e.Router.AddRoute(echo.Route{ + Method: http.MethodDelete, + Path: "/api/modules", + Handler: func(c echo.Context) error { + return events.DeleteAllEvents(app) + }, + 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, @@ -243,4 +255,27 @@ 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/feed/migrate", + Handler: func(c echo.Context) error { + err := ical.MigrateFeedJson(app) + + if err != nil { + return c.JSON(500, err) + } else { + return c.JSON(200, "Migrated") + } + }, + Middlewares: []echo.MiddlewareFunc{ + apis.ActivityLogger(app), + }, + }) + if err != nil { + return err + } + return nil + }) } diff --git a/backend/service/db/dbEvents.go b/backend/service/db/dbEvents.go index 5789e39..72a2bf4 100644 --- a/backend/service/db/dbEvents.go +++ b/backend/service/db/dbEvents.go @@ -180,6 +180,17 @@ func DeleteAllEventsForCourse(app *pocketbase.PocketBase, course string, semeste return nil } +func DeleteAllEvents(app *pocketbase.PocketBase) error { + + _, err := app.Dao().DB().Delete("events", dbx.NewExp("1=1")).Execute() + + if err != nil { + return err + } + + return nil +} + func FindAllEventsByModule(app *pocketbase.PocketBase, moduleName string) (model.Events, error) { var events model.Events diff --git a/backend/service/events/eventService.go b/backend/service/events/eventService.go index 5f589fe..e114bff 100644 --- a/backend/service/events/eventService.go +++ b/backend/service/events/eventService.go @@ -53,6 +53,7 @@ func GetModuleByName(app *pocketbase.PocketBase, name string) (model.Module, err return model.Module{}, err } else { return model.Module{ + UUID: events[0].UUID, Name: name, Events: events, Prof: events[0].Prof, @@ -74,6 +75,15 @@ func DeleteAllEventsByCourseAndSemester(app *pocketbase.PocketBase, course strin } } +func DeleteAllEvents(app *pocketbase.PocketBase) error { + err := db.DeleteAllEvents(app) + if err != nil { + return err + } else { + return nil + } +} + // UpdateModulesForCourse updates all modules for a course // Does Updates for ws and ss semester sequentially // Update runs through the following steps: diff --git a/backend/service/fetch/fetchSeminarEventService.go b/backend/service/fetch/fetchSeminarEventService.go index 6afdd28..e942a78 100644 --- a/backend/service/fetch/fetchSeminarEventService.go +++ b/backend/service/fetch/fetchSeminarEventService.go @@ -113,22 +113,23 @@ func parseSeminarGroup(result string) model.SeminarGroup { 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) + events = generateUUIDs(events, course) events = splitEventType(events) var seminarGroup = model.SeminarGroup{ University: findFirstSpanWithClass(table, "header-1-0-0").FirstChild.Data, - Course: findFirstSpanWithClass(table, "header-2-0-1").FirstChild.Data, + Course: course, Events: events, } return seminarGroup } -func generateUUIDs(events []model.Event) []model.Event { +func generateUUIDs(events []model.Event, course string) []model.Event { for i, event := range events { // generate a hash value from the event name, course and semester - hash := uuid.NewSHA1(uuid.NameSpaceOID, []byte(event.Name+event.Course+event.Semester)) + hash := uuid.NewSHA1(uuid.NameSpaceOID, []byte(event.Name+course)) events[i].UUID = hash.String() } return events diff --git a/backend/service/fetch/fetchSeminarEventService_test.go b/backend/service/fetch/fetchSeminarEventService_test.go index f916dd9..bb98520 100644 --- a/backend/service/fetch/fetchSeminarEventService_test.go +++ b/backend/service/fetch/fetchSeminarEventService_test.go @@ -208,3 +208,57 @@ func Test_splitEventType(t *testing.T) { }) } } + +func Test_generateUUIDs(t *testing.T) { + type args struct { + events []model.Event + course string + } + tests := []struct { + name string + args args + want []model.Event + }{ + { + name: "Test 1", + args: args{ + events: []model.Event{ + { + Name: " Arbeitssicherheit / Rechtsformen von Unternehmen B435 SBB (wpf) & B348 BIB (pf) 5. FS", + }, + }, + course: "21BIB-2a", + }, + want: []model.Event{ + { + Name: " Arbeitssicherheit / Rechtsformen von Unternehmen B435 SBB (wpf) & B348 BIB (pf) 5. FS", + UUID: "3720afdc-10c7-5b72-9489-cffb70cb0c13", + }, + }, + }, + { + name: "Test 2", + args: args{ + events: []model.Event{ + { + Name: " Arbeitssicherheit / Rechtsformen von Unternehmen B435 SBB (wpf) & B348 BIB (pf) 5. FS", + }, + }, + course: "21BIB-2b", + }, + want: []model.Event{ + { + Name: " Arbeitssicherheit / Rechtsformen von Unternehmen B435 SBB (wpf) & B348 BIB (pf) 5. FS", + UUID: "81083480-bcf1-5452-af84-bb27d79282d8", + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := generateUUIDs(tt.args.events, tt.args.course); !reflect.DeepEqual(got, tt.want) { + t.Errorf("generateUUIDs() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/backend/service/ical/ical.go b/backend/service/ical/ical.go index a0ad8d4..6ff6372 100644 --- a/backend/service/ical/ical.go +++ b/backend/service/ical/ical.go @@ -16,8 +16,6 @@ import ( const expirationTime = 5 * time.Minute func Feed(c echo.Context, app *pocketbase.PocketBase, token string) error { - layout := "2006-01-02 15:04:05 -0700 MST" - var result string var responseWriter = c.Response().Writer feed, err := db.FindFeedByToken(token, app) @@ -25,11 +23,11 @@ func Feed(c echo.Context, app *pocketbase.PocketBase, token string) error { return c.JSON(http.StatusNotFound, err) } - created, _ := time.Parse(layout, feed.Created) + created := feed.Created var modules []model.FeedCollection _ = json.Unmarshal([]byte(feed.Modules), &modules) - if created.Add(time.Hour * 265).Before(time.Now()) { + if created.Time().Add(time.Hour * 265).Before(time.Now()) { newFeed, err := createFeedForToken(app, modules) if err != nil { return c.JSON(http.StatusInternalServerError, err) diff --git a/backend/service/ical/icalJsonMigrate.go b/backend/service/ical/icalJsonMigrate.go new file mode 100644 index 0000000..94ac33d --- /dev/null +++ b/backend/service/ical/icalJsonMigrate.go @@ -0,0 +1,58 @@ +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/frontend/src/api/fetchCourse.ts b/frontend/src/api/fetchCourse.ts index 3f23516..b641635 100644 --- a/frontend/src/api/fetchCourse.ts +++ b/frontend/src/api/fetchCourse.ts @@ -27,6 +27,7 @@ export async function fetchModulesByCourseAndSemester( modulesResponse.forEach((module: Module) => modules.push( new Module( + module.uuid, module.name, course, module.name, @@ -50,6 +51,7 @@ export async function fetchAllModules(): Promise { responseModules.forEach((module: Module) => { modules.push( new Module( + module.uuid, module.name, module.course, module.name, diff --git a/frontend/src/api/fetchModule.ts b/frontend/src/api/fetchModule.ts index 17a7ec9..a6b2d86 100644 --- a/frontend/src/api/fetchModule.ts +++ b/frontend/src/api/fetchModule.ts @@ -16,6 +16,7 @@ export async function fetchModule(name: string): Promise { .then( (module: Module) => new Module( + module.uuid, module.name, module.course, module.name, diff --git a/frontend/src/components/AdditionalModules.vue b/frontend/src/components/AdditionalModules.vue index 7074611..c989cb1 100644 --- a/frontend/src/components/AdditionalModules.vue +++ b/frontend/src/components/AdditionalModules.vue @@ -39,7 +39,7 @@ const ModuleInformation = defineAsyncComponent( ); async function showInfo(moduleName: string) { - const module: Ref = ref(new Module("", "", "", "", "", [])); + const module: Ref = ref(new Module("", "", "", "", "", "", [])); await fetchModule(moduleName).then((data) => { module.value = data; }); diff --git a/frontend/src/components/editCalendar/EditAdditionalModules.vue b/frontend/src/components/editCalendar/EditAdditionalModules.vue index 9f696e8..b19f63e 100644 --- a/frontend/src/components/editCalendar/EditAdditionalModules.vue +++ b/frontend/src/components/editCalendar/EditAdditionalModules.vue @@ -37,7 +37,7 @@ const ModuleInformation = defineAsyncComponent( ); async function showInfo(moduleName: string) { - const module: Ref = ref(new Module("", "", "", "", "", [])); + const module: Ref = ref(new Module("", "", "", "", "", "", [])); await fetchModule(moduleName).then((data) => { module.value = data; }); diff --git a/frontend/src/model/module.ts b/frontend/src/model/module.ts index 8be7e35..9633c9d 100644 --- a/frontend/src/model/module.ts +++ b/frontend/src/model/module.ts @@ -2,6 +2,7 @@ import { Event } from "./event"; export class Module { constructor( + public uuid: string, public name: string, public course: string, public userDefinedName: string,