Merge branch 'main' into 11-enhanced-module-titles

# Conflicts:
#	backend/model/eventModel.go
#	backend/service/fetch/fetchSeminarEventService.go
This commit is contained in:
masterelmar
2023-10-30 13:57:16 +01:00
15 changed files with 706 additions and 84 deletions

View File

@@ -0,0 +1,114 @@
package migrations
import (
"encoding/json"
"github.com/pocketbase/dbx"
"github.com/pocketbase/pocketbase/daos"
m "github.com/pocketbase/pocketbase/migrations"
"github.com/pocketbase/pocketbase/models/schema"
)
func init() {
m.Register(func(db dbx.Builder) error {
dao := daos.New(db)
collection, err := dao.FindCollectionByNameOrId("7her4515qsmrxe8")
if err != nil {
return err
}
// remove
collection.Schema.RemoveField("7vsr9h6p")
// remove
collection.Schema.RemoveField("wwpokofe")
// add
new_start := &schema.SchemaField{}
json.Unmarshal([]byte(`{
"system": false,
"id": "tvxitgwc",
"name": "start",
"type": "date",
"required": false,
"presentable": false,
"unique": false,
"options": {
"min": "",
"max": ""
}
}`), new_start)
collection.Schema.AddField(new_start)
// add
new_end := &schema.SchemaField{}
json.Unmarshal([]byte(`{
"system": false,
"id": "trbmsfcz",
"name": "end",
"type": "date",
"required": false,
"presentable": false,
"unique": false,
"options": {
"min": "",
"max": ""
}
}`), new_end)
collection.Schema.AddField(new_end)
return dao.SaveCollection(collection)
}, func(db dbx.Builder) error {
dao := daos.New(db)
collection, err := dao.FindCollectionByNameOrId("7her4515qsmrxe8")
if err != nil {
return err
}
// add
del_Start := &schema.SchemaField{}
json.Unmarshal([]byte(`{
"system": false,
"id": "7vsr9h6p",
"name": "Start",
"type": "text",
"required": false,
"presentable": false,
"unique": false,
"options": {
"min": null,
"max": null,
"pattern": ""
}
}`), del_Start)
collection.Schema.AddField(del_Start)
// add
del_End := &schema.SchemaField{}
json.Unmarshal([]byte(`{
"system": false,
"id": "wwpokofe",
"name": "End",
"type": "text",
"required": false,
"presentable": false,
"unique": false,
"options": {
"min": null,
"max": null,
"pattern": ""
}
}`), del_End)
collection.Schema.AddField(del_End)
// remove
collection.Schema.RemoveField("tvxitgwc")
// remove
collection.Schema.RemoveField("trbmsfcz")
return dao.SaveCollection(collection)
})
}

View File

@@ -0,0 +1,417 @@
package migrations
import (
"encoding/json"
"github.com/pocketbase/dbx"
"github.com/pocketbase/pocketbase/daos"
m "github.com/pocketbase/pocketbase/migrations"
"github.com/pocketbase/pocketbase/models"
)
func init() {
m.Register(func(db dbx.Builder) error {
jsonData := `[
{
"id": "cfq9mqlmd97v8z5",
"created": "2023-09-19 17:31:15.957Z",
"updated": "2023-10-27 22:15:09.073Z",
"name": "groups",
"type": "base",
"system": false,
"schema": [
{
"system": false,
"id": "85msl21p",
"name": "university",
"type": "text",
"required": false,
"presentable": false,
"unique": false,
"options": {
"min": null,
"max": null,
"pattern": ""
}
},
{
"system": false,
"id": "2sii4dtp",
"name": "shortcut",
"type": "text",
"required": false,
"presentable": false,
"unique": false,
"options": {
"min": null,
"max": null,
"pattern": ""
}
},
{
"system": false,
"id": "uiwgo28f",
"name": "groupId",
"type": "text",
"required": false,
"presentable": false,
"unique": false,
"options": {
"min": null,
"max": null,
"pattern": ""
}
},
{
"system": false,
"id": "y0l1lrzs",
"name": "course",
"type": "text",
"required": false,
"presentable": false,
"unique": false,
"options": {
"min": null,
"max": null,
"pattern": ""
}
},
{
"system": false,
"id": "kr62mhbz",
"name": "faculty",
"type": "text",
"required": false,
"presentable": false,
"unique": false,
"options": {
"min": null,
"max": null,
"pattern": ""
}
},
{
"system": false,
"id": "ya6znpez",
"name": "facultyId",
"type": "text",
"required": false,
"presentable": false,
"unique": 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",
"created": "2023-09-19 17:31:15.957Z",
"updated": "2023-10-27 22:15:09.073Z",
"name": "feeds",
"type": "base",
"system": false,
"schema": [
{
"system": false,
"id": "cowxjfmc",
"name": "modules",
"type": "json",
"required": true,
"presentable": false,
"unique": false,
"options": {}
}
],
"indexes": [],
"listRule": null,
"viewRule": "",
"createRule": null,
"updateRule": "",
"deleteRule": null,
"options": {}
},
{
"id": "7her4515qsmrxe8",
"created": "2023-09-19 17:31:15.958Z",
"updated": "2023-10-27 22:16:26.924Z",
"name": "events",
"type": "base",
"system": false,
"schema": [
{
"system": false,
"id": "m8ne8e3m",
"name": "Day",
"type": "text",
"required": false,
"presentable": false,
"unique": false,
"options": {
"min": null,
"max": null,
"pattern": ""
}
},
{
"system": false,
"id": "xnsxqp7j",
"name": "Week",
"type": "text",
"required": false,
"presentable": false,
"unique": false,
"options": {
"min": null,
"max": null,
"pattern": ""
}
},
{
"system": false,
"id": "aeuskrjo",
"name": "Name",
"type": "text",
"required": false,
"presentable": false,
"unique": false,
"options": {
"min": null,
"max": null,
"pattern": ""
}
},
{
"system": false,
"id": "klrzqyw0",
"name": "EventType",
"type": "text",
"required": false,
"presentable": false,
"unique": false,
"options": {
"min": null,
"max": null,
"pattern": ""
}
},
{
"system": false,
"id": "5zltexoy",
"name": "Prof",
"type": "text",
"required": false,
"presentable": false,
"unique": false,
"options": {
"min": null,
"max": null,
"pattern": ""
}
},
{
"system": false,
"id": "gy3nvfmx",
"name": "Rooms",
"type": "text",
"required": false,
"presentable": false,
"unique": false,
"options": {
"min": null,
"max": null,
"pattern": ""
}
},
{
"system": false,
"id": "hn7b8dfy",
"name": "Notes",
"type": "text",
"required": false,
"presentable": false,
"unique": false,
"options": {
"min": null,
"max": null,
"pattern": ""
}
},
{
"system": false,
"id": "axskpwm8",
"name": "BookedAt",
"type": "text",
"required": false,
"presentable": false,
"unique": false,
"options": {
"min": null,
"max": null,
"pattern": ""
}
},
{
"system": false,
"id": "vyyefxp7",
"name": "course",
"type": "text",
"required": false,
"presentable": false,
"unique": false,
"options": {
"min": null,
"max": null,
"pattern": ""
}
},
{
"system": false,
"id": "vlbpm9fz",
"name": "semester",
"type": "text",
"required": false,
"presentable": false,
"unique": false,
"options": {
"min": null,
"max": null,
"pattern": ""
}
},
{
"system": false,
"id": "0kahthzr",
"name": "uuid",
"type": "text",
"required": false,
"presentable": false,
"unique": false,
"options": {
"min": null,
"max": null,
"pattern": ""
}
},
{
"system": false,
"id": "tvxitgwc",
"name": "start",
"type": "date",
"required": false,
"presentable": false,
"unique": false,
"options": {
"min": "",
"max": ""
}
},
{
"system": false,
"id": "trbmsfcz",
"name": "end",
"type": "date",
"required": false,
"presentable": false,
"unique": false,
"options": {
"min": "",
"max": ""
}
}
],
"indexes": [
"CREATE UNIQUE INDEX ` + "`" + `idx_orp1NWL` + "`" + ` ON ` + "`" + `events` + "`" + ` (\n ` + "`" + `Day` + "`" + `,\n ` + "`" + `Week` + "`" + `,\n ` + "`" + `Start` + "`" + `,\n ` + "`" + `End` + "`" + `,\n ` + "`" + `Name` + "`" + `,\n ` + "`" + `course` + "`" + `,\n ` + "`" + `Prof` + "`" + `,\n ` + "`" + `Rooms` + "`" + `,\n ` + "`" + `EventType` + "`" + `\n)"
],
"listRule": null,
"viewRule": null,
"createRule": null,
"updateRule": null,
"deleteRule": null,
"options": {}
},
{
"id": "_pb_users_auth_",
"created": "2023-10-27 22:15:08.738Z",
"updated": "2023-10-27 22:15:09.074Z",
"name": "users",
"type": "auth",
"system": false,
"schema": [
{
"system": false,
"id": "users_name",
"name": "name",
"type": "text",
"required": false,
"presentable": false,
"unique": false,
"options": {
"min": null,
"max": null,
"pattern": ""
}
},
{
"system": false,
"id": "users_avatar",
"name": "avatar",
"type": "file",
"required": false,
"presentable": false,
"unique": 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
}
}
]`
collections := []*models.Collection{}
if err := json.Unmarshal([]byte(jsonData), &collections); err != nil {
return err
}
return daos.New(db).ImportCollections(collections, true, nil)
}, func(db dbx.Builder) error {
return nil
})
}

View File

@@ -4,6 +4,7 @@ import (
"slices" "slices"
"github.com/pocketbase/pocketbase/models" "github.com/pocketbase/pocketbase/models"
"github.com/pocketbase/pocketbase/tools/types"
) )
type Events []Event type Events []Event
@@ -13,20 +14,20 @@ func (m Events) Contains(event Event) bool {
} }
type Event struct { type Event struct {
UUID string `db:"uuid" json:"uuid"` UUID string `db:"uuid" json:"uuid"`
Day string `db:"Day" json:"day"` Day string `db:"Day" json:"day"`
Week string `db:"Week" json:"week"` Week string `db:"Week" json:"week"`
Start string `db:"Start" json:"start"` Start types.DateTime `db:"start" json:"start"`
End string `db:"End" json:"end"` End types.DateTime `db:"end" json:"end"`
Name string `db:"Name" json:"name"` Name string `db:"Name" json:"name"`
EventType string `db:"EventType" json:"eventType"` EventType string `db:"EventType" json:"eventType"`
Compulsory string `db:"Compulsory" json:"compulsory"` Compulsory string `db:"Compulsory" json:"compulsory"`
Prof string `db:"Prof" json:"prof"` Prof string `db:"Prof" json:"prof"`
Rooms string `db:"Rooms" json:"rooms"` Rooms string `db:"Rooms" json:"rooms"`
Notes string `db:"Notes" json:"notes"` Notes string `db:"Notes" json:"notes"`
BookedAt string `db:"BookedAt" json:"bookedAt"` BookedAt string `db:"BookedAt" json:"bookedAt"`
Course string `db:"course" json:"course"` Course string `db:"course" json:"course"`
Semester string `db:"semester" json:"semester"` Semester string `db:"semester" json:"semester"`
models.BaseModel models.BaseModel
} }

View File

@@ -1,8 +1,10 @@
package model package model
import ( import (
"github.com/pocketbase/pocketbase/models"
"testing" "testing"
"github.com/pocketbase/pocketbase/models"
"github.com/pocketbase/pocketbase/tools/types"
) )
func TestEvents_Contains(t *testing.T) { func TestEvents_Contains(t *testing.T) {
@@ -23,20 +25,20 @@ func TestEvents_Contains(t *testing.T) {
}, },
{ {
name: "one event", name: "one event",
m: Events{{Day: "test", Week: "test", Start: "test", End: "test", Name: "test", Course: "test", Prof: "test", Rooms: "test", EventType: "test"}}, m: Events{{Day: "test", Week: "test", Start: types.NowDateTime(), End: types.NowDateTime(), Name: "test", Course: "test", Prof: "test", Rooms: "test", EventType: "test"}},
args: args{event: Event{Day: "test", Week: "test", Start: "test", End: "test", Name: "test", Course: "test", Prof: "test", Rooms: "test", EventType: "test"}}, args: args{event: Event{Day: "test", Week: "test", Start: types.NowDateTime(), End: types.NowDateTime(), Name: "test", Course: "test", Prof: "test", Rooms: "test", EventType: "test"}},
want: true, want: true,
}, },
{ {
name: "two events", name: "two events",
m: Events{{Day: "test", Week: "test", Start: "test", End: "test", Name: "test", Course: "test", Prof: "test", Rooms: "test", EventType: "test"}, {Day: "test2", Week: "test2", Start: "test2", End: "test2", Name: "test2", Course: "test2", Prof: "test2", Rooms: "test2", EventType: "test2"}}, m: Events{{Day: "test", Week: "test", Start: types.NowDateTime(), End: types.NowDateTime(), Name: "test", Course: "test", Prof: "test", Rooms: "test", EventType: "test"}, {Day: "test2", Week: "test2", Start: types.NowDateTime(), End: types.NowDateTime(), Name: "test2", Course: "test2", Prof: "test2", Rooms: "test2", EventType: "test2"}},
args: args{event: Event{Day: "test2", Week: "test2", Start: "test2", End: "test2", Name: "test2", Course: "test2", Prof: "test2", Rooms: "test2", EventType: "test2"}}, args: args{event: Event{Day: "test2", Week: "test2", Start: types.NowDateTime(), End: types.NowDateTime(), Name: "test2", Course: "test2", Prof: "test2", Rooms: "test2", EventType: "test2"}},
want: true, want: true,
}, },
{ {
name: "two events with different values", name: "two events with different values",
m: Events{{Day: "test", Week: "test", Start: "test", End: "test", Name: "test", Course: "test", Prof: "test", Rooms: "test", EventType: "test", UUID: "439ßu56rf8u9ijn4f4-2345345"}, {Day: "test2", Week: "test2", Start: "test2", End: "test2", Name: "test2", Course: "test2", Prof: "test2", Rooms: "test2", EventType: "test2", UUID: "432a39ßu545349ijn4f4-23dsa45"}}, m: Events{{Day: "test", Week: "test", Start: types.NowDateTime(), End: types.NowDateTime(), Name: "test", Course: "test", Prof: "test", Rooms: "test", EventType: "test", UUID: "439ßu56rf8u9ijn4f4-2345345"}, {Day: "test2", Week: "test2", Start: types.NowDateTime(), End: types.NowDateTime(), Name: "test2", Course: "test2", Prof: "test2", Rooms: "test2", EventType: "test2", UUID: "432a39ßu545349ijn4f4-23dsa45"}},
args: args{event: Event{Day: "test3", Week: "test3", Start: "test3", End: "test3", Name: "test3", Course: "test3", Prof: "test3", Rooms: "test3", EventType: "test3", UUID: "934mf43r34f-g68h7655tg3"}}, args: args{event: Event{Day: "test3", Week: "test3", Start: types.NowDateTime(), End: types.NowDateTime(), Name: "test3", Course: "test3", Prof: "test3", Rooms: "test3", EventType: "test3", UUID: "934mf43r34f-g68h7655tg3"}},
want: false, want: false,
}, },
} }
@@ -54,8 +56,8 @@ func TestEvent_Equals(t *testing.T) {
UUID string UUID string
Day string Day string
Week string Week string
Start string Start types.DateTime
End string End types.DateTime
Name string Name string
EventType string EventType string
Prof string Prof string
@@ -83,20 +85,20 @@ func TestEvent_Equals(t *testing.T) {
}, },
{ {
name: "one empty one not", name: "one empty one not",
fields: fields{Day: "test", Week: "test", Start: "test", End: "test", Name: "test", Course: "test", Prof: "test", Rooms: "test", EventType: "test"}, fields: fields{Day: "test", Week: "test", Start: types.NowDateTime(), End: types.NowDateTime(), Name: "test", Course: "test", Prof: "test", Rooms: "test", EventType: "test"},
args: args{event: Event{}}, args: args{event: Event{}},
want: false, want: false,
}, },
{ {
name: "one event", name: "one event",
fields: fields{Day: "test", Week: "test", Start: "test", End: "test", Name: "test", Course: "test", Prof: "test", Rooms: "test", EventType: "test"}, fields: fields{Day: "test", Week: "test", Start: types.NowDateTime(), End: types.NowDateTime(), Name: "test", Course: "test", Prof: "test", Rooms: "test", EventType: "test"},
args: args{event: Event{Day: "test", Week: "test", Start: "test", End: "test", Name: "test", Course: "test", Prof: "test", Rooms: "test", EventType: "test"}}, args: args{event: Event{Day: "test", Week: "test", Start: types.NowDateTime(), End: types.NowDateTime(), Name: "test", Course: "test", Prof: "test", Rooms: "test", EventType: "test"}},
want: true, want: true,
}, },
{ {
name: "two events", name: "two events",
fields: fields{Day: "test", Week: "test", Start: "test", End: "test", Name: "test", Course: "test", Prof: "test", Rooms: "test", EventType: "test"}, fields: fields{Day: "test", Week: "test", Start: types.NowDateTime(), End: types.NowDateTime(), Name: "test", Course: "test", Prof: "test", Rooms: "test", EventType: "test"},
args: args{event: Event{Day: "test2", Week: "test2", Start: "test2", End: "test2", Name: "test2", Course: "test2", Prof: "test2", Rooms: "test2", EventType: "test2"}}, args: args{event: Event{Day: "test2", Week: "test2", Start: types.NowDateTime(), End: types.NowDateTime(), Name: "test2", Course: "test2", Prof: "test2", Rooms: "test2", EventType: "test2"}},
want: false, want: false,
}, },
} }

View File

@@ -1,17 +1,18 @@
package service package service
import ( import (
"github.com/labstack/echo/v5" "htwkalender/model"
"github.com/pocketbase/pocketbase"
"github.com/pocketbase/pocketbase/apis"
"github.com/pocketbase/pocketbase/core"
"htwkalender/service/events" "htwkalender/service/events"
"htwkalender/service/fetch" "htwkalender/service/fetch"
"htwkalender/service/ical" "htwkalender/service/ical"
"htwkalender/service/room" "htwkalender/service/room"
"io" "io"
"net/http" "net/http"
"net/url"
"github.com/labstack/echo/v5"
"github.com/pocketbase/pocketbase"
"github.com/pocketbase/pocketbase/apis"
"github.com/pocketbase/pocketbase/core"
) )
func AddRoutes(app *pocketbase.PocketBase) { func AddRoutes(app *pocketbase.PocketBase) {
@@ -191,12 +192,17 @@ func AddRoutes(app *pocketbase.PocketBase) {
app.OnBeforeServe().Add(func(e *core.ServeEvent) error { app.OnBeforeServe().Add(func(e *core.ServeEvent) error {
_, err := e.Router.AddRoute(echo.Route{ _, err := e.Router.AddRoute(echo.Route{
Method: http.MethodGet, Method: http.MethodPost,
Path: "/api/module", Path: "/api/module",
Handler: func(c echo.Context) error { Handler: func(c echo.Context) error {
name := c.Request().Header.Get("Name")
name, err := url.QueryUnescape(name) var requestModule model.Module
module, err := events.GetModuleByName(app, name)
if err := c.Bind(&requestModule); err != nil {
return apis.NewBadRequestError("Failed to read request body", err)
}
module, err := events.GetModuleByName(app, requestModule)
if err != nil { if err != nil {
return c.JSON(400, err) return c.JSON(400, err)
@@ -248,6 +254,7 @@ func AddRoutes(app *pocketbase.PocketBase) {
}, },
Middlewares: []echo.MiddlewareFunc{ Middlewares: []echo.MiddlewareFunc{
apis.ActivityLogger(app), apis.ActivityLogger(app),
apis.RequireAdminAuth(),
}, },
}) })
if err != nil { if err != nil {
@@ -271,6 +278,7 @@ func AddRoutes(app *pocketbase.PocketBase) {
}, },
Middlewares: []echo.MiddlewareFunc{ Middlewares: []echo.MiddlewareFunc{
apis.ActivityLogger(app), apis.ActivityLogger(app),
apis.RequireAdminAuth(),
}, },
}) })
if err != nil { if err != nil {

View File

@@ -4,7 +4,8 @@ import "time"
func GetDateFromWeekNumber(year int, weekNumber int, dayName string) (time.Time, error) { func GetDateFromWeekNumber(year int, weekNumber int, dayName string) (time.Time, error) {
// Create a time.Date for the first day of the year // Create a time.Date for the first day of the year
firstDayOfYear := time.Date(year, time.January, 1, 0, 0, 0, 0, time.UTC) europeTime, _ := time.LoadLocation("Europe/Berlin")
firstDayOfYear := time.Date(year, time.January, 1, 0, 0, 0, 0, europeTime)
// Calculate the number of days to add to reach the desired week // Calculate the number of days to add to reach the desired week
daysToAdd := time.Duration((weekNumber-1)*7) * 24 * time.Hour daysToAdd := time.Duration((weekNumber-1)*7) * 24 * time.Hour

View File

@@ -191,10 +191,10 @@ func DeleteAllEvents(app *pocketbase.PocketBase) error {
return nil return nil
} }
func FindAllEventsByModule(app *pocketbase.PocketBase, moduleName string) (model.Events, error) { func FindAllEventsByModule(app *pocketbase.PocketBase, module model.Module) (model.Events, error) {
var events model.Events var events model.Events
err := app.Dao().DB().Select("*").From("events").Where(dbx.NewExp("Name = {:moduleName}", dbx.Params{"moduleName": moduleName})).All(&events) err := app.Dao().DB().Select("*").From("events").Where(dbx.NewExp("Name = {:moduleName} AND course = {:course}", dbx.Params{"moduleName": module.Name, "course": module.Course})).All(&events)
if err != nil { if err != nil {
print("Error while getting events from database: ", err) print("Error while getting events from database: ", err)
return nil, err return nil, err

View File

@@ -46,15 +46,15 @@ func GetAllModulesDistinct(app *pocketbase.PocketBase, c echo.Context) error {
// If the module does not exist, an error is returned // If the module does not exist, an error is returned
// If the module exists, the module is returned // If the module exists, the module is returned
// Module is a struct that exists in database as events // Module is a struct that exists in database as events
func GetModuleByName(app *pocketbase.PocketBase, name string) (model.Module, error) { func GetModuleByName(app *pocketbase.PocketBase, module model.Module) (model.Module, error) {
events, err := db.FindAllEventsByModule(app, name) events, err := db.FindAllEventsByModule(app, module)
if err != nil || len(events) == 0 { if err != nil || len(events) == 0 {
return model.Module{}, err return model.Module{}, err
} else { } else {
return model.Module{ return model.Module{
UUID: events[0].UUID, UUID: events[0].UUID,
Name: name, Name: events[0].Name,
Events: events, Events: events,
Prof: events[0].Prof, Prof: events[0].Prof,
Course: events[0].Course, Course: events[0].Course,

View File

@@ -13,10 +13,10 @@ import (
"time" "time"
"github.com/google/uuid" "github.com/google/uuid"
"github.com/labstack/echo/v5" "github.com/labstack/echo/v5"
"github.com/pocketbase/pocketbase" "github.com/pocketbase/pocketbase"
"github.com/pocketbase/pocketbase/apis" "github.com/pocketbase/pocketbase/apis"
"github.com/pocketbase/pocketbase/tools/types"
"golang.org/x/net/html" "golang.org/x/net/html"
) )
@@ -142,27 +142,23 @@ func convertWeeksToDates(events []model.Event, semester string, year string) []m
// for each event we need to calculate the start and end date based on the week and the year // for each event we need to calculate the start and end date based on the week and the year
for _, event := range events { for _, event := range events {
eventWeek, _ := strconv.Atoi(event.Week) eventWeek, _ := strconv.Atoi(event.Week)
eventDay, _ := date.GetDateFromWeekNumber(eventYear, eventWeek, event.Day) eventDay, _ := date.GetDateFromWeekNumber(eventYear, eventWeek, event.Day)
start := addTimeToDate(eventDay, event.Start) start := replaceTimeForDate(eventDay, event.Start.Time())
end := addTimeToDate(eventDay, event.End) end := replaceTimeForDate(eventDay, event.End.Time())
newEvent := event newEvent := event
newEvent.Start = start.String() newEvent.Start, _ = types.ParseDateTime(start.In(time.UTC))
newEvent.End = end.String() newEvent.End, _ = types.ParseDateTime(end.In(time.UTC))
newEvent.Semester = semester newEvent.Semester = semester
newEvents = append(newEvents, newEvent) newEvents = append(newEvents, newEvent)
} }
return newEvents return newEvents
} }
func addTimeToDate(date time.Time, timeString string) time.Time { // replaceTimeForDate replaces hour, minute, second, nsec for the selected date
europeTime, _ := time.LoadLocation("Europe/Berlin") func replaceTimeForDate(date time.Time, replacementTime time.Time) time.Time {
//convert time functions to time return time.Date(date.Year(), date.Month(), date.Day(), replacementTime.Hour(), replacementTime.Minute(), replacementTime.Second(), replacementTime.Nanosecond(), date.Location())
timeParts := strings.Split(timeString, ":")
hour, _ := strconv.Atoi(timeParts[0])
minute, _ := strconv.Atoi(timeParts[1])
return time.Date(date.Year(), date.Month(), date.Day(), hour, minute, 0, 0, europeTime)
} }
func extractSemesterAndYear(semesterString string) (string, string) { func extractSemesterAndYear(semesterString string) (string, string) {
@@ -206,11 +202,13 @@ func toEvents(tables [][]*html.Node, days []string) []model.Event {
tableData := findTableData(tables[table][row]) tableData := findTableData(tables[table][row])
if len(tableData) > 0 { if len(tableData) > 0 {
start, _ := types.ParseDateTime(createTimeFromHourAndMinuteString(getTextContent(tableData[1])))
end, _ := types.ParseDateTime(createTimeFromHourAndMinuteString(getTextContent(tableData[2])))
events = append(events, model.Event{ events = append(events, model.Event{
Day: days[table], Day: days[table],
Week: getTextContent(tableData[0]), Week: getTextContent(tableData[0]),
Start: getTextContent(tableData[1]), Start: start,
End: getTextContent(tableData[2]), End: end,
Name: getTextContent(tableData[3]), Name: getTextContent(tableData[3]),
EventType: getTextContent(tableData[4]), EventType: getTextContent(tableData[4]),
Prof: getTextContent(tableData[5]), Prof: getTextContent(tableData[5]),
@@ -226,6 +224,16 @@ func toEvents(tables [][]*html.Node, days []string) []model.Event {
return events return events
} }
// createEventFromTableData should create an event from the table data
// tableTime represents Hour and Minute like HH:MM
// tableDate returns a Time
func createTimeFromHourAndMinuteString(tableTime string) time.Time {
timeParts := strings.Split(tableTime, ":")
hour, _ := strconv.Atoi(timeParts[0])
minute, _ := strconv.Atoi(timeParts[1])
return time.Date(0, 0, 0, hour, minute, 0, 0, time.UTC)
}
func splitEventsByWeek(events []model.Event) []model.Event { func splitEventsByWeek(events []model.Event) []model.Event {
var newEvents []model.Event var newEvents []model.Event

View File

@@ -4,6 +4,7 @@ import (
"htwkalender/model" "htwkalender/model"
"reflect" "reflect"
"testing" "testing"
"time"
) )
func Test_extractSemesterAndYear(t *testing.T) { func Test_extractSemesterAndYear(t *testing.T) {
@@ -262,3 +263,80 @@ func Test_generateUUIDs(t *testing.T) {
}) })
} }
} }
func Test_createTimeFromHourAndMinuteString(t *testing.T) {
europeTime, _ := time.LoadLocation("Europe/Berlin")
type args struct {
tableTime string
}
tests := []struct {
name string
args args
want time.Time
}{
{
name: "Test 1",
args: args{
tableTime: "08:00",
},
want: time.Date(0, 0, 0, 8, 0, 0, 0, europeTime),
},
{
name: "Test 2",
args: args{
tableTime: "08:15",
},
want: time.Date(0, 0, 0, 8, 15, 0, 0, europeTime),
},
{
name: "Test 3",
args: args{
tableTime: "08:30",
},
want: time.Date(0, 0, 0, 8, 30, 0, 0, europeTime),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := createTimeFromHourAndMinuteString(tt.args.tableTime); !reflect.DeepEqual(got, tt.want) {
t.Errorf("createTimeFromHourAndMinuteString() = %v, want %v", got, tt.want)
}
})
}
}
func Test_replaceTimeInDate(t *testing.T) {
type args struct {
date time.Time
time time.Time
}
tests := []struct {
name string
args args
want time.Time
}{
{
name: "Test 1",
args: args{
date: time.Date(2021, 1, 1, 0, 0, 0, 0, time.UTC),
time: time.Date(0, 0, 0, 8, 0, 0, 0, time.UTC),
},
want: time.Date(2021, 1, 1, 8, 0, 0, 0, time.UTC),
},
{
name: "Test 2",
args: args{
date: time.Date(2021, 1, 1, 0, 0, 0, 0, time.UTC),
time: time.Date(0, 0, 0, 8, 15, 0, 0, time.UTC),
},
want: time.Date(2021, 1, 1, 8, 15, 0, 0, time.UTC),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := replaceTimeForDate(tt.args.date, tt.args.time); !reflect.DeepEqual(got, tt.want) {
t.Errorf("addTimeToDate() = %v, want %v", got, tt.want)
}
})
}
}

View File

@@ -23,17 +23,14 @@ func Feed(c echo.Context, app *pocketbase.PocketBase, token string) error {
return c.JSON(http.StatusNotFound, err) return c.JSON(http.StatusNotFound, err)
} }
created := feed.Created
var modules []model.FeedCollection var modules []model.FeedCollection
_ = json.Unmarshal([]byte(feed.Modules), &modules) _ = json.Unmarshal([]byte(feed.Modules), &modules)
if created.Time().Add(time.Hour * 265).Before(time.Now()) {
newFeed, err := createFeedForToken(app, modules) newFeed, err := createFeedForToken(app, modules)
if err != nil { if err != nil {
return c.JSON(http.StatusInternalServerError, err) return c.JSON(http.StatusInternalServerError, err)
}
result = newFeed.Content
} }
result = newFeed.Content
responseWriter.Header().Set("Content-type", "text/calendar") responseWriter.Header().Set("Content-type", "text/calendar")
responseWriter.Header().Set("charset", "utf-8") responseWriter.Header().Set("charset", "utf-8")

View File

@@ -9,7 +9,7 @@ import (
"github.com/jordic/goics" "github.com/jordic/goics"
) )
// local type for EmitICal function // IcalModel local type for EmitICal function
type IcalModel struct { type IcalModel struct {
Events model.Events Events model.Events
Mapping []model.FeedCollection Mapping []model.FeedCollection
@@ -17,7 +17,7 @@ type IcalModel struct {
// EmitICal implements the interface for goics // EmitICal implements the interface for goics
func (icalModel IcalModel) EmitICal() goics.Componenter { func (icalModel IcalModel) EmitICal() goics.Componenter {
layout := "2006-01-02 15:04:05 -0700 MST" europeTime, _ := time.LoadLocation("Europe/Berlin")
c := goics.NewComponent() c := goics.NewComponent()
c.SetType("VCALENDAR") c.SetType("VCALENDAR")
c.AddProperty("VERSION", "2.0") c.AddProperty("VERSION", "2.0")
@@ -29,11 +29,9 @@ func (icalModel IcalModel) EmitICal() goics.Componenter {
for _, event := range icalModel.Events { for _, event := range icalModel.Events {
s := goics.NewComponent() s := goics.NewComponent()
s.SetType("VEVENT") s.SetType("VEVENT")
timeEnd, _ := time.Parse(layout, event.End) k, v := goics.FormatDateTime("DTEND;TZID=Europe/Berlin", event.End.Time().Local().In(europeTime))
timeStart, _ := time.Parse(layout, event.Start)
k, v := goics.FormatDateTime("DTEND;TZID=Europe/Berlin", timeEnd)
s.AddProperty(k, v) s.AddProperty(k, v)
k, v = goics.FormatDateTime("DTSTART;TZID=Europe/Berlin", timeStart) k, v = goics.FormatDateTime("DTSTART;TZID=Europe/Berlin", event.Start.Time().Local().In(europeTime))
s.AddProperty(k, v) s.AddProperty(k, v)
s.AddProperty("SUMMARY", replaceNameIfUserDefined(&event, icalModel.Mapping)) s.AddProperty("SUMMARY", replaceNameIfUserDefined(&event, icalModel.Mapping))
s.AddProperty("DESCRIPTION", generateDescription(event)) s.AddProperty("DESCRIPTION", generateDescription(event))

View File

@@ -1,12 +1,12 @@
import { Module } from "../model/module"; import { Module } from "../model/module";
export async function fetchModule(name: string): Promise<Module> { export async function fetchModule(module: Module): Promise<Module> {
const request = new Request("/api/module", { const request = new Request("/api/module", {
method: "GET", method: "POST",
headers: { headers: {
"Content-Type": "application/json", "Content-Type": "application/json",
Name: encodeURI(name),
}, },
body: JSON.stringify(module),
}); });
return await fetch(request) return await fetch(request)

View File

@@ -38,10 +38,9 @@ const ModuleInformation = defineAsyncComponent(
() => import("./ModuleInformation.vue"), () => import("./ModuleInformation.vue"),
); );
async function showInfo(moduleName: string) { async function showInfo(module: Module) {
const module: Ref<Module> = ref(new Module("", "", "", "", "", "", [])); await fetchModule(module).then((data) => {
await fetchModule(moduleName).then((data) => { module = data;
module.value = data;
}); });
dialog.open(ModuleInformation, { dialog.open(ModuleInformation, {
props: { props: {
@@ -66,7 +65,7 @@ const selectAll = ref(false);
const onSelectAllChange = (event: MultiSelectAllChangeEvent) => { const onSelectAllChange = (event: MultiSelectAllChangeEvent) => {
selectedModules.value = event.checked selectedModules.value = event.checked
? modules.value.map((module) => module) ? modules.value.map((module: Module) => module)
: []; : [];
selectAll.value = event.checked; selectAll.value = event.checked;
}; };
@@ -112,7 +111,7 @@ function selectChange() {
rounded rounded
outlined outlined
aria-label="Information" aria-label="Information"
@click.stop="showInfo(slotProps.option.name)" @click.stop="showInfo(slotProps.option)"
></Button> ></Button>
<DynamicDialog /> <DynamicDialog />
</div> </div>

View File

@@ -36,9 +36,8 @@ const ModuleInformation = defineAsyncComponent(
() => import("../ModuleInformation.vue"), () => import("../ModuleInformation.vue"),
); );
async function showInfo(moduleName: string) { async function showInfo(module: Module) {
const module: Ref<Module> = ref(new Module("", "", "", "", "", "", [])); await fetchModule(module).then((data) => {
await fetchModule(moduleName).then((data) => {
module.value = data; module.value = data;
}); });
dialog.open(ModuleInformation, { dialog.open(ModuleInformation, {
@@ -110,7 +109,7 @@ function selectChange() {
rounded rounded
outlined outlined
aria-label="Information" aria-label="Information"
@click.stop="showInfo(slotProps.option.name)" @click.stop="showInfo(slotProps.option)"
></Button> ></Button>
<DynamicDialog /> <DynamicDialog />
</div> </div>