From 79203e5c5b053146c56ad9e5063f72683590c719 Mon Sep 17 00:00:00 2001 From: KoCoder Date: Fri, 20 Mar 2026 21:49:31 +0100 Subject: [PATCH 1/2] Timetracking Jobs --- scripts/timetracking/main.go | 146 +++++++++++++++++++++++++++++++++++ 1 file changed, 146 insertions(+) create mode 100644 scripts/timetracking/main.go diff --git a/scripts/timetracking/main.go b/scripts/timetracking/main.go new file mode 100644 index 0000000..0702fb0 --- /dev/null +++ b/scripts/timetracking/main.go @@ -0,0 +1,146 @@ +package main + +import ( + "fmt" + "log" + "os" + "sort" + "strings" + "time" + + "code.gitea.io/sdk/gitea" +) + +func main() { + url := os.Getenv("GITEA_URL") + token := os.Getenv("GITEA_TOKEN") + repoFullName := os.Getenv("GITEA_REPO") // format: owner/repo + orgName := os.Getenv("GITEA_ORG") + + if url == "" || token == "" { + log.Fatal("Missing required environment variables: GITEA_URL, GITEA_TOKEN") + } + + if repoFullName == "" && orgName == "" { + log.Fatal("Either GITEA_REPO or GITEA_ORG must be set") + } + + client, err := gitea.NewClient(url, gitea.SetToken(token)) + if err != nil { + log.Fatalf("Failed to create Gitea client: %v", err) + } + + var repos []*gitea.Repository + if orgName != "" { + fmt.Printf("Fetching all repositories for organization: %s\n", orgName) + page := 1 + for { + orgRepos, _, err := client.ListOrgRepos(orgName, gitea.ListOrgReposOptions{ + ListOptions: gitea.ListOptions{Page: page, PageSize: 50}, + }) + if err != nil { + log.Fatalf("Failed to fetch organization repositories: %v", err) + } + if len(orgRepos) == 0 { + break + } + repos = append(repos, orgRepos...) + page++ + } + } else { + parts := strings.Split(repoFullName, "/") + if len(parts) != 2 { + log.Fatal("GITEA_REPO must be in format 'owner/repo'") + } + repo, _, err := client.GetRepo(parts[0], parts[1]) + if err != nil { + log.Fatalf("Failed to fetch repository: %v", err) + } + repos = append(repos, repo) + } + + // Start date for Sprint 1 (2024-10-02) + startDate := time.Date(2025, 5, 2, 0, 0, 0, 0, time.Local) + + // user -> bucketIndex -> hours + data := make(map[string]map[int]float64) + maxBucket := 0 + + for _, r := range repos { + fmt.Printf("Processing repository: %s/%s\n", r.Owner.UserName, r.Name) + page := 1 + for { + times, _, err := client.ListRepoTrackedTimes(r.Owner.UserName, r.Name, gitea.ListTrackedTimesOptions{ + ListOptions: gitea.ListOptions{Page: page, PageSize: 50}, + }) + if err != nil { + fmt.Printf("Warning: Failed to fetch time tracking for %s (page %d): %v\n", r.Name, page, err) + break + } + if len(times) == 0 { + break + } + + for _, t := range times { + if t.Created.Before(startDate) { + continue + } + + daysSinceStart := int(t.Created.Sub(startDate).Hours() / 24) + bucketIndex := daysSinceStart / 14 + if bucketIndex < 0 { + continue + } + + if bucketIndex > maxBucket { + maxBucket = bucketIndex + } + + userName := t.UserName + if data[userName] == nil { + data[userName] = make(map[int]float64) + } + + hours := float64(t.Time) / 3600.0 + data[userName][bucketIndex] += hours + } + page++ + } + } + + if len(data) == 0 { + fmt.Println("No time tracking entries found.") + return + } + + // Print Table + users := make([]string, 0, len(data)) + for u := range data { + users = append(users, u) + } + sort.Strings(users) + + // Header + fmt.Print("\nNutzer ") + for i := 0; i <= maxBucket; i++ { + start := startDate.AddDate(0, 0, i*14) + fmt.Printf("| %s ", start.Format("02.01.")) + } + fmt.Println("| Gesamt") + fmt.Println(strings.Repeat("-", 14+(maxBucket+1)*10+8)) + + for _, u := range users { + fmt.Printf("%-12s ", u) + total := 0.0 + for i := 0; i <= maxBucket; i++ { + h := data[u][i] + total += h + if h > 0 { + fmt.Printf("| %7.1fh ", h) + } else { + fmt.Printf("| %8s ", "-") + } + } + fmt.Printf("| %7.1fh\n", total) + } +} From 18687087f71ba06e5a0df218ff169e3844e64e6a Mon Sep 17 00:00:00 2001 From: KoCoder Date: Fri, 20 Mar 2026 21:50:17 +0100 Subject: [PATCH 2/2] Newlines im Preamble --- 00-praemble.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/00-praemble.md b/00-praemble.md index f7ef4af..e0cc1bd 100644 --- a/00-praemble.md +++ b/00-praemble.md @@ -29,16 +29,18 @@ Als wir im Oktober kurzfristig eine neue Produktionsumgebung benötigten, wurde Für den „frischen Wind“ und das sorgfältige Korrekturlesen der Arbeit bedanken wir uns herzlich bei Frau Prof. Gertrude Brindlmayer. Ihr geschulter Blick auf Details, die dem Team im Arbeitsprozess entgangen waren, stellte eine große Bereicherung für die finale Qualität dieser Arbeit dar. +\newpage + # Vorwort {.unnumbered .unlisted} -Diese Diplomarbeit ist im Schuljahr 2025/2026 an der HTL SZU Ungargasse im Rahmen des Projekts Alumnihub entstanden. Unser Ziel war es, zusammen mit dem Absolventenverein die veraltete Mitgliederverwaltung zu modernisieren und eine neue digitale Plattform für ehemalige Schülerinnen und Schüler zu entwickeln. Was am Anfang nur eine Pflichtaufgabe für die Schule war, wurde für uns schnell zu einem Projekt, mit dem wir uns voll identifiziert haben.Besonders herausfordernd war, dass unser Team im Laufe der Zeit kleiner wurde und wir am Ende nur noch zu dritt waren. Konstantin Hintermayer, Florian Edlmayer und Adam Gaiswinkler mussten deshalb viel mehr Verantwortung übernehmen und technisches Wissen schneller aufbauen als eigentlich geplant. Die Arbeit am AlumniHub war für uns die ideale Chance, das Wissen aus der Theorie endlich mal in einem echten und anspruchsvollen Softwareprojekt einzusetzen. Es war eine stressige, aber sehr lehrreiche Zeit, und wir sind stolz auf das, was wir am Ende als Team abgeliefert haben. +Diese Diplomarbeit ist im Schuljahr 2025/2026 an der HTL SZU Ungargasse im Rahmen des Projekts Alumnihub entstanden. Unser Ziel war es, zusammen mit dem Absolventenverein die veraltete Mitgliederverwaltung zu modernisieren und eine neue digitale Plattform für ehemalige Schülerinnen und Schüler zu entwickeln. Was am Anfang nur eine Pflichtaufgabe für die Schule war, wurde für uns schnell zu einem Projekt, mit dem wir uns voll identifiziert haben. Besonders herausfordernd war, dass unser Team im Laufe der Zeit kleiner wurde und wir am Ende nur noch zu dritt waren. Konstantin Hintermayer, Florian Edlmayer und Adam Gaiswinkler mussten deshalb viel mehr Verantwortung übernehmen und technisches Wissen schneller aufbauen als eigentlich geplant. Die Arbeit am AlumniHub war für uns die ideale Chance, das Wissen aus der Theorie endlich mal in einem echten und anspruchsvollen Softwareprojekt einzusetzen. Es war eine stressige, aber sehr lehrreiche Zeit, und wir sind stolz auf das, was wir am Ende als Team abgeliefert haben. -# Einsatz Künstlicher Intelligenz (KI-Disclaimer) +\newpage + +# Einsatz Künstlicher Intelligenz (KI-Disclaimer) {.unnumbered .unlisted} Diese Diplomarbeit und die Software Alumnihub wurden von uns eigenständig entwickelt. KI-Tools haben wir lediglich als Unterstützung für Aufgaben wie Korrekturlesen, Übersetzungen oder zur gezielten Recherche genutzt. Auch beim Debugging im Code war die KI eine Hilfe. Der eigentliche Quellcode, die inhaltliche Ausarbeitung und alle technischen Entscheidungen stammen jedoch direkt von uns. Sämtliche KI-Vorschläge wurden von uns kritisch hinterfragt und nur nach eigener Prüfung übernommen. - - \cleardoublepage \pagestyle{empty} \pagenumbering{arabic}