Merge remote-tracking branch 'origin/main' into 11-enhanced-module-titles

This commit is contained in:
survellow
2023-10-25 18:50:41 +02:00
41 changed files with 2280 additions and 590 deletions

View File

@@ -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
}

View 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)
}
})
}
}

View File

@@ -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)
}

View File

@@ -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)

View File

@@ -16,7 +16,6 @@ func Test_extractSemesterAndYear(t *testing.T) {
want string
want1 string
}{
// TODO: Add test cases.
{
name: "Test 1",
args: args{

View File

@@ -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) {