//Calendar implementation for the HTWK Leipzig timetable. Evaluation and display of the individual dates in iCal format. //Copyright (C) 2024 HTWKalender support@htwkalender.de //This program is free software: you can redistribute it and/or modify //it under the terms of the GNU Affero General Public License as published by //the Free Software Foundation, either version 3 of the License, or //(at your option) any later version. //This program is distributed in the hope that it will be useful, //but WITHOUT ANY WARRANTY; without even the implied warranty of //MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the //GNU Affero General Public License for more details. //You should have received a copy of the GNU Affero General Public License //along with this program. If not, see . package v1 import ( "encoding/xml" "fmt" "github.com/pocketbase/pocketbase" "htwkalender/data-manager/model" "htwkalender/data-manager/service/db" "htwkalender/data-manager/service/functions" "htwkalender/data-manager/service/functions/time" "io" "log/slog" "net/http" ) func getSeminarHTML(semester string) (string, error) { url := "https://stundenplan.htwk-leipzig.de/stundenplan/xml/public/semgrp_" + semester + ".xml" // 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 string(body), err } func FetchSeminarGroups(base *pocketbase.PocketBase) (db.SeminarGroups, error) { var groups db.SeminarGroups semesterString := functions.CalculateSemesterList(time.RealClock{}) var results [2]string var err error for i, semester := range semesterString { results[i], err = getSeminarHTML(semester) if err != nil { slog.Error("Error while fetching seminar groups for: "+semester, "error", err) return nil, err } groups = append(groups, parseSeminarGroups(results[i], semester)...) } // filter duplicates groups = removeDuplicates(groups) insertedGroups, dbError := db.SaveGroups(base.App, groups) if dbError != nil { slog.Error("FetchSeminarGroups", "error", dbError) return nil, err } return insertedGroups, nil } func removeDuplicates(groups db.SeminarGroups) db.SeminarGroups { uniqueGroups := make(db.SeminarGroups, 0, len(groups)) seen := make(map[string]struct{}) // Use an empty struct to minimize memory usage // unique Identifier is the course and semester for _, group := range groups { key := group.UniqueKey() if _, exists := seen[key]; !exists { seen[key] = struct{}{} uniqueGroups = append(uniqueGroups, group) } } return uniqueGroups } func contains(groups []model.SeminarGroup, group model.SeminarGroup) bool { for _, a := range groups { if (a.Course == group.Course) && (a.Semester == group.Semester) { return true } } return false } func parseSeminarGroups(result string, semester string) db.SeminarGroups { var studium model.Studium err := xml.Unmarshal([]byte(result), &studium) if err != nil { return nil } var seminarGroups db.SeminarGroups for _, faculty := range studium.Faculty { for _, Studiengang := range faculty.Studiengang { for _, Studienrichtung := range Studiengang.Semgrp { seminarGroup := db.SeminarGroup{ University: "HTWK-Leipzig", GroupShortcut: Studiengang.Name, GroupId: Studiengang.ID, Course: Studienrichtung.Name, Faculty: faculty.Name, FacultyId: faculty.ID, Semester: semester, } seminarGroups = append(seminarGroups, &seminarGroup) } } } return seminarGroups }