package reports import ( "fmt" "os" "path/filepath" "sort" "time" "gopkg.in/yaml.v3" "github.com/atridad/wrapped-cli/pkg/config" "github.com/atridad/wrapped-cli/pkg/models" ) type ReportMetadata struct { Timestamp time.Time `yaml:"timestamp"` Username string `yaml:"username"` TotalCommits int `yaml:"total_commits"` Repositories int `yaml:"repositories"` Stats *models.UserStats `yaml:"stats"` } type Report struct { Metadata *ReportMetadata MarkdownID string MetadataID string Timestamp time.Time } func SaveReport(stats *models.UserStats) (string, error) { resultsDir, err := config.GetResultsDir() if err != nil { return "", err } now := time.Now() timestamp := now.Format("2006-01-02 15-04-05") metadataID := fmt.Sprintf("%s.yaml", now.Format("2006-01-02-15-04-05")) imageID := fmt.Sprintf("%s - Gitea Wrapped.png", timestamp) metadata := &ReportMetadata{ Timestamp: now, Username: stats.Username, TotalCommits: stats.TotalCommits, Repositories: stats.TotalRepositories, Stats: stats, } metadataPath := filepath.Join(resultsDir, metadataID) data, err := yaml.Marshal(metadata) if err != nil { return "", err } if err := os.WriteFile(metadataPath, data, 0o644); err != nil { return "", err } if _, err := ExportReportAsImage(stats, "png"); err != nil { fmt.Fprintf(os.Stderr, "Warning: failed to generate image: %v\n", err) } return imageID, nil } func ListReports() ([]Report, error) { resultsDir, err := config.GetResultsDir() if err != nil { return nil, err } entries, err := os.ReadDir(resultsDir) if err != nil { if os.IsNotExist(err) { return []Report{}, nil } return nil, err } var reports []Report for _, entry := range entries { if entry.IsDir() { continue } if filepath.Ext(entry.Name()) == ".yaml" { metadataPath := filepath.Join(resultsDir, entry.Name()) data, err := os.ReadFile(metadataPath) if err != nil { continue } var metadata ReportMetadata if err := yaml.Unmarshal(data, &metadata); err != nil { continue } reports = append(reports, Report{ Metadata: &metadata, MetadataID: entry.Name(), Timestamp: metadata.Timestamp, }) } } sort.Slice(reports, func(i, j int) bool { return reports[i].Timestamp.After(reports[j].Timestamp) }) return reports, nil } func GetReport(metadataID string) (*ReportMetadata, error) { resultsDir, err := config.GetResultsDir() if err != nil { return nil, err } metadataPath := filepath.Join(resultsDir, metadataID) data, err := os.ReadFile(metadataPath) if err != nil { return nil, err } var metadata ReportMetadata if err := yaml.Unmarshal(data, &metadata); err != nil { return nil, err } return &metadata, nil } func generateMarkdown(stats *models.UserStats) string { md := fmt.Sprintf("# Gitea Wrapped — %s\n\n", time.Now().Format("January 2, 2006")) md += fmt.Sprintf("**User**: %s\n\n", stats.Username) md += "## Overview\n\n" md += fmt.Sprintf("- **Total Commits**: %d\n", stats.TotalCommits) md += fmt.Sprintf("- **Total Repositories**: %d\n", stats.TotalRepositories) md += fmt.Sprintf("- **Average Commits/Day**: %.2f\n", stats.AverageCommitsPerDay) md += fmt.Sprintf("- **Most Active Day**: %s\n", stats.MostActiveDay) md += fmt.Sprintf("- **Most Active Month**: %s\n\n", stats.MostActiveMonth) md += "## Languages\n\n" for i, lang := range stats.Languages { if i >= 10 { break } md += fmt.Sprintf("- %s: %d repos (%.1f%%)\n", lang.Language, lang.Count, lang.Percent) } md += "\n" md += "## Top Repositories\n\n" for i, repo := range stats.TopRepositories { md += fmt.Sprintf("%d. [%s](%s)\n", i+1, repo.Name, repo.HTMLURL) } md += "\n" md += "## Most Active Days\n\n" for i, day := range stats.CommitsByWeekday { if i >= 7 { break } md += fmt.Sprintf("- %s: %d commits\n", day.Day, day.Count) } md += "\n" md += "## Most Active Months\n\n" for i, month := range stats.CommitsByMonth { if i >= 12 { break } md += fmt.Sprintf("- %s: %d commits\n", month.Date.Format("January 2006"), month.Count) } return md }