mirror of
https://gitlab.dit.htwk-leipzig.de/htwk-software/htwkalender.git
synced 2025-08-02 17:59:14 +02:00
Merge branch 'main' into 15-calendar-preview
# Conflicts: # frontend/src/view/AdditionalModules.vue # frontend/src/view/EditCalendarView.vue
This commit is contained in:
52
backend/migrations/1700512738_updated_feeds.go
Normal file
52
backend/migrations/1700512738_updated_feeds.go
Normal file
@@ -0,0 +1,52 @@
|
||||
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("d65h4wh7zk13gxp")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// add
|
||||
new_retrieved := &schema.SchemaField{}
|
||||
json.Unmarshal([]byte(`{
|
||||
"system": false,
|
||||
"id": "wmmney8x",
|
||||
"name": "retrieved",
|
||||
"type": "date",
|
||||
"required": false,
|
||||
"presentable": false,
|
||||
"unique": false,
|
||||
"options": {
|
||||
"min": "",
|
||||
"max": ""
|
||||
}
|
||||
}`), new_retrieved)
|
||||
collection.Schema.AddField(new_retrieved)
|
||||
|
||||
return dao.SaveCollection(collection)
|
||||
}, func(db dbx.Builder) error {
|
||||
dao := daos.New(db)
|
||||
|
||||
collection, err := dao.FindCollectionByNameOrId("d65h4wh7zk13gxp")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// remove
|
||||
collection.Schema.RemoveField("wmmney8x")
|
||||
|
||||
return dao.SaveCollection(collection)
|
||||
})
|
||||
}
|
444
backend/migrations/1700512916_collections_snapshot.go
Normal file
444
backend/migrations/1700512916_collections_snapshot.go
Normal file
@@ -0,0 +1,444 @@
|
||||
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-11-01 21:17:43.567Z",
|
||||
"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-11-20 20:38:58.258Z",
|
||||
"name": "feeds",
|
||||
"type": "base",
|
||||
"system": false,
|
||||
"schema": [
|
||||
{
|
||||
"system": false,
|
||||
"id": "cowxjfmc",
|
||||
"name": "modules",
|
||||
"type": "json",
|
||||
"required": true,
|
||||
"presentable": false,
|
||||
"unique": false,
|
||||
"options": {}
|
||||
},
|
||||
{
|
||||
"system": false,
|
||||
"id": "wmmney8x",
|
||||
"name": "retrieved",
|
||||
"type": "date",
|
||||
"required": false,
|
||||
"presentable": false,
|
||||
"unique": false,
|
||||
"options": {
|
||||
"min": "",
|
||||
"max": ""
|
||||
}
|
||||
}
|
||||
],
|
||||
"indexes": [],
|
||||
"listRule": null,
|
||||
"viewRule": "",
|
||||
"createRule": null,
|
||||
"updateRule": "",
|
||||
"deleteRule": null,
|
||||
"options": {}
|
||||
},
|
||||
{
|
||||
"id": "7her4515qsmrxe8",
|
||||
"created": "2023-09-19 17:31:15.958Z",
|
||||
"updated": "2023-11-01 21:17:43.567Z",
|
||||
"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": {}
|
||||
},
|
||||
{
|
||||
"id": "_pb_users_auth_",
|
||||
"created": "2023-11-01 21:17:43.390Z",
|
||||
"updated": "2023-11-01 21:17:43.567Z",
|
||||
"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
|
||||
})
|
||||
}
|
@@ -1,9 +1,13 @@
|
||||
package model
|
||||
|
||||
import "github.com/pocketbase/pocketbase/models"
|
||||
import (
|
||||
"github.com/pocketbase/pocketbase/models"
|
||||
"github.com/pocketbase/pocketbase/tools/types"
|
||||
)
|
||||
|
||||
type Feed struct {
|
||||
Modules string `db:"modules" json:"modules"`
|
||||
Modules string `db:"modules" json:"modules"`
|
||||
Retrieved types.DateTime `db:"retrieved" json:"retrieved"`
|
||||
models.BaseModel
|
||||
}
|
||||
|
||||
|
@@ -1,10 +1,10 @@
|
||||
package model
|
||||
|
||||
type Module struct {
|
||||
UUID string `json:"uuid"`
|
||||
Name string `json:"name"`
|
||||
Prof string `json:"prof"`
|
||||
Course string `json:"course"`
|
||||
Semester string `json:"semester"`
|
||||
UUID string `json:"uuid" db:"uuid"`
|
||||
Name string `json:"name" db:"Name"`
|
||||
Prof string `json:"prof" db:"Prof"`
|
||||
Course string `json:"course" db:"course"`
|
||||
Semester string `json:"semester" db:"semester"`
|
||||
Events Events `json:"events"`
|
||||
}
|
||||
|
@@ -11,30 +11,150 @@ paths:
|
||||
/api/fetchPlans:
|
||||
get:
|
||||
summary: Fetch Seminar Plans
|
||||
security:
|
||||
- ApiKeyAuth: []
|
||||
responses:
|
||||
'200':
|
||||
description: Successful response
|
||||
/api/fetchGroups:
|
||||
get:
|
||||
summary: Fetch Seminar Groups
|
||||
security:
|
||||
- ApiKeyAuth: []
|
||||
responses:
|
||||
'200':
|
||||
description: Successful response
|
||||
/api/modules:
|
||||
delete:
|
||||
summary: Delete Module
|
||||
security:
|
||||
- ApiKeyAuth: []
|
||||
responses:
|
||||
'200':
|
||||
description: Successful response
|
||||
/api/rooms:
|
||||
get:
|
||||
summary: Get Rooms
|
||||
responses:
|
||||
'200':
|
||||
description: Successful response
|
||||
/api/feedURL:
|
||||
/api/schedule/day:
|
||||
get:
|
||||
summary: Get iCal Feed URL
|
||||
summary: Get Day Schedule
|
||||
parameters:
|
||||
- name: room
|
||||
in: query
|
||||
description: room
|
||||
example: "LN006-H"
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
- name: date
|
||||
in: query
|
||||
description: date
|
||||
example: "2023-11-26"
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
'200':
|
||||
description: Successful response
|
||||
/api/schedule:
|
||||
get:
|
||||
summary: Get Schedule
|
||||
parameters:
|
||||
- name: room
|
||||
in: query
|
||||
description: room
|
||||
example: "LN006-H"
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
- name: from
|
||||
in: query
|
||||
description: date
|
||||
example: "2023-11-26"
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
- name: to
|
||||
in: query
|
||||
description: date
|
||||
example: "2023-11-30"
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
'200':
|
||||
description: Successful response
|
||||
/api/createFeed:
|
||||
post:
|
||||
summary: Create iCal Feed
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
type: array
|
||||
items:
|
||||
$ref: '#/components/schemas/Module'
|
||||
required:
|
||||
- name
|
||||
- modules
|
||||
responses:
|
||||
'200':
|
||||
description: Successful response
|
||||
/api/feed:
|
||||
get:
|
||||
summary: Get iCal Feed for calendar
|
||||
parameters:
|
||||
- name: token
|
||||
in: query
|
||||
description: calendar token
|
||||
required: true
|
||||
example: "ldluwzg3e73ffxq"
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
'200':
|
||||
description: Successful response
|
||||
/api/course/modules:
|
||||
get:
|
||||
summary: Get Modules for Course
|
||||
parameters:
|
||||
- name: course
|
||||
in: query
|
||||
description: course
|
||||
required: true
|
||||
example: "Software Engineering"
|
||||
schema:
|
||||
type: string
|
||||
- name: semester
|
||||
in: query
|
||||
description: semester
|
||||
required: true
|
||||
example: "ws"
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
'200':
|
||||
description: Successful response
|
||||
/api/module:
|
||||
get:
|
||||
summary: Get Module
|
||||
parameters:
|
||||
- name: uuid
|
||||
in: query
|
||||
description: uuid
|
||||
required: true
|
||||
example: "d0b3a0e0-2f1a-4e1a-8b0a-0b9e1a0b9e1a"
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
'200':
|
||||
description: Successful response
|
||||
/api/courses:
|
||||
get:
|
||||
summary: Get Courses
|
||||
responses:
|
||||
'200':
|
||||
description: Successful response
|
||||
@@ -51,3 +171,52 @@ paths:
|
||||
responses:
|
||||
'200':
|
||||
description: Successful response
|
||||
/api/events:
|
||||
delete:
|
||||
summary: Delete Event
|
||||
security:
|
||||
- ApiKeyAuth: []
|
||||
responses:
|
||||
'200':
|
||||
description: Successful response
|
||||
/api/feeds/migrate:
|
||||
get:
|
||||
summary: Migrates all iCal Feeds in the database to the new format
|
||||
security:
|
||||
- ApiKeyAuth: []
|
||||
responses:
|
||||
'200':
|
||||
description: Successful response
|
||||
security:
|
||||
- ApiKeyAuth: []
|
||||
components:
|
||||
securitySchemes:
|
||||
ApiKeyAuth:
|
||||
type: apiKey
|
||||
in: header
|
||||
name: Authorization
|
||||
schemas:
|
||||
Module:
|
||||
type: object
|
||||
properties:
|
||||
name:
|
||||
type: string
|
||||
description: name
|
||||
example: "Software Engineering"
|
||||
uuid:
|
||||
type: string
|
||||
format: uuid
|
||||
course:
|
||||
type: string
|
||||
userDefinedName:
|
||||
type: string
|
||||
prof:
|
||||
type: string
|
||||
semester:
|
||||
type: string
|
||||
reminder:
|
||||
type: boolean
|
||||
events:
|
||||
type: array
|
||||
items:
|
||||
type: string
|
@@ -1,7 +1,6 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"htwkalender/model"
|
||||
"htwkalender/service/events"
|
||||
"htwkalender/service/fetch"
|
||||
"htwkalender/service/ical"
|
||||
@@ -26,6 +25,7 @@ func AddRoutes(app *pocketbase.PocketBase) {
|
||||
},
|
||||
Middlewares: []echo.MiddlewareFunc{
|
||||
apis.ActivityLogger(app),
|
||||
apis.RequireAdminAuth(),
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
@@ -61,6 +61,7 @@ func AddRoutes(app *pocketbase.PocketBase) {
|
||||
},
|
||||
Middlewares: []echo.MiddlewareFunc{
|
||||
apis.ActivityLogger(app),
|
||||
apis.RequireAdminAuth(),
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
@@ -212,18 +213,11 @@ func AddRoutes(app *pocketbase.PocketBase) {
|
||||
|
||||
app.OnBeforeServe().Add(func(e *core.ServeEvent) error {
|
||||
_, err := e.Router.AddRoute(echo.Route{
|
||||
Method: http.MethodPost,
|
||||
Method: http.MethodGet,
|
||||
Path: "/api/module",
|
||||
Handler: func(c echo.Context) error {
|
||||
|
||||
var requestModule model.Module
|
||||
|
||||
if err := c.Bind(&requestModule); err != nil {
|
||||
return apis.NewBadRequestError("Failed to read request body", err)
|
||||
}
|
||||
|
||||
module, err := events.GetModuleByName(app, requestModule)
|
||||
|
||||
requestModule := c.QueryParam("uuid")
|
||||
module, err := events.GetModuleByUUID(app, requestModule)
|
||||
if err != nil {
|
||||
return c.JSON(400, err)
|
||||
} else {
|
||||
@@ -286,7 +280,7 @@ func AddRoutes(app *pocketbase.PocketBase) {
|
||||
app.OnBeforeServe().Add(func(e *core.ServeEvent) error {
|
||||
_, err := e.Router.AddRoute(echo.Route{
|
||||
Method: http.MethodGet,
|
||||
Path: "/api/feed/migrate",
|
||||
Path: "/api/feeds/migrate",
|
||||
Handler: func(c echo.Context) error {
|
||||
err := ical.MigrateFeedJson(app)
|
||||
|
||||
|
@@ -4,8 +4,9 @@ import (
|
||||
"github.com/pocketbase/pocketbase"
|
||||
"github.com/pocketbase/pocketbase/core"
|
||||
"github.com/pocketbase/pocketbase/tools/cron"
|
||||
"htwkalender/service/events"
|
||||
"log"
|
||||
"htwkalender/service/course"
|
||||
"htwkalender/service/feed"
|
||||
"htwkalender/service/functions/time"
|
||||
)
|
||||
|
||||
func AddSchedules(app *pocketbase.PocketBase) {
|
||||
@@ -17,23 +18,15 @@ func AddSchedules(app *pocketbase.PocketBase) {
|
||||
// Every three hours update all courses (5 segments - minute, hour, day, month, weekday) "0 */3 * * *"
|
||||
// Every 10 minutes update all courses (5 segments - minute, hour, day, month, weekday) "*/10 * * * *"
|
||||
scheduler.MustAdd("updateCourse", "0 */3 * * *", func() {
|
||||
|
||||
courses := events.GetAllCourses(app)
|
||||
|
||||
for _, course := range courses {
|
||||
err := events.UpdateModulesForCourse(app, course)
|
||||
if err != nil {
|
||||
log.Println("Update Course: " + course + " failed")
|
||||
log.Println(err)
|
||||
} else {
|
||||
log.Println("Update Course: " + course + " successful")
|
||||
}
|
||||
}
|
||||
|
||||
course.UpdateCourse(app)
|
||||
})
|
||||
|
||||
// Every sunday at 3am clean all courses (5 segments - minute, hour, day, month, weekday) "0 3 * * 0"
|
||||
scheduler.MustAdd("cleanFeeds", "0 3 * * 0", func() {
|
||||
// clean feeds older than 6 months
|
||||
feed.ClearFeeds(app.Dao(), 6, time.RealClock{})
|
||||
})
|
||||
scheduler.Start()
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
|
20
backend/service/course/courseFunctions.go
Normal file
20
backend/service/course/courseFunctions.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package course
|
||||
|
||||
import (
|
||||
"github.com/pocketbase/pocketbase"
|
||||
"htwkalender/service/events"
|
||||
"log"
|
||||
)
|
||||
|
||||
func UpdateCourse(app *pocketbase.PocketBase) {
|
||||
courses := events.GetAllCourses(app)
|
||||
for _, course := range courses {
|
||||
err := events.UpdateModulesForCourse(app, course)
|
||||
if err != nil {
|
||||
log.Println("Update Course: " + course + " failed")
|
||||
log.Println(err)
|
||||
} else {
|
||||
log.Println("Update Course: " + course + " successful")
|
||||
}
|
||||
}
|
||||
}
|
@@ -196,6 +196,18 @@ func DeleteAllEvents(app *pocketbase.PocketBase) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func FindModuleByUUID(app *pocketbase.PocketBase, uuid string) (model.Module, error) {
|
||||
var module model.Module
|
||||
|
||||
err := app.Dao().DB().Select("*").From("events").Where(dbx.NewExp("uuid = {:uuid}", dbx.Params{"uuid": uuid})).One(&module)
|
||||
if err != nil {
|
||||
print("Error while getting events from database: ", err)
|
||||
return model.Module{}, err
|
||||
}
|
||||
|
||||
return module, nil
|
||||
}
|
||||
|
||||
func FindAllEventsByModule(app *pocketbase.PocketBase, module model.Module) (model.Events, error) {
|
||||
var events model.Events
|
||||
|
||||
|
@@ -1,10 +1,11 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"github.com/pocketbase/dbx"
|
||||
"github.com/pocketbase/pocketbase"
|
||||
"github.com/pocketbase/pocketbase/daos"
|
||||
"github.com/pocketbase/pocketbase/models"
|
||||
"htwkalender/model"
|
||||
"time"
|
||||
)
|
||||
|
||||
func SaveFeed(feed model.Feed, collection *models.Collection, app *pocketbase.PocketBase) (*models.Record, error) {
|
||||
@@ -19,10 +20,30 @@ func SaveFeed(feed model.Feed, collection *models.Collection, app *pocketbase.Po
|
||||
}
|
||||
|
||||
func FindFeedByToken(token string, app *pocketbase.PocketBase) (*model.Feed, error) {
|
||||
var feed model.Feed
|
||||
err := app.Dao().DB().Select("*").From("feeds").Where(dbx.NewExp("id = {:id}", dbx.Params{"id": token})).One(&feed)
|
||||
|
||||
record, err := app.Dao().FindRecordById("feeds", token)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var feed model.Feed
|
||||
feed.Modules = record.GetString("modules")
|
||||
feed.Retrieved = record.GetDateTime("retrieved")
|
||||
|
||||
//update retrieved time
|
||||
record.Set("retrieved", time.Now())
|
||||
|
||||
err = app.Dao().SaveRecord(record)
|
||||
|
||||
return &feed, err
|
||||
}
|
||||
|
||||
func GetAllFeeds(db *daos.Dao) ([]model.Feed, error) {
|
||||
var feeds []model.Feed
|
||||
err := db.DB().Select("*").From("feeds").All(&feeds)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return feeds, nil
|
||||
}
|
||||
|
@@ -42,11 +42,8 @@ func GetAllModulesDistinct(app *pocketbase.PocketBase, c echo.Context) error {
|
||||
}
|
||||
}
|
||||
|
||||
// GetModuleByName returns a module by its name
|
||||
// If the module does not exist, an error is returned
|
||||
// If the module exists, the module is returned
|
||||
// Module is a struct that exists in database as events
|
||||
func GetModuleByName(app *pocketbase.PocketBase, module model.Module) (model.Module, error) {
|
||||
func GetModuleByUUID(app *pocketbase.PocketBase, uuid string) (model.Module, error) {
|
||||
module, err := db.FindModuleByUUID(app, uuid)
|
||||
events, err := db.FindAllEventsByModule(app, module)
|
||||
|
||||
if err != nil || len(events) == 0 {
|
||||
|
35
backend/service/feed/feedFunctions.go
Normal file
35
backend/service/feed/feedFunctions.go
Normal file
@@ -0,0 +1,35 @@
|
||||
package feed
|
||||
|
||||
import (
|
||||
"github.com/pocketbase/dbx"
|
||||
"github.com/pocketbase/pocketbase/daos"
|
||||
database "htwkalender/service/db"
|
||||
localTime "htwkalender/service/functions/time"
|
||||
"log"
|
||||
)
|
||||
|
||||
func ClearFeeds(db *daos.Dao, months int, clock localTime.Clock) {
|
||||
feeds, err := database.GetAllFeeds(db)
|
||||
if err != nil {
|
||||
log.Println("CleanFeeds: get all feeds failed")
|
||||
return
|
||||
}
|
||||
for _, feed := range feeds {
|
||||
// if retrieved time is older than a half year delete feed
|
||||
now := clock.Now()
|
||||
feedRetrievedTime := feed.Retrieved.Time()
|
||||
timeShift := now.AddDate(0, -months, 0)
|
||||
|
||||
if feedRetrievedTime.Before(timeShift) {
|
||||
// delete feed
|
||||
sqlResult, err := db.DB().Delete("feeds", dbx.NewExp("id = {:id}", dbx.Params{"id": feed.GetId()})).Execute()
|
||||
if err != nil {
|
||||
log.Println("CleanFeeds: delete feed " + feed.GetId() + " failed")
|
||||
log.Println(err)
|
||||
log.Println(sqlResult)
|
||||
} else {
|
||||
log.Println("CleanFeeds: delete feed " + feed.GetId() + " successful")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
83
backend/service/feed/feedFunctions_test.go
Normal file
83
backend/service/feed/feedFunctions_test.go
Normal file
@@ -0,0 +1,83 @@
|
||||
package feed
|
||||
|
||||
import (
|
||||
"github.com/pocketbase/pocketbase/daos"
|
||||
"github.com/pocketbase/pocketbase/tests"
|
||||
"htwkalender/model"
|
||||
mockTime "htwkalender/service/functions/time"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
const testDataDir = "./mockData"
|
||||
|
||||
func TestClearFeeds(t *testing.T) {
|
||||
|
||||
setupTestApp := func(t *testing.T) *daos.Dao {
|
||||
testApp, err := tests.NewTestApp(testDataDir)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
dao := daos.New(testApp.Dao().DB())
|
||||
return dao
|
||||
}
|
||||
|
||||
type args struct {
|
||||
db *daos.Dao
|
||||
months int
|
||||
mockClock mockTime.MockClock
|
||||
}
|
||||
testCases := []struct {
|
||||
name string
|
||||
args args
|
||||
want int
|
||||
}{
|
||||
{
|
||||
name: "TestClearFeeds",
|
||||
args: args{
|
||||
db: setupTestApp(t),
|
||||
months: 6,
|
||||
mockClock: mockTime.MockClock{
|
||||
NowTime: time.Date(2023, 12, 1, 0, 0, 0, 0, time.UTC),
|
||||
},
|
||||
},
|
||||
want: 1,
|
||||
},
|
||||
{
|
||||
name: "TestClearAllFeeds",
|
||||
args: args{
|
||||
db: setupTestApp(t),
|
||||
months: 1,
|
||||
mockClock: mockTime.MockClock{
|
||||
NowTime: time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC),
|
||||
},
|
||||
},
|
||||
want: 0,
|
||||
},
|
||||
{
|
||||
name: "TestClearFeedsClearBeforeRetrievedTime",
|
||||
args: args{
|
||||
db: setupTestApp(t),
|
||||
months: 1,
|
||||
mockClock: mockTime.MockClock{
|
||||
NowTime: time.Date(2010, 1, 1, 0, 0, 0, 0, time.UTC),
|
||||
},
|
||||
},
|
||||
want: 3,
|
||||
},
|
||||
}
|
||||
for _, tt := range testCases {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
ClearFeeds(tt.args.db, tt.args.months, tt.args.mockClock)
|
||||
// count all feeds in db
|
||||
var feeds []*model.Feed
|
||||
err := tt.args.db.DB().Select("id").From("feeds").All(&feeds)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if got := len(feeds); got != tt.want {
|
||||
t.Errorf("ClearFeeds() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
BIN
backend/service/feed/mockData/data.db
Normal file
BIN
backend/service/feed/mockData/data.db
Normal file
Binary file not shown.
BIN
backend/service/feed/mockData/logs.db
Normal file
BIN
backend/service/feed/mockData/logs.db
Normal file
Binary file not shown.
10
backend/service/functions/time/mockClock.go
Normal file
10
backend/service/functions/time/mockClock.go
Normal file
@@ -0,0 +1,10 @@
|
||||
package time
|
||||
|
||||
import "time"
|
||||
|
||||
type MockClock struct {
|
||||
NowTime time.Time
|
||||
}
|
||||
|
||||
func (m MockClock) Now() time.Time { return m.NowTime }
|
||||
func (MockClock) After(d time.Duration) <-chan time.Time { return time.After(d) }
|
8
backend/service/functions/time/realClock.go
Normal file
8
backend/service/functions/time/realClock.go
Normal file
@@ -0,0 +1,8 @@
|
||||
package time
|
||||
|
||||
import "time"
|
||||
|
||||
type RealClock struct{}
|
||||
|
||||
func (RealClock) Now() time.Time { return time.Now() }
|
||||
func (RealClock) After(d time.Duration) <-chan time.Time { return time.After(d) }
|
8
backend/service/functions/time/time.go
Normal file
8
backend/service/functions/time/time.go
Normal file
@@ -0,0 +1,8 @@
|
||||
package time
|
||||
|
||||
import "time"
|
||||
|
||||
type Clock interface {
|
||||
Now() time.Time
|
||||
After(d time.Duration) <-chan time.Time
|
||||
}
|
Reference in New Issue
Block a user