mirror of
https://gitlab.dit.htwk-leipzig.de/htwk-software/htwkalender-pwa.git
synced 2025-07-16 09:38:51 +02:00
278 lines
7.4 KiB
Go
278 lines
7.4 KiB
Go
package fetch
|
|
|
|
import (
|
|
"fmt"
|
|
"github.com/labstack/echo/v5"
|
|
"github.com/pocketbase/pocketbase"
|
|
"github.com/pocketbase/pocketbase/apis"
|
|
"golang.org/x/net/html"
|
|
"htwk-planner/model"
|
|
"htwk-planner/service/date"
|
|
"htwk-planner/service/db"
|
|
"io"
|
|
"net/http"
|
|
"regexp"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
func GetSeminarEvents(c echo.Context, app *pocketbase.PocketBase) error {
|
|
|
|
seminarGroupsLabel := db.GetAllCourses(app)
|
|
|
|
seminarGroups := GetSeminarGroupsEventsFromHTML(seminarGroupsLabel)
|
|
|
|
collection, dbError := db.FindCollection(app, "events")
|
|
if dbError != nil {
|
|
return apis.NewNotFoundError("Collection not found", dbError)
|
|
}
|
|
|
|
seminarGroups = clearEmptySeminarGroups(seminarGroups)
|
|
|
|
savedRecords, dbError := db.SaveEvents(seminarGroups, collection, app)
|
|
|
|
if dbError != nil {
|
|
return apis.NewNotFoundError("Events could not be saved", dbError)
|
|
}
|
|
|
|
return c.JSON(http.StatusOK, savedRecords)
|
|
}
|
|
|
|
func clearEmptySeminarGroups(seminarGroups []model.SeminarGroup) []model.SeminarGroup {
|
|
var newSeminarGroups []model.SeminarGroup
|
|
for _, seminarGroup := range seminarGroups {
|
|
if len(seminarGroup.Events) > 0 && seminarGroup.Course != "" {
|
|
newSeminarGroups = append(newSeminarGroups, seminarGroup)
|
|
}
|
|
}
|
|
return newSeminarGroups
|
|
}
|
|
|
|
func GetSeminarGroupsEventsFromHTML(seminarGroupsLabel []string) []model.SeminarGroup {
|
|
var seminarGroups []model.SeminarGroup
|
|
for _, seminarGroupLabel := range seminarGroupsLabel {
|
|
|
|
result, getError := getPlanHTML("ss", seminarGroupLabel)
|
|
if getError == nil {
|
|
seminarGroup := parseSeminarGroup(result)
|
|
seminarGroups = append(seminarGroups, seminarGroup)
|
|
}
|
|
|
|
result, getError = getPlanHTML("ws", seminarGroupLabel)
|
|
if getError == nil {
|
|
seminarGroup := parseSeminarGroup(result)
|
|
seminarGroups = append(seminarGroups, seminarGroup)
|
|
}
|
|
}
|
|
return seminarGroups
|
|
}
|
|
|
|
func parseSeminarGroup(result string) model.SeminarGroup {
|
|
doc, err := html.Parse(strings.NewReader(result))
|
|
if err != nil {
|
|
fmt.Printf("Error occurred while parsing the HTML document: %s\n", err.Error())
|
|
return model.SeminarGroup{}
|
|
}
|
|
|
|
table := findFirstTable(doc)
|
|
eventTables := getEventTables(doc)
|
|
allDayLabels := getAllDayLabels(doc)
|
|
|
|
if eventTables == nil || allDayLabels == nil {
|
|
return model.SeminarGroup{}
|
|
}
|
|
|
|
eventsWithCombinedWeeks := toEvents(eventTables, allDayLabels)
|
|
splitEventsByWeekVal := splitEventsByWeek(eventsWithCombinedWeeks)
|
|
events := splitEventsBySingleWeek(splitEventsByWeekVal)
|
|
semesterString := findFirstSpanWithClass(table, "header-0-2-0").FirstChild.Data
|
|
semester, year := extractSemesterAndYear(semesterString)
|
|
events = convertWeeksToDates(events, semester, year)
|
|
var seminarGroup = model.SeminarGroup{
|
|
University: findFirstSpanWithClass(table, "header-1-0-0").FirstChild.Data,
|
|
Course: findFirstSpanWithClass(table, "header-2-0-1").FirstChild.Data,
|
|
Events: events,
|
|
}
|
|
return seminarGroup
|
|
}
|
|
|
|
func convertWeeksToDates(events []model.Event, semester string, year string) []model.Event {
|
|
var newEvents []model.Event
|
|
eventYear, _ := strconv.Atoi(year)
|
|
|
|
// for each event we need to calculate the start and end date based on the week and the year
|
|
for _, event := range events {
|
|
eventWeek, _ := strconv.Atoi(event.Week)
|
|
eventDay, _ := date.GetDateFromWeekNumber(eventYear, eventWeek, event.Day)
|
|
start := addTimeToDate(eventDay, event.Start)
|
|
end := addTimeToDate(eventDay, event.End)
|
|
newEvent := event
|
|
newEvent.Start = start.String()
|
|
newEvent.End = end.String()
|
|
newEvent.Semester = semester
|
|
newEvents = append(newEvents, newEvent)
|
|
}
|
|
return newEvents
|
|
}
|
|
|
|
func addTimeToDate(date time.Time, timeString string) time.Time {
|
|
//convert time string to time
|
|
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, time.UTC)
|
|
}
|
|
|
|
func extractSemesterAndYear(semesterString string) (string, string) {
|
|
winterPattern := "Wintersemester"
|
|
summerPattern := "Sommersemester"
|
|
|
|
winterMatch := strings.Contains(semesterString, winterPattern)
|
|
summerMatch := strings.Contains(semesterString, summerPattern)
|
|
|
|
semester := ""
|
|
semesterShortcut := ""
|
|
|
|
if winterMatch {
|
|
semester = "Wintersemester"
|
|
semesterShortcut = "ws"
|
|
} else if summerMatch {
|
|
semester = "Sommersemester"
|
|
semesterShortcut = "ss"
|
|
} else {
|
|
return "", ""
|
|
}
|
|
|
|
yearPattern := `\d{4}`
|
|
combinedPattern := semester + `\s` + yearPattern
|
|
re := regexp.MustCompile(combinedPattern)
|
|
match := re.FindString(semesterString)
|
|
year := ""
|
|
|
|
if match != "" {
|
|
reYear := regexp.MustCompile(yearPattern)
|
|
year = reYear.FindString(match)
|
|
}
|
|
return semesterShortcut, year
|
|
}
|
|
|
|
func toEvents(tables [][]*html.Node, days []string) []model.Event {
|
|
var events []model.Event
|
|
|
|
for table := range tables {
|
|
for row := range tables[table] {
|
|
|
|
tableData := findTableData(tables[table][row])
|
|
if len(tableData) > 0 {
|
|
events = append(events, model.Event{
|
|
Day: days[table],
|
|
Week: getTextContent(tableData[0]),
|
|
Start: getTextContent(tableData[1]),
|
|
End: getTextContent(tableData[2]),
|
|
Name: getTextContent(tableData[3]),
|
|
EventType: getTextContent(tableData[4]),
|
|
Prof: getTextContent(tableData[5]),
|
|
Rooms: getTextContent(tableData[6]),
|
|
Notes: getTextContent(tableData[7]),
|
|
BookedAt: getTextContent(tableData[8]),
|
|
})
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
return events
|
|
}
|
|
|
|
func splitEventsByWeek(events []model.Event) []model.Event {
|
|
var newEvents []model.Event
|
|
|
|
for _, event := range events {
|
|
weeks := strings.Split(event.Week, ",")
|
|
for _, week := range weeks {
|
|
newEvent := event
|
|
newEvent.Week = strings.TrimSpace(week)
|
|
newEvents = append(newEvents, newEvent)
|
|
}
|
|
}
|
|
return newEvents
|
|
}
|
|
|
|
func splitEventsBySingleWeek(events []model.Event) []model.Event {
|
|
var newEvents []model.Event
|
|
|
|
for _, event := range events {
|
|
if strings.Contains(event.Week, "-") {
|
|
weeks := splitWeekRange(event.Week)
|
|
for _, week := range weeks {
|
|
newEvent := event
|
|
newEvent.Week = week
|
|
newEvents = append(newEvents, newEvent)
|
|
}
|
|
} else {
|
|
newEvents = append(newEvents, event)
|
|
}
|
|
}
|
|
return newEvents
|
|
}
|
|
|
|
func splitWeekRange(weekRange string) []string {
|
|
parts := strings.Split(weekRange, "-")
|
|
if len(parts) != 2 {
|
|
return nil // Invalid format
|
|
}
|
|
|
|
start, errStart := strconv.Atoi(strings.TrimSpace(parts[0]))
|
|
end, errEnd := strconv.Atoi(strings.TrimSpace(parts[1]))
|
|
|
|
if errStart != nil || errEnd != nil {
|
|
return nil // Error converting to integers
|
|
}
|
|
|
|
var weeks []string
|
|
for i := start; i <= end; i++ {
|
|
weeks = append(weeks, strconv.Itoa(i))
|
|
}
|
|
|
|
return weeks
|
|
}
|
|
|
|
func toUtf8(iso88591Buf []byte) string {
|
|
buf := make([]rune, len(iso88591Buf))
|
|
for i, b := range iso88591Buf {
|
|
buf[i] = rune(b)
|
|
}
|
|
return string(buf)
|
|
}
|
|
|
|
// getPlanHTML Get the HTML document from the specified URL
|
|
func getPlanHTML(semester string, matrikel string) (string, error) {
|
|
url := "https://stundenplan.htwk-leipzig.de/" + string(semester) + "/Berichte/Text-Listen;Studenten-Sets;name;" + matrikel + "?template=sws_semgrp&weeks=1-65"
|
|
|
|
// Send GET request
|
|
response, err := http.Get(url)
|
|
if err != nil {
|
|
fmt.Printf("Error occurred while making the request: %s\n", err.Error())
|
|
return "", err
|
|
}
|
|
defer func(Body io.ReadCloser) {
|
|
err := Body.Close()
|
|
if err != nil {
|
|
return
|
|
}
|
|
}(response.Body)
|
|
|
|
// Read the response body
|
|
body, err := io.ReadAll(response.Body)
|
|
|
|
if err != nil {
|
|
fmt.Printf("Error occurred while reading the response: %s\n", err.Error())
|
|
return "", err
|
|
}
|
|
|
|
return toUtf8(body), err
|
|
|
|
}
|