Merge branch 'main' of https://git.kocoder.xyz/Diplomarbeit-Absolventenverein/pm
Some checks failed
Word Count / count-words (push) Failing after 30s

This commit is contained in:
2026-03-20 21:57:04 +01:00
2 changed files with 152 additions and 4 deletions

View File

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

View File

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