Merge branch 'main' of github.com:masterElmar/htwkalender into 10-roomfinder

This commit is contained in:
Tom Wahl
2023-11-01 09:31:03 +01:00
15 changed files with 1011 additions and 67 deletions

View File

@@ -0,0 +1,141 @@
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
}
json.Unmarshal([]byte(`[]`), &collection.Indexes)
// remove
collection.Schema.RemoveField("7vsr9h6p")
// remove
collection.Schema.RemoveField("wwpokofe")
// add
new_start := &schema.SchemaField{}
json.Unmarshal([]byte(`{
"system": false,
"id": "6hkjwgb4",
"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": "szbefpjf",
"name": "end",
"type": "date",
"required": false,
"presentable": false,
"unique": false,
"options": {
"min": "",
"max": ""
}
}`), new_end)
collection.Schema.AddField(new_end)
// add
new_Compulsory := &schema.SchemaField{}
json.Unmarshal([]byte(`{
"system": false,
"id": "nlnnxu7x",
"name": "Compulsory",
"type": "text",
"required": false,
"presentable": false,
"unique": false,
"options": {
"min": null,
"max": null,
"pattern": ""
}
}`), new_Compulsory)
collection.Schema.AddField(new_Compulsory)
return dao.SaveCollection(collection)
}, func(db dbx.Builder) error {
dao := daos.New(db)
collection, err := dao.FindCollectionByNameOrId("7her4515qsmrxe8")
if err != nil {
return err
}
json.Unmarshal([]byte(`[
"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)"
]`), &collection.Indexes)
// 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("6hkjwgb4")
// remove
collection.Schema.RemoveField("szbefpjf")
// remove
collection.Schema.RemoveField("nlnnxu7x")
return dao.SaveCollection(collection)
})
}

View File

@@ -0,0 +1,37 @@
package migrations
import (
"encoding/json"
"github.com/pocketbase/dbx"
"github.com/pocketbase/pocketbase/daos"
m "github.com/pocketbase/pocketbase/migrations"
)
func init() {
m.Register(func(db dbx.Builder) error {
dao := daos.New(db)
collection, err := dao.FindCollectionByNameOrId("7her4515qsmrxe8")
if err != nil {
return err
}
json.Unmarshal([]byte(`[
"CREATE INDEX `+"`"+`idx_4vOTAiC`+"`"+` ON `+"`"+`events`+"`"+` (\n `+"`"+`Name`+"`"+`,\n `+"`"+`course`+"`"+`,\n `+"`"+`start`+"`"+`,\n `+"`"+`end`+"`"+`,\n `+"`"+`semester`+"`"+`,\n `+"`"+`EventType`+"`"+`,\n `+"`"+`Compulsory`+"`"+`\n)"
]`), &collection.Indexes)
return dao.SaveCollection(collection)
}, func(db dbx.Builder) error {
dao := daos.New(db)
collection, err := dao.FindCollectionByNameOrId("7her4515qsmrxe8")
if err != nil {
return err
}
json.Unmarshal([]byte(`[]`), &collection.Indexes)
return dao.SaveCollection(collection)
})
}

View File

@@ -0,0 +1,431 @@
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": "_pb_users_auth_",
"created": "2023-09-20 10:23:59.315Z",
"updated": "2023-10-17 22:18:39.192Z",
"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
}
},
{
"id": "cfq9mqlmd97v8z5",
"created": "2023-09-21 16:53:51.811Z",
"updated": "2023-10-17 22:18:39.190Z",
"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-21 16:53:51.812Z",
"updated": "2023-10-17 22:18:39.191Z",
"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-28 12:07:17.340Z",
"updated": "2023-10-31 16:47:43.090Z",
"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": "6hkjwgb4",
"name": "start",
"type": "date",
"required": false,
"presentable": false,
"unique": false,
"options": {
"min": "",
"max": ""
}
},
{
"system": false,
"id": "szbefpjf",
"name": "end",
"type": "date",
"required": false,
"presentable": false,
"unique": false,
"options": {
"min": "",
"max": ""
}
},
{
"system": false,
"id": "nlnnxu7x",
"name": "Compulsory",
"type": "text",
"required": false,
"presentable": false,
"unique": false,
"options": {
"min": null,
"max": null,
"pattern": ""
}
}
],
"indexes": [
"CREATE INDEX ` + "`" + `idx_4vOTAiC` + "`" + ` ON ` + "`" + `events` + "`" + ` (\n ` + "`" + `Name` + "`" + `,\n ` + "`" + `course` + "`" + `,\n ` + "`" + `start` + "`" + `,\n ` + "`" + `end` + "`" + `,\n ` + "`" + `semester` + "`" + `,\n ` + "`" + `EventType` + "`" + `,\n ` + "`" + `Compulsory` + "`" + `\n)"
],
"listRule": null,
"viewRule": null,
"createRule": null,
"updateRule": null,
"deleteRule": null,
"options": {}
}
]`
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

@@ -1,8 +1,10 @@
package model package model
import ( import (
"github.com/pocketbase/pocketbase/models"
"slices" "slices"
"github.com/pocketbase/pocketbase/models"
"github.com/pocketbase/pocketbase/tools/types"
) )
type Events []Event type Events []Event
@@ -12,19 +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"`
Prof string `db:"Prof" json:"prof"` Compulsory string `db:"Compulsory" json:"compulsory"`
Rooms string `db:"Rooms" json:"rooms"` Prof string `db:"Prof" json:"prof"`
Notes string `db:"Notes" json:"notes"` Rooms string `db:"Rooms" json:"rooms"`
BookedAt string `db:"BookedAt" json:"bookedAt"` Notes string `db:"Notes" json:"notes"`
Course string `db:"course" json:"course"` BookedAt string `db:"BookedAt" json:"bookedAt"`
Semester string `db:"semester" json:"semester"` Course string `db:"course" json:"course"`
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,10 +1,6 @@
package service package service
import ( import (
"github.com/labstack/echo/v5"
"github.com/pocketbase/pocketbase"
"github.com/pocketbase/pocketbase/apis"
"github.com/pocketbase/pocketbase/core"
"htwkalender/model" "htwkalender/model"
"htwkalender/service/events" "htwkalender/service/events"
"htwkalender/service/fetch" "htwkalender/service/fetch"
@@ -12,6 +8,11 @@ import (
"htwkalender/service/room" "htwkalender/service/room"
"io" "io"
"net/http" "net/http"
"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) {
@@ -273,6 +274,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

@@ -1,9 +1,10 @@
package db package db
import ( import (
"htwkalender/model"
"github.com/pocketbase/dbx" "github.com/pocketbase/dbx"
"github.com/pocketbase/pocketbase" "github.com/pocketbase/pocketbase"
"htwkalender/model"
) )
func SaveEvents(seminarGroup []model.SeminarGroup, app *pocketbase.PocketBase) ([]model.Event, error) { func SaveEvents(seminarGroup []model.SeminarGroup, app *pocketbase.PocketBase) ([]model.Event, error) {

View File

@@ -2,11 +2,6 @@ package fetch
import ( import (
"fmt" "fmt"
"github.com/google/uuid"
"github.com/labstack/echo/v5"
"github.com/pocketbase/pocketbase"
"github.com/pocketbase/pocketbase/apis"
"golang.org/x/net/html"
"htwkalender/model" "htwkalender/model"
"htwkalender/service/date" "htwkalender/service/date"
"htwkalender/service/db" "htwkalender/service/db"
@@ -16,6 +11,13 @@ import (
"strconv" "strconv"
"strings" "strings"
"time" "time"
"github.com/google/uuid"
"github.com/labstack/echo/v5"
"github.com/pocketbase/pocketbase"
"github.com/pocketbase/pocketbase/apis"
"github.com/pocketbase/pocketbase/tools/types"
"golang.org/x/net/html"
) )
func GetSeminarEvents(c echo.Context, app *pocketbase.PocketBase) error { func GetSeminarEvents(c echo.Context, app *pocketbase.PocketBase) error {
@@ -77,6 +79,21 @@ func GetSeminarGroupsEventsFromHTML(seminarGroupsLabel []string) []model.Seminar
return seminarGroups return seminarGroups
} }
func splitEventType(events []model.Event) []model.Event {
for i, event := range events {
matched, _ := regexp.Match("^(V|P|S)(w|p)$", []byte(event.EventType))
if matched {
eventType := event.EventType
event.EventType = eventType[0:1]
event.Compulsory = eventType[1:2]
events[i] = event
}
}
return events
}
func parseSeminarGroup(result string) model.SeminarGroup { func parseSeminarGroup(result string) model.SeminarGroup {
doc, err := html.Parse(strings.NewReader(result)) doc, err := html.Parse(strings.NewReader(result))
if err != nil { if err != nil {
@@ -100,6 +117,7 @@ func parseSeminarGroup(result string) model.SeminarGroup {
semester, year := extractSemesterAndYear(semesterString) semester, year := extractSemesterAndYear(semesterString)
events = convertWeeksToDates(events, semester, year) events = convertWeeksToDates(events, semester, year)
events = generateUUIDs(events, course) events = generateUUIDs(events, course)
events = splitEventType(events)
var seminarGroup = model.SeminarGroup{ var seminarGroup = model.SeminarGroup{
University: findFirstSpanWithClass(table, "header-1-0-0").FirstChild.Data, University: findFirstSpanWithClass(table, "header-1-0-0").FirstChild.Data,
Course: course, Course: course,
@@ -124,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) {
@@ -188,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]),
@@ -208,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) {
@@ -119,6 +120,96 @@ func Test_replaceEmptyEventNames(t *testing.T) {
} }
} }
func Test_splitEventType(t *testing.T) {
type args struct {
events []model.Event
}
tests := []struct {
name string
args args
want []model.Event
}{
{
name: "Test 1",
args: args{
events: []model.Event{
{
EventType: "V",
},
},
},
want: []model.Event{
{
EventType: "V",
Compulsory: "",
},
},
},
{
name: "Test 2",
args: args{
events: []model.Event{
{
EventType: "Vw",
},
},
},
want: []model.Event{
{
EventType: "V",
Compulsory: "w",
},
},
},
{
name: "Test 3",
args: args{
events: []model.Event{
{
EventType: "Sperr",
},
},
},
want: []model.Event{
{
EventType: "Sperr",
Compulsory: "",
},
},
},
{
name: "Test 4",
args: args{
events: []model.Event{
{
EventType: "Sperr",
},
{
EventType: "Vw",
},
},
},
want: []model.Event{
{
EventType: "Sperr",
Compulsory: "",
},
{
EventType: "V",
Compulsory: "w",
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := splitEventType(tt.args.events); !reflect.DeepEqual(got, tt.want) {
t.Errorf("splitEventType() = %v, want %v", got, tt.want)
}
})
}
}
func Test_generateUUIDs(t *testing.T) { func Test_generateUUIDs(t *testing.T) {
type args struct { type args struct {
events []model.Event events []model.Event
@@ -172,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

@@ -1,13 +1,15 @@
package ical package ical
import ( import (
"github.com/jordic/goics"
"htwkalender/model" "htwkalender/model"
"htwkalender/service/functions" "htwkalender/service/functions"
"htwkalender/service/names"
"time" "time"
"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
@@ -15,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")
@@ -27,13 +29,11 @@ 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.Name, icalModel.Mapping)) s.AddProperty("SUMMARY", replaceNameIfUserDefined(&event, icalModel.Mapping))
s.AddProperty("DESCRIPTION", generateDescription(event)) s.AddProperty("DESCRIPTION", generateDescription(event))
s.AddProperty("LOCATION", event.Rooms) s.AddProperty("LOCATION", event.Rooms)
c.AddComponent(s) c.AddComponent(s)
@@ -41,13 +41,13 @@ func (icalModel IcalModel) EmitICal() goics.Componenter {
return c return c
} }
func replaceNameIfUserDefined(name string, mapping []model.FeedCollection) string { func replaceNameIfUserDefined(event *model.Event, mapping []model.FeedCollection) string {
for _, mapEntry := range mapping { for _, mapEntry := range mapping {
if mapEntry.Name == name && !functions.OnlyWhitespace(mapEntry.UserDefinedName) { if mapEntry.Name == event.Name && !functions.OnlyWhitespace(mapEntry.UserDefinedName) {
return mapEntry.UserDefinedName return names.ReplaceTemplateSubStrings(mapEntry.UserDefinedName, *event)
} }
} }
return name return event.Name
} }
func generateDescription(event model.Event) string { func generateDescription(event model.Event) string {
@@ -63,7 +63,7 @@ func generateDescription(event model.Event) string {
description += "Gruppe: " + event.Course + "\n" description += "Gruppe: " + event.Course + "\n"
} }
if !functions.OnlyWhitespace(event.EventType) { if !functions.OnlyWhitespace(event.EventType) {
description += "Typ: " + event.EventType + "\n" description += "Typ: " + event.EventType + event.Compulsory + "\n"
} }
return description return description

View File

@@ -0,0 +1,23 @@
package names
import (
"htwkalender/model"
"regexp"
)
func ReplaceTemplateSubStrings(rawString string, event model.Event) string {
re := regexp.MustCompile(`\%(.)`)
return re.ReplaceAllStringFunc(rawString, func(match string) string {
switch match {
case "%%":
return "%"
case "%t":
return event.EventType
case "%p":
return event.Compulsory
default:
return match
}
})
}

View File

@@ -0,0 +1,78 @@
package names
import (
"htwkalender/model"
"testing"
)
func TestReplaceTemplateSubStrings(t *testing.T) {
type args struct {
rawString string
event model.Event
}
tests := []struct {
name string
args args
want string
}{
{
name: "Test 1",
args: args{
rawString: "%t",
event: model.Event{
EventType: "Test",
},
},
want: "Test",
},
{
name: "Test 2",
args: args{
rawString: "%p",
event: model.Event{
Compulsory: "Test",
},
},
want: "Test",
},
{
name: "Test 3",
args: args{
rawString: "%%",
event: model.Event{
EventType: "Test",
},
},
want: "%",
},
{
name: "Test 4",
args: args{
rawString: "%t %p",
event: model.Event{
EventType: "Test",
Compulsory: "Test",
},
},
want: "Test Test",
},
{
name: "Test 5",
args: args{
rawString: "%t %p %%",
event: model.Event{
EventType: "Test",
Compulsory: "Test",
},
},
want: "Test Test %",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := ReplaceTemplateSubStrings(tt.args.rawString, tt.args.event); got != tt.want {
t.Errorf("ReplaceTemplateSubStrings() = %v, want %v", got, tt.want)
}
})
}
}

View File

@@ -3,7 +3,7 @@ import moduleStore from "../store/moduleStore.ts";
import { createIndividualFeed } from "../api/createFeed.ts"; import { createIndividualFeed } from "../api/createFeed.ts";
import router from "../router"; import router from "../router";
import tokenStore from "../store/tokenStore.ts"; import tokenStore from "../store/tokenStore.ts";
import { ref } from "vue"; import { Ref, ref } from "vue";
const tableData = ref( const tableData = ref(
moduleStore().modules.map((module) => { moduleStore().modules.map((module) => {
@@ -19,6 +19,13 @@ const columns = ref([
{ field: "Module", header: "Module" }, { field: "Module", header: "Module" },
]); ]);
const helpVisible: Ref<boolean> = ref(false);
const placeholders = ref([
{ placeholder: "%t", description: "Event Type", examples: "V = Vorlesung, S = Seminar, P = Praktikum/Prüfung" },
{ placeholder: "%p", description: "Mandatory", examples: "w = optional, p = mandatory" },
]);
async function finalStep() { async function finalStep() {
const token: string = await createIndividualFeed(moduleStore().modules); const token: string = await createIndividualFeed(moduleStore().modules);
tokenStore().setToken(token); tokenStore().setToken(token);
@@ -30,6 +37,26 @@ async function finalStep() {
<div class="flex flex-column"> <div class="flex flex-column">
<div class="flex align-items-center justify-content-center h-4rem m-2"> <div class="flex align-items-center justify-content-center h-4rem m-2">
<h3>Rename your selected Modules to your liking.</h3> <h3>Rename your selected Modules to your liking.</h3>
<i class="shadow-3 hover:shadow-8 pi pi-info-circle m-2" style="font-size: 1.5rem; color: orange" @click="helpVisible = true"></i>
<Dialog
v-model:visible="helpVisible"
header="Module placeholders"
>
<p>
Here you can rename your modules to your liking. This will be the name
of the event in your calendar.
</p>
<p>
You can use the following placeholders in your module names:
</p>
<DataTable
:value="placeholders"
>
<Column field="placeholder" header="Placeholder"></Column>
<Column field="description" header="Description"></Column>
<Column field="examples" header="Examples"></Column>
</DataTable>
</Dialog>
</div> </div>
<div class="card flex align-items-center justify-content-center m-2"> <div class="card flex align-items-center justify-content-center m-2">
<DataTable <DataTable

View File

@@ -8,6 +8,7 @@ import Menubar from "primevue/menubar";
import InputText from "primevue/inputtext"; import InputText from "primevue/inputtext";
import Card from "primevue/card"; import Card from "primevue/card";
import DataView from "primevue/dataview"; import DataView from "primevue/dataview";
import Dialog from "primevue/dialog";
import ToggleButton from "primevue/togglebutton"; import ToggleButton from "primevue/togglebutton";
import "primevue/resources/themes/viva-dark/theme.css"; import "primevue/resources/themes/viva-dark/theme.css";
import "primeicons/primeicons.css"; import "primeicons/primeicons.css";
@@ -15,6 +16,7 @@ import "primeflex/primeflex.css";
import router from "./router"; import router from "./router";
import TabView from "primevue/tabview"; import TabView from "primevue/tabview";
import TabPanel from "primevue/tabpanel"; import TabPanel from "primevue/tabpanel";
import Tag from "primevue/tag";
import { createPinia } from "pinia"; import { createPinia } from "pinia";
import MultiSelect from "primevue/multiselect"; import MultiSelect from "primevue/multiselect";
import ToastService from "primevue/toastservice"; import ToastService from "primevue/toastservice";
@@ -37,6 +39,7 @@ app.use(pinia);
app.use(DialogService); app.use(DialogService);
app.component("Button", Button); app.component("Button", Button);
app.component("Menubar", Menubar); app.component("Menubar", Menubar);
app.component("Dialog", Dialog);
app.component("Dropdown", Dropdown); app.component("Dropdown", Dropdown);
app.component("InputText", InputText); app.component("InputText", InputText);
app.component("Card", Card); app.component("Card", Card);
@@ -45,6 +48,7 @@ app.component("ToggleButton", ToggleButton);
app.component("TabView", TabView); app.component("TabView", TabView);
app.component("TabPanel", TabPanel); app.component("TabPanel", TabPanel);
app.component("MultiSelect", MultiSelect); app.component("MultiSelect", MultiSelect);
app.component("Tag", Tag);
app.component("Toast", Toast); app.component("Toast", Toast);
app.component("Accordion", Accordion); app.component("Accordion", Accordion);
app.component("AccordionTab", AccordionTab); app.component("AccordionTab", AccordionTab);