Merge branch '324-fix-event-update-inconsistency' into 'development'

test:#324 change eventService and fetch routine

See merge request htwk-software/htwkalender!6
This commit is contained in:
Elmar Kresse
2024-05-17 18:30:35 +00:00
14 changed files with 102 additions and 73 deletions

View File

@@ -41,6 +41,6 @@ func main() {
service.AddSchedules(app)
if err := app.Start(); err != nil {
slog.Error("Failed to start app: %v", err)
slog.Error("Failed to start app: ", "error", err)
}
}

View File

@@ -17,6 +17,7 @@
package service
import (
"htwkalender/service/course"
"htwkalender/service/events"
"htwkalender/service/fetch/sport"
v1 "htwkalender/service/fetch/v1"
@@ -41,7 +42,7 @@ func AddRoutes(app *pocketbase.PocketBase) {
Handler: func(c echo.Context) error {
savedEvents, err := v2.ParseEventsFromRemote(app)
if err != nil {
slog.Error("Failed to parse events from remote: %v", err)
slog.Error("Failed to parse events from remote: ", "error", err)
return c.JSON(http.StatusBadRequest, "Failed to parse events from remote")
} else {
return c.JSON(http.StatusOK, savedEvents)
@@ -58,6 +59,25 @@ func AddRoutes(app *pocketbase.PocketBase) {
return nil
})
app.OnBeforeServe().Add(func(e *core.ServeEvent) error {
_, err := e.Router.AddRoute(echo.Route{
Method: http.MethodGet,
Path: "/api/fetch/daily/events",
Handler: func(c echo.Context) error {
course.UpdateCourse(app)
return c.JSON(http.StatusOK, "Daily events fetched")
},
Middlewares: []echo.MiddlewareFunc{
apis.ActivityLogger(app),
apis.RequireAdminAuth(),
},
})
if err != nil {
return err
}
return nil
})
app.OnBeforeServe().Add(func(e *core.ServeEvent) error {
_, err := e.Router.AddRoute(echo.Route{
Method: http.MethodGet,
@@ -156,7 +176,7 @@ func AddRoutes(app *pocketbase.PocketBase) {
date := c.QueryParam("date")
roomSchedule, err := room.GetRoomScheduleForDay(app, roomParam, date)
if err != nil {
slog.Error("Failed to get room schedule for day: %v", err)
slog.Error("Failed to get room schedule for day: ", "error", err)
return c.JSON(http.StatusBadRequest, "Failed to get room schedule for day")
}
return c.JSON(http.StatusOK, roomSchedule)
@@ -182,7 +202,7 @@ func AddRoutes(app *pocketbase.PocketBase) {
from := c.QueryParam("from")
roomSchedule, err := room.GetRoomSchedule(app, roomParam, from, to)
if err != nil {
slog.Error("Failed to get room schedule: %v", err)
slog.Error("Failed to get room schedule:", "error", err)
return c.JSON(http.StatusBadRequest, "Failed to get room schedule")
}
return c.JSON(http.StatusOK, roomSchedule)
@@ -205,17 +225,17 @@ func AddRoutes(app *pocketbase.PocketBase) {
Handler: func(c echo.Context) error {
from, err := time.ParseTime(c.QueryParam("from"))
if err != nil {
slog.Error("Failed to parse time: %v", err)
slog.Error("Failed to parse time: ", "error", err)
return c.JSON(http.StatusBadRequest, "Failed to parse time")
}
to, err := time.ParseTime(c.QueryParam("to"))
if err != nil {
slog.Error("Failed to parse time: %v", err)
slog.Error("Failed to parse time: ", "error", err)
return c.JSON(http.StatusBadRequest, "Failed to parse time")
}
rooms, err := room.GetFreeRooms(app, from, to)
if err != nil {
slog.Error("Failed to get free rooms: %v", err)
slog.Error("Failed to get free rooms: ", "error", err)
return c.JSON(http.StatusBadRequest, "Failed to get free rooms")
}
return c.JSON(http.StatusOK, rooms)
@@ -237,12 +257,14 @@ func AddRoutes(app *pocketbase.PocketBase) {
Method: http.MethodGet,
Path: "/api/course/modules",
Handler: func(c echo.Context) error {
course := c.QueryParam("course")
semester := c.QueryParam("semester")
modules, err := events.GetModulesForCourseDistinct(app, course, semester)
modules, err := events.GetModulesForCourseDistinct(
app,
c.QueryParam("course"),
c.QueryParam("semester"),
)
if err != nil {
slog.Error("Failed to get modules for course and semester: %v", err)
slog.Error("Failed to get modules for course and semester: ", "error", err)
return c.JSON(http.StatusBadRequest, "Failed to get modules for course and semester")
} else {
return c.JSON(http.StatusOK, modules)
@@ -265,7 +287,7 @@ func AddRoutes(app *pocketbase.PocketBase) {
Handler: func(c echo.Context) error {
modules, err := events.GetAllModulesDistinct(app)
if err != nil {
slog.Error("Failed to get modules: %v", err)
slog.Error("Failed to get modules: ", "error", err)
return c.JSON(http.StatusBadRequest, "Failed to get modules")
}
return c.JSON(http.StatusOK, modules)
@@ -288,7 +310,7 @@ func AddRoutes(app *pocketbase.PocketBase) {
requestModule := c.QueryParam("uuid")
module, err := events.GetModuleByUUID(app, requestModule)
if err != nil {
slog.Error("Failed to get module: %v", err)
slog.Error("Failed to get module: ", "error", err)
return c.JSON(http.StatusBadRequest, "Failed to get module")
} else {
return c.JSON(http.StatusOK, module)
@@ -338,7 +360,7 @@ func AddRoutes(app *pocketbase.PocketBase) {
courses, err := events.GetAllCoursesForSemesterWithEvents(app, semester)
if err != nil {
slog.Error("Failed to get courses for semester with events: %v", err)
slog.Error("Failed to get courses for semester with events: ", "error", err)
return c.JSON(http.StatusBadRequest, "Failed to get courses for semester with events")
} else {
return c.JSON(200, courses)
@@ -359,11 +381,13 @@ func AddRoutes(app *pocketbase.PocketBase) {
Method: http.MethodDelete,
Path: "/api/events",
Handler: func(c echo.Context) error {
course := c.QueryParam("course")
semester := c.QueryParam("semester")
err := events.DeleteAllEventsByCourseAndSemester(app, course, semester)
err := events.DeleteAllEventsByCourseAndSemester(
app,
c.QueryParam("course"),
c.QueryParam("semester"),
)
if err != nil {
slog.Error("Failed to delete events: %v", err)
slog.Error("Failed to delete events: ", "error", err)
return c.JSON(http.StatusBadRequest, "Failed to delete events")
} else {
return c.JSON(http.StatusBadRequest, "Events deleted")

View File

@@ -42,7 +42,7 @@ func AddSchedules(app *pocketbase.PocketBase) {
slog.Info("Started updating courses schedule")
groups, err := v1.FetchSeminarGroups(app)
if err != nil {
slog.Warn("Failed to fetch seminar groups: %v", err)
slog.Warn("Failed to fetch seminar groups: ", "error", err)
}
slog.Info("Successfully fetched " + strconv.FormatInt(int64(len(groups)), 10) + " seminar groups")
})
@@ -66,7 +66,7 @@ func AddSchedules(app *pocketbase.PocketBase) {
slog.Info("Started fetching sport events schedule")
sportEvents, err := sport.FetchAndUpdateSportEvents(app)
if err != nil {
slog.Error("Failed to fetch and save sport events: %v", err)
slog.Error("Failed to fetch and save sport events:", "error", err)
}
slog.Info("Successfully fetched " + strconv.FormatInt(int64(len(sportEvents)), 10) + " sport events")
})
@@ -75,7 +75,7 @@ func AddSchedules(app *pocketbase.PocketBase) {
scheduler.MustAdd("fetchEvents", "0 22 * * 6", func() {
savedEvents, err := v2.FetchAllEventsAndSave(app, time.RealClock{})
if err != nil {
slog.Error("Failed to fetch and save events: %v", err)
slog.Error("Failed to fetch and save events: ", "error", err)
} else {
slog.Info("Successfully fetched " + strconv.FormatInt(int64(len(savedEvents)), 10) + " events")
}

View File

@@ -20,17 +20,14 @@ import (
"github.com/pocketbase/pocketbase"
"htwkalender/service/events"
"log/slog"
"strconv"
)
func UpdateCourse(app *pocketbase.PocketBase) {
courses := events.GetAllCourses(app)
for _, course := range courses {
savedEvents, err := events.UpdateModulesForCourse(app, course)
_, err := events.UpdateModulesForCourse(app, course)
if err != nil {
slog.Warn("Update Course: "+course+" failed: %v", err)
} else {
slog.Info("Updated Course: " + course + " with " + strconv.FormatInt(int64(len(savedEvents)), 10) + " events")
slog.Warn("Update Course: "+course+" failed:", "error", err)
}
}
}

View File

@@ -29,7 +29,7 @@ func GetDateFromWeekNumber(year int, weekNumber int, dayName string) (time.Time,
europeTime, err := time.LoadLocation("Europe/Berlin")
if err != nil {
slog.Error("Failed to load location: ", err)
slog.Error("Failed to load location: ", "error", err)
return time.Time{}, err
}

View File

@@ -192,13 +192,26 @@ func GetPlanForModules(app *pocketbase.PocketBase, modules map[string]model.Feed
return events, nil
}
func GetAllEventsForCourse(app *pocketbase.PocketBase, course string) (model.Events, error) {
var events model.Events
// get all events from event records in the events collection
err := app.Dao().DB().Select("*").From("events").Where(dbx.NewExp("course = {:course}", dbx.Params{"course": course})).All(&events)
if err != nil {
slog.Error("Error while getting events from database: ", "error", err)
return nil, fmt.Errorf("error while getting events from database for course %s", course)
}
return events, nil
}
func GetAllModulesForCourse(app *pocketbase.PocketBase, course string, semester string) (model.Events, error) {
var events model.Events
// get all events from event records in the events collection
err := app.Dao().DB().Select("*").From("events").Where(dbx.NewExp("course = {:course} AND semester = {:semester}", dbx.Params{"course": course, "semester": semester})).GroupBy("Name").Distinct(true).All(&events)
if err != nil {
slog.Error("Error while getting events from database: ", err)
slog.Error("Error while getting events from database: ", "error", err)
return nil, fmt.Errorf("error while getting events from database for course %s and semester %s", course, semester)
}
@@ -210,7 +223,7 @@ func GetAllModulesDistinctByNameAndCourse(app *pocketbase.PocketBase) ([]model.M
err := app.Dao().DB().Select("Name", "EventType", "Prof", "course", "semester", "uuid").From("events").GroupBy("Name", "Course").Distinct(true).All(&modules)
if err != nil {
slog.Error("Error while getting events from database: ", err)
slog.Error("Error while getting events from database: ", "error", err)
return nil, fmt.Errorf("error while getting events distinct by name and course from data")
}

View File

@@ -85,7 +85,7 @@ func GetAllCourses(app *pocketbase.PocketBase) []string {
// get all rooms from event records in the events collection
err := app.Dao().DB().Select("course").From("groups").All(&courses)
if err != nil {
slog.Error("Error while getting groups from database: ", err)
slog.Error("Error while getting groups from database: ", "error", err)
return []string{}
}
@@ -107,7 +107,7 @@ func GetAllCoursesForSemester(app *pocketbase.PocketBase, semester string) []str
// get all courses for a specific semester
err := app.Dao().DB().Select("course").From("groups").Where(dbx.NewExp("semester = {:semester}", dbx.Params{"semester": semester})).All(&courses)
if err != nil {
slog.Error("Error while getting groups from database: ", err)
slog.Error("Error while getting groups from database: ", "error", err)
return []string{}
}
@@ -130,7 +130,7 @@ func GetAllCoursesForSemesterWithEvents(app *pocketbase.PocketBase, semester str
// get all courses from events distinct for a specific semester
err := app.Dao().DB().Select("course").From("events").Where(dbx.NewExp("semester = {:semester}", dbx.Params{"semester": semester})).Distinct(true).All(&courses)
if err != nil {
slog.Error("Error while getting groups from database: ", err)
slog.Error("Error while getting groups from database: ", "error", err)
return nil, err
}

View File

@@ -23,6 +23,7 @@ import (
"htwkalender/service/fetch/v1"
"htwkalender/service/functions"
"log/slog"
"strconv"
)
func GetModulesForCourseDistinct(app *pocketbase.PocketBase, course string, semester string) (model.Events, error) {
@@ -133,21 +134,13 @@ func UpdateModulesForCourse(app *pocketbase.PocketBase, course string) (model.Ev
//if there are no events in the database, save the new events
//get all events for the course and the semester
events, err := db.GetAllModulesForCourse(app, course, "ws")
dbEvents, err := db.GetAllEventsForCourse(app, course)
if err != nil {
return nil, err
}
// append all events for the course and the semester to the events array for ss
summerEvents, err := db.GetAllModulesForCourse(app, course, "ss")
if err != nil {
return nil, err
}
events = append(events, summerEvents...)
//if there are no events in the database, save the new events
if len(events) == 0 {
if len(dbEvents) == 0 {
events, dbError := db.SaveSeminarGroupEvents(seminarGroup, app)
if dbError != nil {
return nil, dbError
@@ -161,32 +154,33 @@ func UpdateModulesForCourse(app *pocketbase.PocketBase, course string) (model.Ev
// check which events are not already in the database and need to be inserted/saved
for _, event := range seminarGroup.Events {
if !ContainsEvent(events, event) {
if !ContainsEvent(dbEvents, event) {
insertList = append(insertList, event)
}
}
// save all events that are in the insertList
savedEvents, err := db.SaveEvents(insertList, app)
if err != nil {
slog.Error("Failed to save events: %v", err)
return nil, err
}
// check which events are in the database but not in the seminarGroup and need to be deleted
for _, event := range events {
if !ContainsEvent(seminarGroup.Events, event) {
deleteList = append(deleteList, event)
for _, dbEvent := range dbEvents {
if !ContainsEvent(seminarGroup.Events, dbEvent) {
deleteList = append(deleteList, dbEvent)
}
}
// delete all events that are in the deleteList
err = db.DeleteEvents(deleteList, app)
if err != nil {
slog.Error("Failed to delete events: %v", err)
slog.Error("Failed to delete events:", "error", err)
return nil, err
}
// save all events that are in the insertList
savedEvents, err := db.SaveEvents(insertList, app)
if err != nil {
slog.Error("Failed to save events: ", "error", err)
return nil, err
}
slog.Info("Course: " + course + " - Event changes: " + strconv.FormatInt(int64(len(insertList)), 10) + " new events, " + strconv.FormatInt(int64(len(deleteList)), 10) + " deleted events")
return savedEvents, nil
}

View File

@@ -30,7 +30,7 @@ import (
func ClearFeeds(db *daos.Dao, months int, clock localTime.Clock) {
feeds, err := database.GetAllFeeds(db)
if err != nil {
slog.Error("CleanFeeds: failed to get all feeds", err)
slog.Error("CleanFeeds: failed to get all feeds", "error", err)
return
}
for _, feed := range feeds {
@@ -44,8 +44,8 @@ func ClearFeeds(db *daos.Dao, months int, clock localTime.Clock) {
var sqlResult sql.Result
sqlResult, err = db.DB().Delete("feeds", dbx.NewExp("id = {:id}", dbx.Params{"id": feed.GetId()})).Execute()
if err != nil {
slog.Error("CleanFeeds: delete feed "+feed.GetId()+" failed", err)
slog.Error("SQL Result: ", sqlResult)
slog.Error("CleanFeeds: delete feed "+feed.GetId()+" failed", "error", err)
slog.Error("SQL Result: ", "error", sqlResult)
} else {
slog.Info("CleanFeeds: delete feed " + feed.GetId() + " successful")
}

View File

@@ -209,7 +209,7 @@ func getWeekEvents(start time.Time, end time.Time, cycle string) ([]time.Time, [
for _, day := range days {
weekDay, err := getDayInt(day)
if err != nil {
slog.Error("Error while getting day int: "+day+" ", err)
slog.Error("Error while getting day int: "+day+" ", "error", err)
} else {
weekEvents = append(weekEvents, model.SportDayStartEnd{
Start: time.Date(start.Year(), start.Month(), start.Day(), startHour, startMinute, 0, 0, start.Location()),
@@ -234,7 +234,8 @@ func getWeekEvents(start time.Time, end time.Time, cycle string) ([]time.Time, [
endI, endIErr = getDayInt(days[1])
if endIErr != nil || startIErr != nil {
slog.Error("Error while getting day int: "+days[0]+" - "+days[1]+" :", startIErr, endIErr)
slog.Error("StartError while getting day int: "+days[0]+" - "+days[1]+" :", "error", startIErr)
slog.Error("EndError while getting day int: "+days[0]+" - "+days[1]+" :", "error", endIErr)
} else {
//create a int array with all days from start to end day
var daysBetween []int
@@ -259,7 +260,7 @@ func getWeekEvents(start time.Time, end time.Time, cycle string) ([]time.Time, [
dayInt, err := getDayInt(day)
if err != nil {
slog.Error("Error while getting day int: "+day+" ", err)
slog.Error("Error while getting day int: "+day+" ", "error", err)
} else {
dayNumbers = append(dayNumbers, dayInt)
}
@@ -271,7 +272,7 @@ func getWeekEvents(start time.Time, end time.Time, cycle string) ([]time.Time, [
weekDay, err := getDayInt(day)
if err != nil {
slog.Error("Error while getting day int: "+day+" ", err)
slog.Error("Error while getting day int: "+day+" ", "error", err)
} else {
weekEvents = append(weekEvents, model.SportDayStartEnd{
Start: time.Date(start.Year(), start.Month(), start.Day(), startHour, startMinute, 0, 0, start.Location()),
@@ -378,7 +379,7 @@ func fetchAllAvailableSportCourses() ([]string, error) {
var doc, err = htmlRequest(url)
if err != nil {
slog.Error("Error while fetching sport courses from webpage", err)
slog.Error("Error while fetching sport courses from webpage", "error", err)
return nil, err
}
@@ -443,7 +444,7 @@ func htmlRequest(url string) (*goquery.Document, error) {
defer func(Body io.ReadCloser) {
readErr := Body.Close()
if readErr != nil {
slog.Error("Error while closing response body from html request", readErr)
slog.Error("Error while closing response body from html request", "error", readErr)
return
}
}(resp.Body)

View File

@@ -103,19 +103,18 @@ func parseSeminarGroup(result string) model.SeminarGroup {
if eventTables == nil || allDayLabels == nil {
return model.SeminarGroup{}
}
eventsWithCombinedWeeks := toEvents(eventTables, allDayLabels)
course := findFirstSpanWithClass(table, "header-2-0-1").FirstChild.Data
eventsWithCombinedWeeks := toEvents(eventTables, allDayLabels, course)
splitEventsByWeekVal := splitEventsByWeek(eventsWithCombinedWeeks)
events := splitEventsBySingleWeek(splitEventsByWeekVal)
semesterString := findFirstSpanWithClass(table, "header-0-2-0").FirstChild.Data
course := findFirstSpanWithClass(table, "header-2-0-1").FirstChild.Data
semester, year := extractSemesterAndYear(semesterString)
events = convertWeeksToDates(events, semester, year)
events = generateUUIDs(events, course)
events, err = SplitEventType(events)
if err != nil {
slog.Error("Error occurred while splitting event types: %s", err)
slog.Error("Error occurred while splitting event types:", "error", err)
return model.SeminarGroup{}
}
@@ -204,7 +203,7 @@ func extractSemesterAndYear(semesterString string) (string, string) {
return semesterShortcut, year
}
func toEvents(tables [][]*html.Node, days []string) []model.Event {
func toEvents(tables [][]*html.Node, days []string, course string) []model.Event {
var events []model.Event
for table := range tables {
@@ -225,6 +224,7 @@ func toEvents(tables [][]*html.Node, days []string) []model.Event {
Rooms: getTextContent(tableData[6]),
Notes: getTextContent(tableData[7]),
BookedAt: getTextContent(tableData[8]),
Course: course,
})
}
}

View File

@@ -68,7 +68,7 @@ func FetchSeminarGroups(app *pocketbase.PocketBase) ([]*models.Record, error) {
for i, semester := range semesterString {
results[i], err = getSeminarHTML(semester)
if err != nil {
slog.Error("Error while fetching seminar groups for: "+semester, err)
slog.Error("Error while fetching seminar groups for: "+semester, "error", err)
return nil, err
}
groups = append(groups, parseSeminarGroups(results[i], semester)...)
@@ -79,14 +79,14 @@ func FetchSeminarGroups(app *pocketbase.PocketBase) ([]*models.Record, error) {
collection, dbError := db.FindCollection(app, "groups")
if dbError != nil {
slog.Error("Error while searching collection groups", dbError)
slog.Error("Error while searching collection groups", "error", dbError)
return nil, err
}
var insertedGroups []*models.Record
insertedGroups, dbError = db.SaveGroups(groups, collection, app)
if dbError != nil {
slog.Error("Error while saving groups", dbError)
slog.Error("Error while saving groups", "error", dbError)
return nil, err
}

View File

@@ -141,7 +141,7 @@ func parseEventForOneSemester(url string) ([]model.Event, error) {
events = convertWeeksToDates(events, semester, year)
events, err = v1.SplitEventType(events)
if err != nil {
slog.Error("Error occurred while splitting event types: %s", err)
slog.Error("Error occurred while splitting event types: ", "error", err)
return nil, err
}
events = switchNameAndNotesForExam(events)

View File

@@ -29,7 +29,7 @@ func ParseTime(timeString string) (time.Time, error) {
func ParseAsTypesDatetime(time time.Time) types.DateTime {
dateTime, err := types.ParseDateTime(time)
if err != nil {
slog.Error("Failed to parse time as types.DateTime: %v", err)
slog.Error("Failed to parse time as types.DateTime", "error", err)
return types.DateTime{}
}
return dateTime