┌─ nix/pkgs/ookstats/src/cmd/generate.go ────────────────────────────────────┐
│ diff --git a/nix/pkgs/ookstats/src/cmd/generate.go b/nix/pkgs/ookstats/src/cmd/gen │
│ erate.go │
│ index 89ed309..ffba9f3 100644 │
│ --- a/nix/pkgs/ookstats/src/cmd/generate.go │
│ +++ b/nix/pkgs/ookstats/src/cmd/generate.go │
│ @@ -56,21 +56,16 @@ var generateAPICmd = &cobra.Command{ │
│ } │
│ │
│ if doLeaderboards { │
│ - regions := []string{} │
│ - if strings.TrimSpace(regionsCSV) != "" { │
│ - for _, r := range strings.Split(regionsCSV, ",") { │
│ - rr := strings.TrimSpace(r) │
│ - if rr != "" { │
│ - regions = append(regions, rr) │
│ - } │
│ - } │
│ - } │
│ + regions := parseRegions(regionsCSV) │
│ if err := generator.GenerateLeaderboards(db, filepath.Join(base, "leaderboard" │
│ ), pageSize, regions, workers); err != nil { │
│ return err │
│ } │
│ if err := generator.GeneratePlayerLeaderboards(db, filepath.Join(base, "leader │
│ board"), pageSize, regions, workers); err != nil { │
│ return err │
│ } │
│ + if err := generator.GenerateTotalRunsLeaderboard(db, filepath.Join(base, "lead │
│ erboard"), pageSize, regions, workers); err != nil { │
│ + return err │
│ + } │
│ } │
│ │
│ if doSearch { │
│ @@ -142,6 +137,84 @@ var generateStatsCmd = &cobra.Command{ │
│ }, │
│ } │
│ │
│ +var generatePlayerLeaderboardsCmd = &cobra.Command{ │
│ + Use: "player-leaderboards", │
│ + Short: "Generate the per-season player leaderboard JSON (Overall Time, all scope │
│ s)", │
│ + RunE: func(cmd *cobra.Command, args []string) error { │
│ + outDir, _ := cmd.Flags().GetString("out") │
│ + pageSize, _ := cmd.Flags().GetInt("page-size") │
│ + regionsCSV, _ := cmd.Flags().GetString("regions") │
│ + workers, _ := cmd.Flags().GetInt("workers") │
│ + │
│ + if strings.TrimSpace(outDir) == "" { │
│ + return errors.New("--out is required") │
│ + } │
│ + db, err := database.Connect() │
│ + if err != nil { │
│ + return fmt.Errorf("failed to connect to db: %w", err) │
│ + } │
│ + defer db.Close() │
│ + │
│ + base := filepath.Join(outDir, "api", "leaderboard") │
│ + if err := os.MkdirAll(base, 0o755); err != nil { │
│ + return fmt.Errorf("mkdir base: %w", err) │
│ + } │
│ + │
│ + regions := parseRegions(regionsCSV) │
│ + if err := generator.GeneratePlayerLeaderboards(db, base, pageSize, regions, wor │
│ kers); err != nil { │
│ + return err │
│ + } │
│ + fmt.Printf("\nPer-season player leaderboards generated at %s/season/<id>/player │
│ s/...\n", base) │
│ + return nil │
│ + }, │
│ +} │
│ + │
│ +var generateTotalRunsLeaderboardCmd = &cobra.Command{ │
│ + Use: "total-runs-leaderboard", │
│ + Short: "Generate the cross-season Total Runs leaderboard JSON (all scopes)", │
│ + RunE: func(cmd *cobra.Command, args []string) error { │
│ + outDir, _ := cmd.Flags().GetString("out") │
│ + pageSize, _ := cmd.Flags().GetInt("page-size") │
│ + regionsCSV, _ := cmd.Flags().GetString("regions") │
│ + workers, _ := cmd.Flags().GetInt("workers") │
│ + │
│ + if strings.TrimSpace(outDir) == "" { │
│ + return errors.New("--out is required") │
│ + } │
│ + db, err := database.Connect() │
│ + if err != nil { │
│ + return fmt.Errorf("failed to connect to db: %w", err) │
│ + } │
│ + defer db.Close() │
│ + │
│ + base := filepath.Join(outDir, "api", "leaderboard") │
│ + if err := os.MkdirAll(base, 0o755); err != nil { │
│ + return fmt.Errorf("mkdir base: %w", err) │
│ + } │
│ + │
│ + regions := parseRegions(regionsCSV) │
│ + if err := generator.GenerateTotalRunsLeaderboard(db, base, pageSize, regions, w │
│ orkers); err != nil { │
│ + return err │
│ + } │
│ + fmt.Printf("\nTotal Runs leaderboard generated at %s/players/total-runs/...\n", │
│ base) │
│ + return nil │
│ + }, │
│ +} │
│ + │
│ +func parseRegions(csv string) []string { │
│ + out := []string{} │
│ + if strings.TrimSpace(csv) == "" { │
│ + return out │
│ + } │
│ + for _, r := range strings.Split(csv, ",") { │
│ + rr := strings.TrimSpace(r) │
│ + if rr != "" { │
│ + out = append(out, rr) │
│ + } │
│ + } │
│ + return out │
│ +} │
│ + │
│ var generateGearCmd = &cobra.Command{ │
│ Use: "gear", │
│ Short: "Generate gear popularity JSON (per-spec top items per slot, validated by │
│ spec primary stat)", │
│ @@ -189,4 +262,16 @@ func init() { │
│ │
│ generateCmd.AddCommand(generateGearCmd) │
│ generateGearCmd.Flags().String("out", "web/public", "Output directory (api/gear. │
│ json will be written under it)") │
│ + │
│ + generateCmd.AddCommand(generatePlayerLeaderboardsCmd) │
│ + generatePlayerLeaderboardsCmd.Flags().String("out", "web/public", "Output direct │
│ ory (api/leaderboard/... will be written under it)") │
│ + generatePlayerLeaderboardsCmd.Flags().Int("page-size", 25, "Leaderboard page siz │
│ e") │
│ + generatePlayerLeaderboardsCmd.Flags().String("regions", "us,eu,kr,tw", "Regions │
│ to include for regional/realm/class leaderboards") │
│ + generatePlayerLeaderboardsCmd.Flags().Int("workers", 10, "Number of parallel wor │
│ kers for leaderboard generation") │
│ + │
│ + generateCmd.AddCommand(generateTotalRunsLeaderboardCmd) │
│ + generateTotalRunsLeaderboardCmd.Flags().String("out", "web/public", "Output dire │
│ ctory (api/leaderboard/players/total-runs/... will be written under it)") │
│ + generateTotalRunsLeaderboardCmd.Flags().Int("page-size", 25, "Leaderboard page s │
│ ize") │
│ + generateTotalRunsLeaderboardCmd.Flags().String("regions", "us,eu,kr,tw", "Region │
│ s to include for regional/realm/class leaderboards") │
│ + generateTotalRunsLeaderboardCmd.Flags().Int("workers", 10, "Number of parallel w │
│ orkers for leaderboard generation") │
│ } │
└────────────────────────────────────────────────────────────────────────────────────┘
┌─ .../ookstats/src/cmd/generate.go ───┐
│ diff --git a/nix/pkgs/ookstats/src/cmd/gener │
│ ate.go b/nix/pkgs/ookstats/src/cmd/generate. │
│ go │
│ index 89ed309..ffba9f3 100644 │
│ --- a/nix/pkgs/ookstats/src/cmd/generate.go │
│ +++ b/nix/pkgs/ookstats/src/cmd/generate.go │
│ @@ -56,21 +56,16 @@ var generateAPICmd = &co │
│ bra.Command{ │
│ } │
│ │
│ if doLeaderboards { │
│ - regions := []string{} │
│ - if strings.TrimSpace(regionsCSV) != "" { │
│ - for _, r := range strings.Split(regions │
│ CSV, ",") { │
│ - rr := strings.TrimSpace(r) │
│ - if rr != "" { │
│ - regions = append(regions, rr) │
│ - } │
│ - } │
│ - } │
│ + regions := parseRegions(regionsCSV) │
│ if err := generator.GenerateLeaderboards │
│ (db, filepath.Join(base, "leaderboard"), pag │
│ eSize, regions, workers); err != nil { │
│ return err │
│ } │
│ if err := generator.GeneratePlayerLeader │
│ boards(db, filepath.Join(base, "leaderboard" │
│ ), pageSize, regions, workers); err != nil { │
│ return err │
│ } │
│ + if err := generator.GenerateTotalRunsLea │
│ derboard(db, filepath.Join(base, "leaderboar │
│ d"), pageSize, regions, workers); err != nil │
│ { │
│ + return err │
│ + } │
│ } │
│ │
│ if doSearch { │
│ @@ -142,6 +137,84 @@ var generateStatsCmd = │
│ &cobra.Command{ │
│ }, │
│ } │
│ │
│ +var generatePlayerLeaderboardsCmd = &cobra. │
│ Command{ │
│ + Use: "player-leaderboards", │
│ + Short: "Generate the per-season player lea │
│ derboard JSON (Overall Time, all scopes)", │
│ + RunE: func(cmd *cobra.Command, args []stri │
│ ng) error { │
│ + outDir, _ := cmd.Flags().GetString("out") │
│ + pageSize, _ := cmd.Flags().GetInt("page-s │
│ ize") │
│ + regionsCSV, _ := cmd.Flags().GetString("r │
│ egions") │
│ + workers, _ := cmd.Flags().GetInt("workers │
│ ") │
│ + │
│ + if strings.TrimSpace(outDir) == "" { │
│ + return errors.New("--out is required") │
│ + } │
│ + db, err := database.Connect() │
│ + if err != nil { │
│ + return fmt.Errorf("failed to connect to │
│ db: %w", err) │
│ + } │
│ + defer db.Close() │
│ + │
│ + base := filepath.Join(outDir, "api", "lea │
│ derboard") │
│ + if err := os.MkdirAll(base, 0o755); err ! │
│ = nil { │
│ + return fmt.Errorf("mkdir base: %w", err) │
│ + } │
│ + │
│ + regions := parseRegions(regionsCSV) │
│ + if err := generator.GeneratePlayerLeaderb │
│ oards(db, base, pageSize, regions, workers); │
│ err != nil { │
│ + return err │
│ + } │
│ + fmt.Printf("\nPer-season player leaderboa │
│ rds generated at %s/season/<id>/players/...\ │
│ n", base) │
│ + return nil │
│ + }, │
│ +} │
│ + │
│ +var generateTotalRunsLeaderboardCmd = &cobr │
│ a.Command{ │
│ + Use: "total-runs-leaderboard", │
│ + Short: "Generate the cross-season Total Ru │
│ ns leaderboard JSON (all scopes)", │
│ + RunE: func(cmd *cobra.Command, args []stri │
│ ng) error { │
│ + outDir, _ := cmd.Flags().GetString("out") │
│ + pageSize, _ := cmd.Flags().GetInt("page-s │
│ ize") │
│ + regionsCSV, _ := cmd.Flags().GetString("r │
│ egions") │
│ + workers, _ := cmd.Flags().GetInt("workers │
│ ") │
│ + │
│ + if strings.TrimSpace(outDir) == "" { │
│ + return errors.New("--out is required") │
│ + } │
│ + db, err := database.Connect() │
│ + if err != nil { │
│ + return fmt.Errorf("failed to connect to │
│ db: %w", err) │
│ + } │
│ + defer db.Close() │
│ + │
│ + base := filepath.Join(outDir, "api", "lea │
│ derboard") │
│ + if err := os.MkdirAll(base, 0o755); err ! │
│ = nil { │
│ + return fmt.Errorf("mkdir base: %w", err) │
│ + } │
│ + │
│ + regions := parseRegions(regionsCSV) │
│ + if err := generator.GenerateTotalRunsLead │
│ erboard(db, base, pageSize, regions, workers │
│ ); err != nil { │
│ + return err │
│ + } │
│ + fmt.Printf("\nTotal Runs leaderboard gene │
│ rated at %s/players/total-runs/...\n", base) │
│ + return nil │
│ + }, │
│ +} │
│ + │
│ +func parseRegions(csv string) []string { │
│ + out := []string{} │
│ + if strings.TrimSpace(csv) == "" { │
│ + return out │
│ + } │
│ + for _, r := range strings.Split(csv, ",") │
│ { │
│ + rr := strings.TrimSpace(r) │
│ + if rr != "" { │
│ + out = append(out, rr) │
│ + } │
│ + } │
│ + return out │
│ +} │
│ + │
│ var generateGearCmd = &cobra.Command{ │
│ Use: "gear", │
│ Short: "Generate gear popularity JSON (per │
│ -spec top items per slot, validated by spec │
│ primary stat)", │
│ @@ -189,4 +262,16 @@ func init() { │
│ │
│ generateCmd.AddCommand(generateGearCmd) │
│ generateGearCmd.Flags().String("out", "web │
│ /public", "Output directory (api/gear.json w │
│ ill be written under it)") │
│ + │
│ + generateCmd.AddCommand(generatePlayerLeade │
│ rboardsCmd) │
│ + generatePlayerLeaderboardsCmd.Flags().Stri │
│ ng("out", "web/public", "Output directory (a │
│ pi/leaderboard/... will be written under it) │
│ ") │
│ + generatePlayerLeaderboardsCmd.Flags().Int( │
│ "page-size", 25, "Leaderboard page size") │
│ + generatePlayerLeaderboardsCmd.Flags().Stri │
│ ng("regions", "us,eu,kr,tw", "Regions to inc │
│ lude for regional/realm/class leaderboards") │
│ + generatePlayerLeaderboardsCmd.Flags().Int( │
│ "workers", 10, "Number of parallel workers f │
│ or leaderboard generation") │
│ + │
│ + generateCmd.AddCommand(generateTotalRunsLe │
│ aderboardCmd) │
│ + generateTotalRunsLeaderboardCmd.Flags().St │
│ ring("out", "web/public", "Output directory │
│ (api/leaderboard/players/total-runs/... will │
│ be written under it)") │
│ + generateTotalRunsLeaderboardCmd.Flags().In │
│ t("page-size", 25, "Leaderboard page size") │
│ + generateTotalRunsLeaderboardCmd.Flags().St │
│ ring("regions", "us,eu,kr,tw", "Regions to i │
│ nclude for regional/realm/class leaderboards │
│ ") │
│ + generateTotalRunsLeaderboardCmd.Flags().In │
│ t("workers", 10, "Number of parallel workers │
│ for leaderboard generation") │
│ } │
└──────────────────────────────────────────────┘
┌─ nix/pkgs/ookstats/src/internal/generator/total_runs_leaderboard.go ───────┐
│ diff --git a/nix/pkgs/ookstats/src/internal/generator/total_runs_leaderboard.go b/ │
│ nix/pkgs/ookstats/src/internal/generator/total_runs_leaderboard.go │
│ new file mode 100644 │
│ index 0000000..b5677dc │
│ --- /dev/null │
│ +++ b/nix/pkgs/ookstats/src/internal/generator/total_runs_leaderboard.go │
│ @@ -0,0 +1,463 @@ │
│ +package generator │
│ + │
│ +import ( │
│ + "database/sql" │
│ + "fmt" │
│ + "ookstats/internal/wow" │
│ + "ookstats/internal/writer" │
│ + "os" │
│ + "path/filepath" │
│ + "strings" │
│ + "sync" │
│ + "sync/atomic" │
│ +) │
│ + │
│ +// cross-season total runs: SUM(pp.total_runs) per character, gated by │
│ +// has_complete_coverage = 1 in any season (kept loose so a top grinder │
│ +// isn't dropped for skipping one dungeon in the latest season). │
│ + │
│ +type totalRunsJob struct { │
│ + scope string │
│ + region string │
│ + realmSlug string │
│ + classKey string │
│ + out string │
│ + pageSize int │
│ + regions []string │
│ +} │
│ + │
│ +// writes to <out>/players/total-runs/... │
│ +func GenerateTotalRunsLeaderboard(db *sql.DB, out string, pageSize int, regions [ │
│ ]string, workers int) error { │
│ + if pageSize <= 0 { │
│ + pageSize = 25 │
│ + } │
│ + if workers <= 0 { │
│ + workers = 10 │
│ + } │
│ + if len(regions) == 0 { │
│ + regions = []string{"us", "eu", "kr", "tw"} │
│ + } │
│ + │
│ + root := filepath.Join(out, "players", "total-runs") │
│ + // wipe so realms dropping out of the data don't leave stale JSON │
│ + if err := os.RemoveAll(root); err != nil { │
│ + return fmt.Errorf("clean total-runs tree: %w", err) │
│ + } │
│ + │
│ + realmSlugs := make(map[string][]string) │
│ + for _, reg := range regions { │
│ + rrows, err := db.Query(` │
│ + SELECT slug │
│ + FROM realms │
│ + WHERE region = ? AND (parent_realm_slug IS NULL OR parent_realm_slug = '') │
│ + ORDER BY slug │
│ + `, reg) │
│ + if err != nil { │
│ + return fmt.Errorf("load realm slugs: %w", err) │
│ + } │
│ + var slugs []string │
│ + for rrows.Next() { │
│ + var s string │
│ + if err := rrows.Scan(&s); err != nil { │
│ + rrows.Close() │
│ + return err │
│ + } │
│ + slugs = append(slugs, s) │
│ + } │
│ + rrows.Close() │
│ + realmSlugs[reg] = slugs │
│ + } │
│ + │
│ + classKeys := []string{"death_knight", "druid", "hunter", "mage", "monk", "paladi │
│ n", "priest", "rogue", "shaman", "warlock", "warrior"} │
│ + │
│ + totalJobs := 1 │
│ + totalJobs += len(regions) │
│ + for _, reg := range regions { │
│ + totalJobs += len(realmSlugs[reg]) │
│ + } │
│ + totalJobs += len(classKeys) │
│ + │
│ + fmt.Printf("Generating Total Runs leaderboard with %d workers (%d total jobs)... │
│ \n", workers, totalJobs) │
│ + │
│ + jobs := make(chan totalRunsJob, 100) │
│ + var firstErr atomic.Value │
│ + var completed atomic.Int64 │
│ + var wg sync.WaitGroup │
│ + │
│ + for i := 0; i < workers; i++ { │
│ + wg.Add(1) │
│ + go func() { │
│ + defer wg.Done() │
│ + for job := range jobs { │
│ + if firstErr.Load() != nil { │
│ + continue │
│ + } │
│ + var err error │
│ + switch job.scope { │
│ + case "global": │
│ + err = generateTotalRunsGlobal(db, job.out, job.pageSize) │
│ + case "regional": │
│ + err = generateTotalRunsRegional(db, job.out, job.region, job.pageSize) │
│ + case "realm": │
│ + err = generateTotalRunsRealm(db, job.out, job.region, job.realmSlug, job.pag │
│ eSize) │
│ + case "class": │
│ + err = generateTotalRunsClass(db, job.out, job.classKey, job.pageSize, job.re │
│ gions) │
│ + } │
│ + if err != nil { │
│ + firstErr.CompareAndSwap(nil, err) │
│ + continue │
│ + } │
│ + c := completed.Add(1) │
│ + if c%20 == 0 { │
│ + fmt.Printf(" ... %d/%d total-runs leaderboards generated\n", c, totalJobs) │
│ + } │
│ + } │
│ + }() │
│ + } │
│ + │
│ + jobs <- totalRunsJob{scope: "global", out: root, pageSize: pageSize} │
│ + for _, reg := range regions { │
│ + jobs <- totalRunsJob{scope: "regional", region: reg, out: root, pageSize: pageS │
│ ize} │
│ + } │
│ + for _, reg := range regions { │
│ + for _, rslug := range realmSlugs[reg] { │
│ + jobs <- totalRunsJob{scope: "realm", region: reg, realmSlug: rslug, out: root, │
│ pageSize: pageSize} │
│ + } │
│ + } │
│ + for _, cls := range classKeys { │
│ + jobs <- totalRunsJob{scope: "class", classKey: cls, out: root, pageSize: pageSi │
│ ze, regions: regions} │
│ + } │
│ + close(jobs) │
│ + wg.Wait() │
│ + │
│ + if err := firstErr.Load(); err != nil { │
│ + return err.(error) │
│ + } │
│ + fmt.Printf("\n[OK] Generated %d total-runs leaderboards\n", completed.Load()) │
│ + return nil │
│ +} │
│ + │
│ +func totalRunsBaseSelect() string { │
│ + return ` │
│ + SELECT p.id, │
│ + p.name, │
│ + r.slug AS realm_slug, │
│ + r.name AS realm_name, │
│ + r.region, │
│ + COALESCE(pd.class_name, '') AS class_name, │
│ + COALESCE(pd.active_spec_name, '') AS active_spec_name, │
│ + MAX(pp.main_spec_id) AS main_spec_id, │
│ + SUM(pp.total_runs) AS all_time_runs │
│ + FROM players p │
│ + JOIN realms r ON p.realm_id = r.id │
│ + JOIN player_profiles pp ON p.id = pp.player_id │
│ + LEFT JOIN player_details pd ON p.id = pd.player_id │
│ + ` │
│ +} │
│ + │
│ +const totalRunsHaving = ` │
│ + GROUP BY p.id, p.name, r.slug, r.name, r.region, pd.class_name, pd.active_spec_n │
│ ame │
│ + HAVING MAX(pp.has_complete_coverage) = 1 AND SUM(pp.total_runs) > 0 │
│ +` │
│ + │
│ +func generateTotalRunsGlobal(db *sql.DB, root string, pageSize int) error { │
│ + dir := filepath.Join(root, "global") │
│ + if err := os.MkdirAll(dir, 0o755); err != nil { │
│ + return err │
│ + } │
│ + │
│ + var total int │
│ + if err := db.QueryRow(` │
│ + SELECT COUNT(*) FROM ( │
│ + SELECT 1 │
│ + FROM players p │
│ + JOIN player_profiles pp ON p.id = pp.player_id │
│ + GROUP BY p.id │
│ + HAVING MAX(pp.has_complete_coverage) = 1 AND SUM(pp.total_runs) > 0 │
│ + ) │
│ + `).Scan(&total); err != nil { │
│ + return fmt.Errorf("total-runs count (global): %w", err) │
│ + } │
│ + │
│ + pages := (total + pageSize - 1) / pageSize │
│ + for p := 1; p <= pages; p++ { │
│ + offset := (p - 1) * pageSize │
│ + rows, err := db.Query(totalRunsBaseSelect()+totalRunsHaving+` │
│ + ORDER BY all_time_runs DESC, p.name ASC │
│ + LIMIT ? OFFSET ? │
│ + `, pageSize, offset) │
│ + if err != nil { │
│ + return err │
│ + } │
│ + list, err := scanTotalRunsRows(rows) │
│ + if err != nil { │
│ + return err │
│ + } │
│ + page := buildTotalRunsPage(list, "Global Total Runs", total, pages, p, pageSize │
│ ) │
│ + if err := writer.WriteJSONFileCompact(filepath.Join(dir, fmt.Sprintf("%d.json", │
│ p)), page); err != nil { │
│ + return err │
│ + } │
│ + } │
│ + return nil │
│ +} │
│ + │
│ +func generateTotalRunsRegional(db *sql.DB, root, region string, pageSize int) err │
│ or { │
│ + dir := filepath.Join(root, "regional", region) │
│ + if err := os.MkdirAll(dir, 0o755); err != nil { │
│ + return err │
│ + } │
│ + │
│ + var total int │
│ + if err := db.QueryRow(` │
│ + SELECT COUNT(*) FROM ( │
│ + SELECT 1 │
│ + FROM players p │
│ + JOIN realms r ON p.realm_id = r.id │
│ + JOIN player_profiles pp ON p.id = pp.player_id │
│ + WHERE r.region = ? │
│ + GROUP BY p.id │
│ + HAVING MAX(pp.has_complete_coverage) = 1 AND SUM(pp.total_runs) > 0 │
│ + ) │
│ + `, region).Scan(&total); err != nil { │
│ + return fmt.Errorf("total-runs count (regional %s): %w", region, err) │
│ + } │
│ + │
│ + pages := (total + pageSize - 1) / pageSize │
│ + for p := 1; p <= pages; p++ { │
│ + offset := (p - 1) * pageSize │
│ + rows, err := db.Query(totalRunsBaseSelect()+` │
│ + WHERE r.region = ? │
│ + `+totalRunsHaving+` │
│ + ORDER BY all_time_runs DESC, p.name ASC │
│ + LIMIT ? OFFSET ? │
│ + `, region, pageSize, offset) │
│ + if err != nil { │
│ + return err │
│ + } │
│ + list, err := scanTotalRunsRows(rows) │
│ + if err != nil { │
│ + return err │
│ + } │
│ + title := strings.ToUpper(region) + " Total Runs" │
│ + page := buildTotalRunsPage(list, title, total, pages, p, pageSize) │
│ + if err := writer.WriteJSONFileCompact(filepath.Join(dir, fmt.Sprintf("%d.json", │
│ p)), page); err != nil { │
│ + return err │
│ + } │
│ + } │
│ + return nil │
│ +} │
│ + │
│ +func generateTotalRunsRealm(db *sql.DB, root, region, rslug string, pageSize int) │
│ error { │
│ + dir := filepath.Join(root, "realm", region, rslug) │
│ + if err := os.MkdirAll(dir, 0o755); err != nil { │
│ + return err │
│ + } │
│ + │
│ + // pool = parent + child realms (matches per-season realm leaderboard) │
│ + var total int │
│ + if err := db.QueryRow(` │
│ + SELECT COUNT(*) FROM ( │
│ + SELECT 1 │
│ + FROM players p │
│ + JOIN realms r ON p.realm_id = r.id │
│ + JOIN player_profiles pp ON p.id = pp.player_id │
│ + WHERE r.region = ? AND (r.slug = ? OR r.parent_realm_slug = ?) │
│ + GROUP BY p.id │
│ + HAVING MAX(pp.has_complete_coverage) = 1 AND SUM(pp.total_runs) > 0 │
│ + ) │
│ + `, region, rslug, rslug).Scan(&total); err != nil { │
│ + return fmt.Errorf("total-runs count (realm %s/%s): %w", region, rslug, err) │
│ + } │
│ + │
│ + pages := (total + pageSize - 1) / pageSize │
│ + for p := 1; p <= pages; p++ { │
│ + offset := (p - 1) * pageSize │
│ + rows, err := db.Query(totalRunsBaseSelect()+` │
│ + WHERE r.region = ? AND (r.slug = ? OR r.parent_realm_slug = ?) │
│ + `+totalRunsHaving+` │
│ + ORDER BY all_time_runs DESC, p.name ASC │
│ + LIMIT ? OFFSET ? │
│ + `, region, rslug, rslug, pageSize, offset) │
│ + if err != nil { │
│ + return err │
│ + } │
│ + list, err := scanTotalRunsRows(rows) │
│ + if err != nil { │
│ + return err │
│ + } │
│ + title := strings.ToUpper(region) + "/" + rslug + " Total Runs" │
│ + page := buildTotalRunsPage(list, title, total, pages, p, pageSize) │
│ + if err := writer.WriteJSONFileCompact(filepath.Join(dir, fmt.Sprintf("%d.json", │
│ p)), page); err != nil { │
│ + return err │
│ + } │
│ + } │
│ + return nil │
│ +} │
│ + │
│ +// filters in Go so we can reuse the wow.GetClassAndSpec fallback when │
│ +// pd.class_name is missing │
│ +func generateTotalRunsClass(db *sql.DB, root, classKey string, pageSize int, regi │
│ ons []string) error { │
│ + if err := generateTotalRunsClassScope(db, root, "global", "", "", classKey, page │
│ Size); err != nil { │
│ + return err │
│ + } │
│ + for _, reg := range regions { │
│ + if err := generateTotalRunsClassScope(db, root, "regional", reg, "", classKey, │
│ pageSize); err != nil { │
│ + return err │
│ + } │
│ + rrows, err := db.Query(` │
│ + SELECT slug │
│ + FROM realms │
│ + WHERE region = ? AND (parent_realm_slug IS NULL OR parent_realm_slug = '') │
│ + ORDER BY slug │
│ + `, reg) │
│ + if err != nil { │
│ + return fmt.Errorf("total-runs class realms list: %w", err) │
│ + } │
│ + var slugs []string │
│ + for rrows.Next() { │
│ + var s string │
│ + if err := rrows.Scan(&s); err != nil { │
│ + rrows.Close() │
│ + return err │
│ + } │
│ + slugs = append(slugs, s) │
│ + } │
│ + rrows.Close() │
│ + for _, rslug := range slugs { │
│ + if err := generateTotalRunsClassScope(db, root, "realm", reg, rslug, classKey, │
│ pageSize); err != nil { │
│ + return err │
│ + } │
│ + } │
│ + } │
│ + return nil │
│ +} │
│ + │
│ +func generateTotalRunsClassScope(db *sql.DB, root, scope, region, rslug, classKey │
│ string, pageSize int) error { │
│ + var dir string │
│ + switch scope { │
│ + case "global": │
│ + dir = filepath.Join(root, "class", classKey, "global") │
│ + case "regional": │
│ + dir = filepath.Join(root, "class", classKey, "regional", region) │
│ + default: │
│ + dir = filepath.Join(root, "class", classKey, "realm", region, rslug) │
│ + } │
│ + if err := os.MkdirAll(dir, 0o755); err != nil { │
│ + return err │
│ + } │
│ + │
│ + var rows *sql.Rows │
│ + var err error │
│ + switch scope { │
│ + case "global": │
│ + rows, err = db.Query(totalRunsBaseSelect() + totalRunsHaving + ` │
│ + ORDER BY all_time_runs DESC, p.name ASC │
│ + `) │
│ + case "regional": │
│ + rows, err = db.Query(totalRunsBaseSelect()+` │
│ + WHERE r.region = ? │
│ + `+totalRunsHaving+` │
│ + ORDER BY all_time_runs DESC, p.name ASC │
│ + `, region) │
│ + default: │
│ + rows, err = db.Query(totalRunsBaseSelect()+` │
│ + WHERE r.region = ? AND (r.slug = ? OR r.parent_realm_slug = ?) │
│ + `+totalRunsHaving+` │
│ + ORDER BY all_time_runs DESC, p.name ASC │
│ + `, region, rslug, rslug) │
│ + } │
│ + if err != nil { │
│ + return err │
│ + } │
│ + all, err := scanTotalRunsRows(rows) │
│ + if err != nil { │
│ + return err │
│ + } │
│ + │
│ + matching := make([]map[string]any, 0, len(all)/4) │
│ + for _, r := range all { │
│ + cls, _ := r["class_name"].(string) │
│ + key := strings.ReplaceAll(strings.ToLower(cls), " ", "_") │
│ + if key == classKey { │
│ + matching = append(matching, r) │
│ + } │
│ + } │
│ + │
│ + total := len(matching) │
│ + pages := (total + pageSize - 1) / pageSize │
│ + for p := 1; p <= pages; p++ { │
│ + offset := (p - 1) * pageSize │
│ + end := offset + pageSize │
│ + if end > total { │
│ + end = total │
│ + } │
│ + title := "Global Total Runs" │
│ + switch scope { │
│ + case "regional": │
│ + title = strings.ToUpper(region) + " Total Runs" │
│ + case "realm": │
│ + title = strings.ToUpper(region) + "/" + rslug + " Total Runs" │
│ + } │
│ + page := buildTotalRunsPage(matching[offset:end], title, total, pages, p, pageSi │
│ ze) │
│ + if err := writer.WriteJSONFileCompact(filepath.Join(dir, fmt.Sprintf("%d.json", │
│ p)), page); err != nil { │
│ + return err │
│ + } │
│ + } │
│ + return nil │
│ +} │
│ + │
│ +// field names mirror scanPlayerRows so the frontend table component can │
│ +// render either tree without branching │
│ +func scanTotalRunsRows(rows *sql.Rows) ([]map[string]any, error) { │
│ + defer rows.Close() │
│ + var out []map[string]any │
│ + for rows.Next() { │
│ + var ( │
│ + id int64 │
│ + name, realmSlug, realmName string │
│ + region, className, activeSpecName string │
│ + mainSpecID sql.NullInt64 │
│ + allTimeRuns sql.NullInt64 │
│ + ) │
│ + if err := rows.Scan(&id, &name, &realmSlug, &realmName, ®ion, &className, &a │
│ ctiveSpecName, &mainSpecID, &allTimeRuns); err != nil { │
│ + return nil, err │
│ + } │
│ + obj := map[string]any{ │
│ + "player_id": id, │
│ + "name": name, │
│ + "realm_slug": realmSlug, │
│ + "realm_name": realmName, │
│ + "region": region, │
│ + "class_name": className, │
│ + "total_runs": allTimeRuns.Int64, │
│ + } │
│ + if mainSpecID.Valid { │
│ + v := int(mainSpecID.Int64) │
│ + cls, spec := wow.FallbackClassAndSpec(className, activeSpecName, &v) │
│ + obj["class_name"] = cls │
│ + obj["active_spec_name"] = spec │
│ + obj["main_spec_id"] = v │
│ + } else if activeSpecName != "" { │
│ + obj["active_spec_name"] = activeSpecName │
│ + } │
│ + out = append(out, obj) │
│ + } │
│ + return out, nil │
│ +} │
│ + │
│ +// shape mirrors buildPlayerLeaderboardPage so Pagination and the table │
│ +// component can render either tree without branching │
│ +func buildTotalRunsPage(list []map[string]any, title string, total, totalPages, c │
│ urrentPage, pageSize int) map[string]any { │
│ + return map[string]any{ │
│ + "leaderboard": list, │
│ + "title": title, │
│ + "pagination": map[string]any{ │
│ + "currentPage": currentPage, │
│ + "pageSize": pageSize, │
│ + "totalPlayers": total, │
│ + "totalPages": totalPages, │
│ + "hasNextPage": currentPage < totalPages, │
│ + "hasPrevPage": currentPage > 1, │
│ + }, │
│ + } │
│ +} │
└────────────────────────────────────────────────────────────────────────────────────┘
┌─ ...tor/total_runs_leaderboard.go ───┐
│ diff --git a/nix/pkgs/ookstats/src/internal/ │
│ generator/total_runs_leaderboard.go b/nix/pk │
│ gs/ookstats/src/internal/generator/total_run │
│ s_leaderboard.go │
│ new file mode 100644 │
│ index 0000000..b5677dc │
│ --- /dev/null │
│ +++ b/nix/pkgs/ookstats/src/internal/generat │
│ or/total_runs_leaderboard.go │
│ @@ -0,0 +1,463 @@ │
│ +package generator │
│ + │
│ +import ( │
│ + "database/sql" │
│ + "fmt" │
│ + "ookstats/internal/wow" │
│ + "ookstats/internal/writer" │
│ + "os" │
│ + "path/filepath" │
│ + "strings" │
│ + "sync" │
│ + "sync/atomic" │
│ +) │
│ + │
│ +// cross-season total runs: SUM(pp.total_ru │
│ ns) per character, gated by │
│ +// has_complete_coverage = 1 in any season │
│ (kept loose so a top grinder │
│ +// isn't dropped for skipping one dungeon i │
│ n the latest season). │
│ + │
│ +type totalRunsJob struct { │
│ + scope string │
│ + region string │
│ + realmSlug string │
│ + classKey string │
│ + out string │
│ + pageSize int │
│ + regions []string │
│ +} │
│ + │
│ +// writes to <out>/players/total-runs/... │
│ +func GenerateTotalRunsLeaderboard(db *sql.D │
│ B, out string, pageSize int, regions []strin │
│ g, workers int) error { │
│ + if pageSize <= 0 { │
│ + pageSize = 25 │
│ + } │
│ + if workers <= 0 { │
│ + workers = 10 │
│ + } │
│ + if len(regions) == 0 { │
│ + regions = []string{"us", "eu", "kr", "tw" │
│ } │
│ + } │
│ + │
│ + root := filepath.Join(out, "players", "tot │
│ al-runs") │
│ + // wipe so realms dropping out of the data │
│ don't leave stale JSON │
│ + if err := os.RemoveAll(root); err != nil { │
│ + return fmt.Errorf("clean total-runs tree: │
│ %w", err) │
│ + } │
│ + │
│ + realmSlugs := make(map[string][]string) │
│ + for _, reg := range regions { │
│ + rrows, err := db.Query(` │
│ + SELECT slug │
│ + FROM realms │
│ + WHERE region = ? AND (parent_realm_slug │
│ IS NULL OR parent_realm_slug = '') │
│ + ORDER BY slug │
│ + `, reg) │
│ + if err != nil { │
│ + return fmt.Errorf("load realm slugs: %w" │
│ , err) │
│ + } │
│ + var slugs []string │
│ + for rrows.Next() { │
│ + var s string │
│ + if err := rrows.Scan(&s); err != nil { │
│ + rrows.Close() │
│ + return err │
│ + } │
│ + slugs = append(slugs, s) │
│ + } │
│ + rrows.Close() │
│ + realmSlugs[reg] = slugs │
│ + } │
│ + │
│ + classKeys := []string{"death_knight", "dru │
│ id", "hunter", "mage", "monk", "paladin", "p │
│ riest", "rogue", "shaman", "warlock", "warri │
│ or"} │
│ + │
│ + totalJobs := 1 │
│ + totalJobs += len(regions) │
│ + for _, reg := range regions { │
│ + totalJobs += len(realmSlugs[reg]) │
│ + } │
│ + totalJobs += len(classKeys) │
│ + │
│ + fmt.Printf("Generating Total Runs leaderbo │
│ ard with %d workers (%d total jobs)...\n", w │
│ orkers, totalJobs) │
│ + │
│ + jobs := make(chan totalRunsJob, 100) │
│ + var firstErr atomic.Value │
│ + var completed atomic.Int64 │
│ + var wg sync.WaitGroup │
│ + │
│ + for i := 0; i < workers; i++ { │
│ + wg.Add(1) │
│ + go func() { │
│ + defer wg.Done() │
│ + for job := range jobs { │
│ + if firstErr.Load() != nil { │
│ + continue │
│ + } │
│ + var err error │
│ + switch job.scope { │
│ + case "global": │
│ + err = generateTotalRunsGlobal(db, job. │
│ out, job.pageSize) │
│ + case "regional": │
│ + err = generateTotalRunsRegional(db, jo │
│ b.out, job.region, job.pageSize) │
│ + case "realm": │
│ + err = generateTotalRunsRealm(db, job.o │
│ ut, job.region, job.realmSlug, job.pageSize) │
│ + case "class": │
│ + err = generateTotalRunsClass(db, job.o │
│ ut, job.classKey, job.pageSize, job.regions) │
│ + } │
│ + if err != nil { │
│ + firstErr.CompareAndSwap(nil, err) │
│ + continue │
│ + } │
│ + c := completed.Add(1) │
│ + if c%20 == 0 { │
│ + fmt.Printf(" ... %d/%d total-runs lea │
│ derboards generated\n", c, totalJobs) │
│ + } │
│ + } │
│ + }() │
│ + } │
│ + │
│ + jobs <- totalRunsJob{scope: "global", out: │
│ root, pageSize: pageSize} │
│ + for _, reg := range regions { │
│ + jobs <- totalRunsJob{scope: "regional", r │
│ egion: reg, out: root, pageSize: pageSize} │
│ + } │
│ + for _, reg := range regions { │
│ + for _, rslug := range realmSlugs[reg] { │
│ + jobs <- totalRunsJob{scope: "realm", reg │
│ ion: reg, realmSlug: rslug, out: root, pageS │
│ ize: pageSize} │
│ + } │
│ + } │
│ + for _, cls := range classKeys { │
│ + jobs <- totalRunsJob{scope: "class", clas │
│ sKey: cls, out: root, pageSize: pageSize, re │
│ gions: regions} │
│ + } │
│ + close(jobs) │
│ + wg.Wait() │
│ + │
│ + if err := firstErr.Load(); err != nil { │
│ + return err.(error) │
│ + } │
│ + fmt.Printf("\n[OK] Generated %d total-runs │
│ leaderboards\n", completed.Load()) │
│ + return nil │
│ +} │
│ + │
│ +func totalRunsBaseSelect() string { │
│ + return ` │
│ + SELECT p.id, │
│ + p.name, │
│ + r.slug AS realm_slug, │
│ + r.name AS realm_name, │
│ + r.region, │
│ + COALESCE(pd.class_name, '') │
│ AS class_name, │
│ + COALESCE(pd.active_spec_name, '') │
│ AS active_spec_name, │
│ + MAX(pp.main_spec_id) │
│ AS main_spec_id, │
│ + SUM(pp.total_runs) │
│ AS all_time_runs │
│ + FROM players p │
│ + JOIN realms r ON p.realm_id = r.id │
│ + JOIN player_profiles pp ON p.id = pp.play │
│ er_id │
│ + LEFT JOIN player_details pd ON p.id = pd. │
│ player_id │
│ + ` │
│ +} │
│ + │
│ +const totalRunsHaving = ` │
│ + GROUP BY p.id, p.name, r.slug, r.name, r.r │
│ egion, pd.class_name, pd.active_spec_name │
│ + HAVING MAX(pp.has_complete_coverage) = 1 A │
│ ND SUM(pp.total_runs) > 0 │
│ +` │
│ + │
│ +func generateTotalRunsGlobal(db *sql.DB, ro │
│ ot string, pageSize int) error { │
│ + dir := filepath.Join(root, "global") │
│ + if err := os.MkdirAll(dir, 0o755); err != │
│ nil { │
│ + return err │
│ + } │
│ + │
│ + var total int │
│ + if err := db.QueryRow(` │
│ + SELECT COUNT(*) FROM ( │
│ + SELECT 1 │
│ + FROM players p │
│ + JOIN player_profiles pp ON p.id = pp.pla │
│ yer_id │
│ + GROUP BY p.id │
│ + HAVING MAX(pp.has_complete_coverage) = 1 │
│ AND SUM(pp.total_runs) > 0 │
│ + ) │
│ + `).Scan(&total); err != nil { │
│ + return fmt.Errorf("total-runs count (glob │
│ al): %w", err) │
│ + } │
│ + │
│ + pages := (total + pageSize - 1) / pageSize │
│ + for p := 1; p <= pages; p++ { │
│ + offset := (p - 1) * pageSize │
│ + rows, err := db.Query(totalRunsBaseSelect │
│ ()+totalRunsHaving+` │
│ + ORDER BY all_time_runs DESC, p.name ASC │
│ + LIMIT ? OFFSET ? │
│ + `, pageSize, offset) │
│ + if err != nil { │
│ + return err │
│ + } │
│ + list, err := scanTotalRunsRows(rows) │
│ + if err != nil { │
│ + return err │
│ + } │
│ + page := buildTotalRunsPage(list, "Global │
│ Total Runs", total, pages, p, pageSize) │
│ + if err := writer.WriteJSONFileCompact(fil │
│ epath.Join(dir, fmt.Sprintf("%d.json", p)), │
│ page); err != nil { │
│ + return err │
│ + } │
│ + } │
│ + return nil │
│ +} │
│ + │
│ +func generateTotalRunsRegional(db *sql.DB, │
│ root, region string, pageSize int) error { │
│ + dir := filepath.Join(root, "regional", reg │
│ ion) │
│ + if err := os.MkdirAll(dir, 0o755); err != │
│ nil { │
│ + return err │
│ + } │
│ + │
│ + var total int │
│ + if err := db.QueryRow(` │
│ + SELECT COUNT(*) FROM ( │
│ + SELECT 1 │
│ + FROM players p │
│ + JOIN realms r ON p.realm_id = r.id │
│ + JOIN player_profiles pp ON p.id = pp.pla │
│ yer_id │
│ + WHERE r.region = ? │
│ + GROUP BY p.id │
│ + HAVING MAX(pp.has_complete_coverage) = 1 │
│ AND SUM(pp.total_runs) > 0 │
│ + ) │
│ + `, region).Scan(&total); err != nil { │
│ + return fmt.Errorf("total-runs count (regi │
│ onal %s): %w", region, err) │
│ + } │
│ + │
│ + pages := (total + pageSize - 1) / pageSize │
│ + for p := 1; p <= pages; p++ { │
│ + offset := (p - 1) * pageSize │
│ + rows, err := db.Query(totalRunsBaseSelect │
│ ()+` │
│ + WHERE r.region = ? │
│ + `+totalRunsHaving+` │
│ + ORDER BY all_time_runs DESC, p.name ASC │
│ + LIMIT ? OFFSET ? │
│ + `, region, pageSize, offset) │
│ + if err != nil { │
│ + return err │
│ + } │
│ + list, err := scanTotalRunsRows(rows) │
│ + if err != nil { │
│ + return err │
│ + } │
│ + title := strings.ToUpper(region) + " Tota │
│ l Runs" │
│ + page := buildTotalRunsPage(list, title, t │
│ otal, pages, p, pageSize) │
│ + if err := writer.WriteJSONFileCompact(fil │
│ epath.Join(dir, fmt.Sprintf("%d.json", p)), │
│ page); err != nil { │
│ + return err │
│ + } │
│ + } │
│ + return nil │
│ +} │
│ + │
│ +func generateTotalRunsRealm(db *sql.DB, roo │
│ t, region, rslug string, pageSize int) error │
│ { │
│ + dir := filepath.Join(root, "realm", region │
│ , rslug) │
│ + if err := os.MkdirAll(dir, 0o755); err != │
│ nil { │
│ + return err │
│ + } │
│ + │
│ + // pool = parent + child realms (matches p │
│ er-season realm leaderboard) │
│ + var total int │
│ + if err := db.QueryRow(` │
│ + SELECT COUNT(*) FROM ( │
│ + SELECT 1 │
│ + FROM players p │
│ + JOIN realms r ON p.realm_id = r.id │
│ + JOIN player_profiles pp ON p.id = pp.pla │
│ yer_id │
│ + WHERE r.region = ? AND (r.slug = ? OR r. │
│ parent_realm_slug = ?) │
│ + GROUP BY p.id │
│ + HAVING MAX(pp.has_complete_coverage) = 1 │
│ AND SUM(pp.total_runs) > 0 │
│ + ) │
│ + `, region, rslug, rslug).Scan(&total); err │
│ != nil { │
│ + return fmt.Errorf("total-runs count (real │
│ m %s/%s): %w", region, rslug, err) │
│ + } │
│ + │
│ + pages := (total + pageSize - 1) / pageSize │
│ + for p := 1; p <= pages; p++ { │
│ + offset := (p - 1) * pageSize │
│ + rows, err := db.Query(totalRunsBaseSelect │
│ ()+` │
│ + WHERE r.region = ? AND (r.slug = ? OR r. │
│ parent_realm_slug = ?) │
│ + `+totalRunsHaving+` │
│ + ORDER BY all_time_runs DESC, p.name ASC │
│ + LIMIT ? OFFSET ? │
│ + `, region, rslug, rslug, pageSize, offset │
│ ) │
│ + if err != nil { │
│ + return err │
│ + } │
│ + list, err := scanTotalRunsRows(rows) │
│ + if err != nil { │
│ + return err │
│ + } │
│ + title := strings.ToUpper(region) + "/" + │
│ rslug + " Total Runs" │
│ + page := buildTotalRunsPage(list, title, t │
│ otal, pages, p, pageSize) │
│ + if err := writer.WriteJSONFileCompact(fil │
│ epath.Join(dir, fmt.Sprintf("%d.json", p)), │
│ page); err != nil { │
│ + return err │
│ + } │
│ + } │
│ + return nil │
│ +} │
│ + │
│ +// filters in Go so we can reuse the wow.Ge │
│ tClassAndSpec fallback when │
│ +// pd.class_name is missing │
│ +func generateTotalRunsClass(db *sql.DB, roo │
│ t, classKey string, pageSize int, regions [] │
│ string) error { │
│ + if err := generateTotalRunsClassScope(db, │
│ root, "global", "", "", classKey, pageSize); │
│ err != nil { │
│ + return err │
│ + } │
│ + for _, reg := range regions { │
│ + if err := generateTotalRunsClassScope(db, │
│ root, "regional", reg, "", classKey, pageSi │
│ ze); err != nil { │
│ + return err │
│ + } │
│ + rrows, err := db.Query(` │
│ + SELECT slug │
│ + FROM realms │
│ + WHERE region = ? AND (parent_realm_slug │
│ IS NULL OR parent_realm_slug = '') │
│ + ORDER BY slug │
│ + `, reg) │
│ + if err != nil { │
│ + return fmt.Errorf("total-runs class real │
│ ms list: %w", err) │
│ + } │
│ + var slugs []string │
│ + for rrows.Next() { │
│ + var s string │
│ + if err := rrows.Scan(&s); err != nil { │
│ + rrows.Close() │
│ + return err │
│ + } │
│ + slugs = append(slugs, s) │
│ + } │
│ + rrows.Close() │
│ + for _, rslug := range slugs { │
│ + if err := generateTotalRunsClassScope(db │
│ , root, "realm", reg, rslug, classKey, pageS │
│ ize); err != nil { │
│ + return err │
│ + } │
│ + } │
│ + } │
│ + return nil │
│ +} │
│ + │
│ +func generateTotalRunsClassScope(db *sql.DB │
│ , root, scope, region, rslug, classKey strin │
│ g, pageSize int) error { │
│ + var dir string │
│ + switch scope { │
│ + case "global": │
│ + dir = filepath.Join(root, "class", classK │
│ ey, "global") │
│ + case "regional": │
│ + dir = filepath.Join(root, "class", classK │
│ ey, "regional", region) │
│ + default: │
│ + dir = filepath.Join(root, "class", classK │
│ ey, "realm", region, rslug) │
│ + } │
│ + if err := os.MkdirAll(dir, 0o755); err != │
│ nil { │
│ + return err │
│ + } │
│ + │
│ + var rows *sql.Rows │
│ + var err error │
│ + switch scope { │
│ + case "global": │
│ + rows, err = db.Query(totalRunsBaseSelect( │
│ ) + totalRunsHaving + ` │
│ + ORDER BY all_time_runs DESC, p.name ASC │
│ + `) │
│ + case "regional": │
│ + rows, err = db.Query(totalRunsBaseSelect( │
│ )+` │
│ + WHERE r.region = ? │
│ + `+totalRunsHaving+` │
│ + ORDER BY all_time_runs DESC, p.name ASC │
│ + `, region) │
│ + default: │
│ + rows, err = db.Query(totalRunsBaseSelect( │
│ )+` │
│ + WHERE r.region = ? AND (r.slug = ? OR r. │
│ parent_realm_slug = ?) │
│ + `+totalRunsHaving+` │
│ + ORDER BY all_time_runs DESC, p.name ASC │
│ + `, region, rslug, rslug) │
│ + } │
│ + if err != nil { │
│ + return err │
│ + } │
│ + all, err := scanTotalRunsRows(rows) │
│ + if err != nil { │
│ + return err │
│ + } │
│ + │
│ + matching := make([]map[string]any, 0, len( │
│ all)/4) │
│ + for _, r := range all { │
│ + cls, _ := r["class_name"].(string) │
│ + key := strings.ReplaceAll(strings.ToLower │
│ (cls), " ", "_") │
│ + if key == classKey { │
│ + matching = append(matching, r) │
│ + } │
│ + } │
│ + │
│ + total := len(matching) │
│ + pages := (total + pageSize - 1) / pageSize │
│ + for p := 1; p <= pages; p++ { │
│ + offset := (p - 1) * pageSize │
│ + end := offset + pageSize │
│ + if end > total { │
│ + end = total │
│ + } │
│ + title := "Global Total Runs" │
│ + switch scope { │
│ + case "regional": │
│ + title = strings.ToUpper(region) + " Tota │
│ l Runs" │
│ + case "realm": │
│ + title = strings.ToUpper(region) + "/" + │
│ rslug + " Total Runs" │
│ + } │
│ + page := buildTotalRunsPage(matching[offse │
│ t:end], title, total, pages, p, pageSize) │
│ + if err := writer.WriteJSONFileCompact(fil │
│ epath.Join(dir, fmt.Sprintf("%d.json", p)), │
│ page); err != nil { │
│ + return err │
│ + } │
│ + } │
│ + return nil │
│ +} │
│ + │
│ +// field names mirror scanPlayerRows so the │
│ frontend table component can │
│ +// render either tree without branching │
│ +func scanTotalRunsRows(rows *sql.Rows) ([]m │
│ ap[string]any, error) { │
│ + defer rows.Close() │
│ + var out []map[string]any │
│ + for rows.Next() { │
│ + var ( │
│ + id int64 │
│ + name, realmSlug, realmName string │
│ + region, className, activeSpecName string │
│ + mainSpecID sql.Nu │
│ llInt64 │
│ + allTimeRuns sql.Nu │
│ llInt64 │
│ + ) │
│ + if err := rows.Scan(&id, &name, &realmSlu │
│ g, &realmName, ®ion, &className, &activeS │
│ pecName, &mainSpecID, &allTimeRuns); err != │
│ nil { │
│ + return nil, err │
│ + } │
│ + obj := map[string]any{ │
│ + "player_id": id, │
│ + "name": name, │
│ + "realm_slug": realmSlug, │
│ + "realm_name": realmName, │
│ + "region": region, │
│ + "class_name": className, │
│ + "total_runs": allTimeRuns.Int64, │
│ + } │
│ + if mainSpecID.Valid { │
│ + v := int(mainSpecID.Int64) │
│ + cls, spec := wow.FallbackClassAndSpec(cl │
│ assName, activeSpecName, &v) │
│ + obj["class_name"] = cls │
│ + obj["active_spec_name"] = spec │
│ + obj["main_spec_id"] = v │
│ + } else if activeSpecName != "" { │
│ + obj["active_spec_name"] = activeSpecName │
│ + } │
│ + out = append(out, obj) │
│ + } │
│ + return out, nil │
│ +} │
│ + │
│ +// shape mirrors buildPlayerLeaderboardPage │
│ so Pagination and the table │
│ +// component can render either tree without │
│ branching │
│ +func buildTotalRunsPage(list []map[string]a │
│ ny, title string, total, totalPages, current │
│ Page, pageSize int) map[string]any { │
│ + return map[string]any{ │
│ + "leaderboard": list, │
│ + "title": title, │
│ + "pagination": map[string]any{ │
│ + "currentPage": currentPage, │
│ + "pageSize": pageSize, │
│ + "totalPlayers": total, │
│ + "totalPages": totalPages, │
│ + "hasNextPage": currentPage < totalPages │
│ , │
│ + "hasPrevPage": currentPage > 1, │
│ + }, │
│ + } │
│ +} │
└──────────────────────────────────────────────┘