From 79203e5c5b053146c56ad9e5063f72683590c719 Mon Sep 17 00:00:00 2001 From: KoCoder Date: Fri, 20 Mar 2026 21:49:31 +0100 Subject: [PATCH] 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) + } +}