diff --git a/.gitignore b/.gitignore index 525d280..2ba9c4f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ .idea/ +.vscode/ .DS_Store diff --git a/backend/model/icalModel.go b/backend/model/icalModel.go index 2060712..53b7aa6 100644 --- a/backend/model/icalModel.go +++ b/backend/model/icalModel.go @@ -27,4 +27,5 @@ type FeedCollection struct { Name string `db:"Name" json:"name"` Course string `db:"course" json:"course"` UserDefinedName string `db:"userDefinedName" json:"userDefinedName"` + Reminder bool `db:"reminder" json:"reminder"` } diff --git a/backend/service/db/dbEvents.go b/backend/service/db/dbEvents.go index acad6af..47f59db 100644 --- a/backend/service/db/dbEvents.go +++ b/backend/service/db/dbEvents.go @@ -118,18 +118,23 @@ func buildIcalQueryForModules(modules []model.FeedCollection) dbx.Expression { // GetPlanForModules returns all events for the given modules with the given course // used for the ical feed -func GetPlanForModules(app *pocketbase.PocketBase, modules []model.FeedCollection) model.Events { +func GetPlanForModules(app *pocketbase.PocketBase, modules map[string]model.FeedCollection) model.Events { var events model.Events + modulesArray := make([]model.FeedCollection, 0, len(modules)) + for _, value := range modules { + modulesArray = append(modulesArray, value) + } + // iterate over modules in 100 batch sizes for i := 0; i < len(modules); i += 100 { var moduleBatch []model.FeedCollection if i+100 > len(modules) { - moduleBatch = modules[i:] + moduleBatch = modulesArray[i:] } else { - moduleBatch = modules[i : i+100] + moduleBatch = modulesArray[i : i+100] } var selectedModulesQuery = buildIcalQueryForModules(moduleBatch) diff --git a/backend/service/ical/ical.go b/backend/service/ical/ical.go index baee837..4fa472b 100644 --- a/backend/service/ical/ical.go +++ b/backend/service/ical/ical.go @@ -3,14 +3,15 @@ package ical import ( "bytes" "encoding/json" - "github.com/jordic/goics" - "github.com/labstack/echo/v5" - "github.com/pocketbase/pocketbase" - "github.com/pocketbase/pocketbase/apis" "htwkalender/model" "htwkalender/service/db" "net/http" "time" + + "github.com/jordic/goics" + "github.com/labstack/echo/v5" + "github.com/pocketbase/pocketbase" + "github.com/pocketbase/pocketbase/apis" ) const expirationTime = 5 * time.Minute @@ -23,8 +24,13 @@ func Feed(c echo.Context, app *pocketbase.PocketBase, token string) error { return c.JSON(http.StatusNotFound, err) } - var modules []model.FeedCollection - _ = json.Unmarshal([]byte(feed.Modules), &modules) + modules := make(map[string]model.FeedCollection) + var modulesArray []model.FeedCollection + _ = json.Unmarshal([]byte(feed.Modules), &modulesArray) + + for _, module := range modulesArray { + modules[module.UUID] = module + } newFeed, err := createFeedForToken(app, modules) if err != nil { @@ -41,7 +47,7 @@ func Feed(c echo.Context, app *pocketbase.PocketBase, token string) error { return nil } -func createFeedForToken(app *pocketbase.PocketBase, modules []model.FeedCollection) (*model.FeedModel, error) { +func createFeedForToken(app *pocketbase.PocketBase, modules map[string]model.FeedCollection) (*model.FeedModel, error) { res := db.GetPlanForModules(app, modules) b := bytes.Buffer{} goics.NewICalEncode(&b).Encode(IcalModel{Events: res, Mapping: modules}) diff --git a/backend/service/ical/icalFileGeneration.go b/backend/service/ical/icalFileGeneration.go index 151ed7c..2c16380 100644 --- a/backend/service/ical/icalFileGeneration.go +++ b/backend/service/ical/icalFileGeneration.go @@ -12,7 +12,7 @@ import ( // IcalModel local type for EmitICal function type IcalModel struct { Events model.Events - Mapping []model.FeedCollection + Mapping map[string]model.FeedCollection } // EmitICal implements the interface for goics @@ -27,13 +27,22 @@ func (icalModel IcalModel) EmitICal() goics.Componenter { c.AddProperty("X-WR-TIMEZONE", "Europe/Berlin") c.AddProperty("X-LIC-LOCATION", "Europe/Berlin") for _, event := range icalModel.Events { + mapEntry, mappingFound := icalModel.Mapping[event.UUID] + s := goics.NewComponent() s.SetType("VEVENT") k, v := goics.FormatDateTime("DTEND;TZID=Europe/Berlin", event.End.Time().Local().In(europeTime)) s.AddProperty(k, v) k, v = goics.FormatDateTime("DTSTART;TZID=Europe/Berlin", event.Start.Time().Local().In(europeTime)) s.AddProperty(k, v) - addPropertyIfNotEmpty(s, "SUMMARY", replaceNameIfUserDefined(&event, icalModel.Mapping)) + + if mappingFound { + addPropertyIfNotEmpty(s, "SUMMARY", replaceNameIfUserDefined(&event, mapEntry)) + addAlarmIfSpecified(s, event, mapEntry) + } else { + addPropertyIfNotEmpty(s, "SUMMARY", event.Name) + } + addPropertyIfNotEmpty(s, "DESCRIPTION", generateDescription(event)) addPropertyIfNotEmpty(s, "LOCATION", event.Rooms) c.AddComponent(s) @@ -41,14 +50,25 @@ func (icalModel IcalModel) EmitICal() goics.Componenter { return c } +// if reminder is specified in the configuration for this event, an alarm will be added to the event +func addAlarmIfSpecified(s *goics.Component, event model.Event, mapping model.FeedCollection) { + if mapping.Reminder { + a := goics.NewComponent() + a.SetType("VALARM") + a.AddProperty("TRIGGER", "-PT15M") + a.AddProperty("ACTION", "DISPLAY") + a.AddProperty("DESCRIPTION", "Next course: "+replaceNameIfUserDefined(&event, mapping)+" in "+event.Rooms) + s.AddComponent(a) + } +} + // replaceNameIfUserDefined replaces the name of the event with the user defined name if it is not empty // all contained template strings will be replaced with the corresponding values from the event -func replaceNameIfUserDefined(event *model.Event, mapping []model.FeedCollection) string { - for _, mapEntry := range mapping { - if mapEntry.Name == event.Name && !functions.OnlyWhitespace(mapEntry.UserDefinedName) { - return names.ReplaceTemplateSubStrings(mapEntry.UserDefinedName, *event) - } +func replaceNameIfUserDefined(event *model.Event, mapping model.FeedCollection) string { + if !functions.OnlyWhitespace(mapping.UserDefinedName) { + return names.ReplaceTemplateSubStrings(mapping.UserDefinedName, *event) } + return event.Name } diff --git a/frontend/src/components/ModuleTemplateDialog.vue b/frontend/src/components/ModuleTemplateDialog.vue index cdb764b..c03b238 100644 --- a/frontend/src/components/ModuleTemplateDialog.vue +++ b/frontend/src/components/ModuleTemplateDialog.vue @@ -21,7 +21,7 @@ const placeholders = ref([ @click="helpVisible = true" />

Here you can rename your modules to your liking. This will be the name @@ -37,6 +37,10 @@ const placeholders = ref([ +

+ Additionally, you can toggle notifications for each module. + If you do so, you will be notified 15 minutes before the event starts. +

diff --git a/frontend/src/components/RenameModules.vue b/frontend/src/components/RenameModules.vue index 10f3f0b..6205344 100644 --- a/frontend/src/components/RenameModules.vue +++ b/frontend/src/components/RenameModules.vue @@ -18,6 +18,7 @@ const tableData = ref( const columns = ref([ { field: "Course", header: "Course" }, { field: "Module", header: "Module" }, + { field: "Reminder", header: "Reminder"} ]); async function finalStep() { @@ -30,7 +31,7 @@ async function finalStep() {