mirror of
https://gitlab.dit.htwk-leipzig.de/htwk-software/htwkalender.git
synced 2025-08-02 17:59:14 +02:00
Merge remote-tracking branch 'origin/main' into 11-enhanced-module-titles
This commit is contained in:
@@ -2,105 +2,147 @@ package db
|
||||
|
||||
import (
|
||||
"htwkalender/model"
|
||||
"log"
|
||||
|
||||
"htwkalender/model"
|
||||
|
||||
"github.com/pocketbase/dbx"
|
||||
"github.com/pocketbase/pocketbase"
|
||||
"github.com/pocketbase/pocketbase/models"
|
||||
)
|
||||
|
||||
func SaveEvents(seminarGroup []model.SeminarGroup, collection *models.Collection, app *pocketbase.PocketBase) ([]*models.Record, error) {
|
||||
var toBeSavedEvents []struct {
|
||||
model.Event
|
||||
string
|
||||
}
|
||||
var savedRecords []*models.Record
|
||||
var insertRecords []*models.Record
|
||||
func SaveEvents(seminarGroup []model.SeminarGroup, app *pocketbase.PocketBase) ([]model.Event, error) {
|
||||
var toBeSavedEvents model.Events
|
||||
var savedRecords model.Events
|
||||
|
||||
// check if event is already in database and add to toBeSavedEvents if not
|
||||
for _, seminarGroup := range seminarGroup {
|
||||
for _, event := range seminarGroup.Events {
|
||||
dbGroup, err := findEventByDayWeekStartEndNameCourse(event, seminarGroup.Course, app)
|
||||
event = event.SetCourse(seminarGroup.Course)
|
||||
existsInDatabase, err := findEventByDayWeekStartEndNameCourse(event, seminarGroup.Course, app)
|
||||
alreadyAddedToSave := toBeSavedEvents.Contains(event)
|
||||
|
||||
if dbGroup == nil && err.Error() == "sql: no rows in result set" {
|
||||
toBeSavedEvents = append(toBeSavedEvents, struct {
|
||||
model.Event
|
||||
string
|
||||
}{event, seminarGroup.Course})
|
||||
} else if err != nil {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !existsInDatabase && !alreadyAddedToSave {
|
||||
toBeSavedEvents = append(toBeSavedEvents, event)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// create record for each event that's not already in the database
|
||||
for _, event := range toBeSavedEvents {
|
||||
record := models.NewRecord(collection)
|
||||
record.Set("Day", event.Day)
|
||||
record.Set("Week", event.Week)
|
||||
record.Set("Start", event.Start)
|
||||
record.Set("End", event.End)
|
||||
record.Set("Name", event.Name)
|
||||
record.Set("EventType", event.EventType)
|
||||
record.Set("Compulsory", event.Compulsory)
|
||||
record.Set("Prof", event.Prof)
|
||||
record.Set("Rooms", event.Rooms)
|
||||
record.Set("Notes", event.Notes)
|
||||
record.Set("BookedAt", event.BookedAt)
|
||||
record.Set("course", event.string)
|
||||
record.Set("semester", event.Semester)
|
||||
insertRecords = append(insertRecords, record)
|
||||
}
|
||||
|
||||
// save all records
|
||||
for _, record := range insertRecords {
|
||||
if record != nil {
|
||||
err := app.Dao().SaveRecord(record)
|
||||
if err == nil {
|
||||
savedRecords = append(savedRecords, record)
|
||||
} else {
|
||||
log.Println("Error while saving record: ", err)
|
||||
return nil, err
|
||||
}
|
||||
event.MarkAsNew()
|
||||
// auto mapping for event fields to record fields
|
||||
err := app.Dao().Save(&event)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
savedRecords = append(savedRecords, event)
|
||||
}
|
||||
}
|
||||
|
||||
return savedRecords, nil
|
||||
}
|
||||
|
||||
func findEventByDayWeekStartEndNameCourse(event model.Event, course string, app *pocketbase.PocketBase) (*model.Event, error) {
|
||||
err := app.Dao().DB().Select("*").From("events").Where(
|
||||
dbx.NewExp("Day = {:day} AND Week = {:week} AND Start = {:start} AND End = {:end} AND Name = {:name} AND course = {:course} AND Prof = {:prof} AND Rooms = {:rooms} AND EventType = {:eventType}",
|
||||
dbx.Params{"day": event.Day, "week": event.Week, "start": event.Start, "end": event.End, "name": event.Name, "course": course, "prof": event.Prof, "rooms": event.Rooms, "eventType": event.EventType}),
|
||||
).One(&event)
|
||||
// check if event is already in database and return true if it is and false if it's not
|
||||
func findEventByDayWeekStartEndNameCourse(event model.Event, course string, app *pocketbase.PocketBase) (bool, error) {
|
||||
|
||||
var dbEvent model.Event
|
||||
|
||||
err := app.Dao().DB().Select("*").From("events").
|
||||
Where(dbx.NewExp(
|
||||
"Day = {:day} AND "+
|
||||
"Week = {:week} AND "+
|
||||
"Start = {:start} AND "+
|
||||
"End = {:end} AND "+
|
||||
"Name = {:name} AND "+
|
||||
"course = {:course} AND "+
|
||||
"Prof = {:prof} AND "+
|
||||
"Rooms = {:rooms} AND "+
|
||||
"EventType = {:eventType}",
|
||||
dbx.Params{
|
||||
"day": event.Day,
|
||||
"week": event.Week,
|
||||
"start": event.Start,
|
||||
"end": event.End,
|
||||
"name": event.Name,
|
||||
"course": course,
|
||||
"prof": event.Prof,
|
||||
"rooms": event.Rooms,
|
||||
"eventType": event.EventType}),
|
||||
).One(&dbEvent)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
if err.Error() == "sql: no rows in result set" {
|
||||
return false, nil
|
||||
}
|
||||
return false, err
|
||||
} else {
|
||||
return true, nil
|
||||
}
|
||||
return &event, err
|
||||
}
|
||||
|
||||
func buildIcalQueryForModules(modules []model.FeedCollection) dbx.Expression {
|
||||
|
||||
// build where conditions for each module
|
||||
|
||||
//first check if modules is empty
|
||||
if len(modules) == 0 {
|
||||
return dbx.HashExp{}
|
||||
}
|
||||
|
||||
//second check if modules has only one element
|
||||
if len(modules) == 1 {
|
||||
return dbx.And(
|
||||
dbx.HashExp{"Name": modules[0].Name},
|
||||
dbx.HashExp{"course": modules[0].Course},
|
||||
)
|
||||
}
|
||||
|
||||
//third check if modules has more than one element
|
||||
var wheres []dbx.Expression
|
||||
|
||||
for _, module := range modules {
|
||||
where := dbx.And(
|
||||
dbx.HashExp{"Name": module.Name},
|
||||
dbx.HashExp{"course": module.Course},
|
||||
)
|
||||
wheres = append(wheres, where)
|
||||
}
|
||||
|
||||
// Use dbx.And or dbx.Or to combine the where conditions as needed
|
||||
where := dbx.Or(wheres...)
|
||||
|
||||
return where
|
||||
|
||||
}
|
||||
|
||||
// GetPlanForModules returns all events for the given modules with the given course
|
||||
// used for the ical feed
|
||||
func GetPlanForModules(app *pocketbase.PocketBase, modules []model.FeedCollection) model.Events {
|
||||
|
||||
// build query functions with name equals elements in modules for dbx query
|
||||
var events model.Events
|
||||
|
||||
var queryString string
|
||||
for i, module := range modules {
|
||||
if i == 0 {
|
||||
queryString = "Name = '" + module.Name + "' AND course = '" + module.Course + "'"
|
||||
// iterate over modules in 100 batch sizes
|
||||
for i := 0; i < len(modules); i += 100 {
|
||||
var moduleBatch []model.FeedCollection
|
||||
|
||||
if i+100 > len(modules) {
|
||||
moduleBatch = modules[i:]
|
||||
} else {
|
||||
queryString = queryString + " OR Name = '" + module.Name + "' AND course = '" + module.Course + "'"
|
||||
moduleBatch = modules[i : i+100]
|
||||
}
|
||||
|
||||
var selectedModulesQuery = buildIcalQueryForModules(moduleBatch)
|
||||
// get all events from event records in the events collection
|
||||
err := app.Dao().DB().Select("*").From("events").Where(selectedModulesQuery).All(&events)
|
||||
if err != nil {
|
||||
print("Error while getting events from database: ", err)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
var events model.Events
|
||||
// get all events from event records in the events collection
|
||||
err := app.Dao().DB().Select("*").From("events").Where(dbx.NewExp(queryString)).All(&events)
|
||||
if err != nil {
|
||||
print("Error while getting events from database: ", err)
|
||||
return nil
|
||||
}
|
||||
return events
|
||||
}
|
||||
|
||||
|
42
backend/service/db/dbEvents_test.go
Normal file
42
backend/service/db/dbEvents_test.go
Normal file
@@ -0,0 +1,42 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"github.com/pocketbase/dbx"
|
||||
"htwkalender/model"
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test_buildIcalQueryForModules(t *testing.T) {
|
||||
type args struct {
|
||||
modules []model.FeedCollection
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want dbx.Expression
|
||||
}{
|
||||
{
|
||||
name: "empty modules",
|
||||
args: args{modules: []model.FeedCollection{}},
|
||||
want: dbx.HashExp{},
|
||||
},
|
||||
{
|
||||
name: "one module",
|
||||
args: args{modules: []model.FeedCollection{{Name: "test", Course: "test"}}},
|
||||
want: dbx.And(dbx.HashExp{"Name": "test"}, dbx.HashExp{"course": "test"}),
|
||||
},
|
||||
{
|
||||
name: "two modules",
|
||||
args: args{modules: []model.FeedCollection{{Name: "test", Course: "test"}, {Name: "test2", Course: "test2"}}},
|
||||
want: dbx.Or(dbx.And(dbx.HashExp{"Name": "test"}, dbx.HashExp{"course": "test"}), dbx.And(dbx.HashExp{"Name": "test2"}, dbx.HashExp{"course": "test2"})),
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := buildIcalQueryForModules(tt.args.modules); !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("buildIcalQueryForModules() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
@@ -90,11 +90,6 @@ func UpdateModulesForCourse(app *pocketbase.PocketBase, course string) error {
|
||||
|
||||
seminarGroups := fetch.GetSeminarGroupsEventsFromHTML(courses)
|
||||
|
||||
collection, dbError := db.FindCollection(app, "events")
|
||||
if dbError != nil {
|
||||
return apis.NewNotFoundError("Collection not found", dbError)
|
||||
}
|
||||
|
||||
seminarGroups = fetch.ClearEmptySeminarGroups(seminarGroups)
|
||||
|
||||
seminarGroups = fetch.ReplaceEmptyEventNames(seminarGroups)
|
||||
@@ -120,7 +115,7 @@ func UpdateModulesForCourse(app *pocketbase.PocketBase, course string) error {
|
||||
|
||||
//if there are no events in the database, save the new events
|
||||
if len(events) == 0 {
|
||||
_, dbError = db.SaveEvents(seminarGroups, collection, app)
|
||||
_, dbError := db.SaveEvents(seminarGroups, app)
|
||||
if dbError != nil {
|
||||
return apis.NewNotFoundError("Events could not be saved", dbError)
|
||||
}
|
||||
@@ -146,7 +141,7 @@ func UpdateModulesForCourse(app *pocketbase.PocketBase, course string) error {
|
||||
}
|
||||
|
||||
//save the new events
|
||||
_, dbError = db.SaveEvents(seminarGroups, collection, app)
|
||||
_, dbError := db.SaveEvents(seminarGroups, app)
|
||||
if dbError != nil {
|
||||
return apis.NewNotFoundError("Events could not be saved", dbError)
|
||||
}
|
||||
|
@@ -12,6 +12,8 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
|
||||
"github.com/labstack/echo/v5"
|
||||
"github.com/pocketbase/pocketbase"
|
||||
"github.com/pocketbase/pocketbase/apis"
|
||||
@@ -24,19 +26,14 @@ func GetSeminarEvents(c echo.Context, app *pocketbase.PocketBase) error {
|
||||
|
||||
seminarGroups := GetSeminarGroupsEventsFromHTML(seminarGroupsLabel)
|
||||
|
||||
collection, dbError := db.FindCollection(app, "events")
|
||||
if dbError != nil {
|
||||
return apis.NewNotFoundError("Collection not found", dbError)
|
||||
}
|
||||
|
||||
seminarGroups = ClearEmptySeminarGroups(seminarGroups)
|
||||
|
||||
seminarGroups = ReplaceEmptyEventNames(seminarGroups)
|
||||
|
||||
savedRecords, dbError := db.SaveEvents(seminarGroups, collection, app)
|
||||
savedRecords, dbError := db.SaveEvents(seminarGroups, app)
|
||||
|
||||
if dbError != nil {
|
||||
return apis.NewNotFoundError("Events could not be saved", dbError)
|
||||
return apis.NewNotFoundError("Events could not be saved", dbError.Error())
|
||||
}
|
||||
|
||||
return c.JSON(http.StatusOK, savedRecords)
|
||||
@@ -118,6 +115,7 @@ func parseSeminarGroup(result string) model.SeminarGroup {
|
||||
semesterString := findFirstSpanWithClass(table, "header-0-2-0").FirstChild.Data
|
||||
semester, year := extractSemesterAndYear(semesterString)
|
||||
events = convertWeeksToDates(events, semester, year)
|
||||
events = generateUUIDs(events)
|
||||
events = splitEventType(events)
|
||||
var seminarGroup = model.SeminarGroup{
|
||||
University: findFirstSpanWithClass(table, "header-1-0-0").FirstChild.Data,
|
||||
@@ -127,6 +125,16 @@ func parseSeminarGroup(result string) model.SeminarGroup {
|
||||
return seminarGroup
|
||||
}
|
||||
|
||||
func generateUUIDs(events []model.Event) []model.Event {
|
||||
for i, event := range events {
|
||||
// generate a hash value from the event name, course and semester
|
||||
hash := uuid.NewSHA1(uuid.NameSpaceOID, []byte(event.Name+event.Course+event.Semester))
|
||||
events[i].UUID = hash.String()
|
||||
}
|
||||
return events
|
||||
|
||||
}
|
||||
|
||||
func convertWeeksToDates(events []model.Event, semester string, year string) []model.Event {
|
||||
var newEvents []model.Event
|
||||
eventYear, _ := strconv.Atoi(year)
|
||||
|
@@ -16,7 +16,6 @@ func Test_extractSemesterAndYear(t *testing.T) {
|
||||
want string
|
||||
want1 string
|
||||
}{
|
||||
// TODO: Add test cases.
|
||||
{
|
||||
name: "Test 1",
|
||||
args: args{
|
||||
|
@@ -52,7 +52,7 @@ func replaceNameIfUserDefined(event *model.Event, mapping []model.FeedCollection
|
||||
return event.Name
|
||||
}
|
||||
|
||||
func generateDescription(event *model.Event) string {
|
||||
func generateDescription(event model.Event) string {
|
||||
var description string
|
||||
|
||||
if !functions.OnlyWhitespace(event.Notes) {
|
||||
|
Reference in New Issue
Block a user