┌─ nix/pkgs/ookstats/src/internal/blizzard/types.go ─────────────────────────┐│ diff --git a/nix/pkgs/ookstats/src/internal/blizzard/types.go b/nix/pkgs/ookstats/ ││ src/internal/blizzard/types.go ││ index 6f1a2e0..25f338c 100644 ││ --- a/nix/pkgs/ookstats/src/internal/blizzard/types.go ││ +++ b/nix/pkgs/ookstats/src/internal/blizzard/types.go ││ @@ -2,10 +2,11 @@ package blizzard ││ ││ // RealmInfo represents a realm ││ type RealmInfo struct { ││ - ID int `json:"id"` ││ - Name string `json:"name"` ││ - Region string `json:"region"` ││ - Slug string `json:"slug"` ││ + ID int `json:"id"` ││ + Name string `json:"name"` ││ + Region string `json:"region"` ││ + Slug string `json:"slug"` ││ + ParentRealmSlug string `json:"parent_realm_slug,omitempty"` ││ } ││ ││ // DungeonInfo represents a challenge mode dungeon │└────────────────────────────────────────────────────────────────────────────────────┘
┌─ ...rc/internal/blizzard/types.go ───┐│ diff --git a/nix/pkgs/ookstats/src/internal/ ││ blizzard/types.go b/nix/pkgs/ookstats/src/in ││ ternal/blizzard/types.go ││ index 6f1a2e0..25f338c 100644 ││ --- a/nix/pkgs/ookstats/src/internal/blizzar ││ d/types.go ││ +++ b/nix/pkgs/ookstats/src/internal/blizzar ││ d/types.go ││ @@ -2,10 +2,11 @@ package blizzard ││ ││ // RealmInfo represents a realm ││ type RealmInfo struct { ││ - ID int `json:"id"` ││ - Name string `json:"name"` ││ - Region string `json:"region"` ││ - Slug string `json:"slug"` ││ + ID int `json:"id"` ││ + Name string `json:"name"` ││ + Region string `json:"region"` ││ + Slug string `json:"slug"` ││ + ParentRealmSlug string `json:"parent_realm ││ _slug,omitempty"` ││ } ││ ││ // DungeonInfo represents a challenge mode ││ dungeon │└──────────────────────────────────────────────┘
┌─ nix/pkgs/ookstats/src/internal/database/client.go ────────────────────────┐│ diff --git a/nix/pkgs/ookstats/src/internal/database/client.go b/nix/pkgs/ookstats ││ /src/internal/database/client.go ││ index 010b398..469e4f1 100644 ││ --- a/nix/pkgs/ookstats/src/internal/database/client.go ││ +++ b/nix/pkgs/ookstats/src/internal/database/client.go ││ @@ -141,7 +141,8 @@ func EnsureCompleteSchema(db *sql.DB) error { ││ slug TEXT, ││ name TEXT, ││ region TEXT, ││ - connected_realm_id INTEGER UNIQUE ││ + connected_realm_id INTEGER UNIQUE, ││ + parent_realm_slug TEXT ││ )`, ││ ││ // Core leaderboard data ││ @@ -412,13 +413,17 @@ func migrateRealmsCompositeSlug(db *sql.DB) error { ││ slug TEXT, ││ name TEXT, ││ region TEXT, ││ - connected_realm_id INTEGER UNIQUE ││ + connected_realm_id INTEGER UNIQUE, ││ + parent_realm_slug TEXT ││ ) ││ `); err != nil { return fmt.Errorf("create realms_new: %w", err) } ││ ││ // Copy data ││ - if _, err := tx.Exec(`INSERT INTO realms_new (id, slug, name, region, connect ││ ed_realm_id) ││ - SELECT id, slug, name, region, connected_realm_id FROM ││ realms`); err != nil { ││ + if _, err := tx.Exec(`INSERT INTO realms_new (id, slug, name, region, connect ││ ed_realm_id, parent_realm_slug) ││ + SELECT id, slug, name, region, connected_realm_id, ││ + CASE WHEN EXISTS(SELECT 1 FROM sqlite_master WHE ││ RE type='table' AND name='realms' AND sql LIKE '%parent_realm_slug%') ││ + THEN parent_realm_slug ELSE NULL END ││ + FROM realms`); err != nil { ││ return fmt.Errorf("copy realms: %w", err) ││ } ││ │└────────────────────────────────────────────────────────────────────────────────────┘
┌─ ...c/internal/database/client.go ───┐│ diff --git a/nix/pkgs/ookstats/src/internal/ ││ database/client.go b/nix/pkgs/ookstats/src/i ││ nternal/database/client.go ││ index 010b398..469e4f1 100644 ││ --- a/nix/pkgs/ookstats/src/internal/databas ││ e/client.go ││ +++ b/nix/pkgs/ookstats/src/internal/databas ││ e/client.go ││ @@ -141,7 +141,8 @@ func EnsureCompleteSchem ││ a(db *sql.DB) error { ││ slug TEXT, ││ name TEXT, ││ region TEXT, ││ - connected_realm_id INTEGER UNIQ ││ UE ││ + connected_realm_id INTEGER UNIQ ││ UE, ││ + parent_realm_slug TEXT ││ )`, ││ ││ // Core leaderboard data ││ @@ -412,13 +413,17 @@ func migrateRealmsComp ││ ositeSlug(db *sql.DB) error { ││ slug TEXT, ││ name TEXT, ││ region TEXT, ││ - connected_realm_id INTEGER UNIQ ││ UE ││ + connected_realm_id INTEGER UNIQ ││ UE, ││ + parent_realm_slug TEXT ││ ) ││ `); err != nil { return fmt.Errorf("cre ││ ate realms_new: %w", err) } ││ ││ // Copy data ││ - if _, err := tx.Exec(`INSERT INTO realm ││ s_new (id, slug, name, region, connected_rea ││ lm_id) ││ - SELECT id, slug, ││ name, region, connected_realm_id FROM realms ││ `); err != nil { ││ + if _, err := tx.Exec(`INSERT INTO realm ││ s_new (id, slug, name, region, connected_rea ││ lm_id, parent_realm_slug) ││ + SELECT id, slug, ││ name, region, connected_realm_id, ││ + CASE WHEN ││ EXISTS(SELECT 1 FROM sqlite_master WHERE typ ││ e='table' AND name='realms' AND sql LIKE '%p ││ arent_realm_slug%') ││ + THEN ││ parent_realm_slug ELSE NULL END ││ + FROM realms`); er ││ r != nil { ││ return fmt.Errorf("copy realms: %w" ││ , err) ││ } ││ │└──────────────────────────────────────────────┘
┌─ nix/pkgs/ookstats/src/internal/database/operations.go ────────────────────┐│ diff --git a/nix/pkgs/ookstats/src/internal/database/operations.go b/nix/pkgs/ooks ││ tats/src/internal/database/operations.go ││ index ce0fb11..1518169 100644 ││ --- a/nix/pkgs/ookstats/src/internal/database/operations.go ││ +++ b/nix/pkgs/ookstats/src/internal/database/operations.go ││ @@ -16,10 +16,10 @@ import ( ││ func (ds *DatabaseService) EnsureReferenceData(realmInfo blizzard.RealmInfo, dung ││ eons []blizzard.DungeonInfo) error { ││ // insert realm data ││ realmQuery := ` ││ - INSERT OR IGNORE INTO realms (slug, name, region, connected_realm_id) ││ - VALUES (?, ?, ?, ?) ││ + INSERT OR IGNORE INTO realms (slug, name, region, connected_realm_id, parent_re ││ alm_slug) ││ + VALUES (?, ?, ?, ?, ?) ││ ` ││ - _, err := ds.db.Exec(realmQuery, realmInfo.Slug, realmInfo.Name, realmInfo.Regio ││ n, realmInfo.ID) ││ + _, err := ds.db.Exec(realmQuery, realmInfo.Slug, realmInfo.Name, realmInfo.Regio ││ n, realmInfo.ID, realmInfo.ParentRealmSlug) ││ if err != nil { ││ return fmt.Errorf("failed to insert realm data: %w", err) ││ } ││ @@ -76,8 +76,8 @@ func (ds *DatabaseService) EnsureRealmsBatch(realms map[string]b ││ lizzard.RealmInf ││ defer tx.Rollback() ││ ││ stmt, err := tx.Prepare(` ││ - INSERT OR IGNORE INTO realms (slug, name, region, connected_realm_id) ││ - VALUES (?, ?, ?, ?) ││ + INSERT OR IGNORE INTO realms (slug, name, region, connected_realm_id, par ││ ent_realm_slug) ││ + VALUES (?, ?, ?, ?, ?) ││ `) ││ if err != nil { ││ return err ││ @@ -97,7 +97,7 @@ func (ds *DatabaseService) EnsureRealmsBatch(realms map[string]b ││ lizzard.RealmInf ││ total := len(keys) ││ for i, slug := range keys { ││ ri := realms[slug] ││ - if _, err := stmt.Exec(ri.Slug, ri.Name, ri.Region, ri.ID); err != nil { ││ + if _, err := stmt.Exec(ri.Slug, ri.Name, ri.Region, ri.ID, ri.ParentRealm ││ Slug); err != nil { ││ return fmt.Errorf("failed to insert realm %s: %w", ri.Slug, err) ││ } ││ // light progress every 10 items ││ @@ -296,9 +296,9 @@ func (ds *DatabaseService) getOrCreateRealm(tx *sql.Tx, realmS ││ lug string, allRea ││ if realmInfo, exists := allRealms[realmSlug]; exists { ││ // insert known realm ││ result, err := tx.Exec(` ││ - INSERT INTO realms (slug, name, region, connected_realm_id) ││ - VALUES (?, ?, ?, ?) ││ - `, realmSlug, realmInfo.Name, realmInfo.Region, realmInfo.ID) ││ + INSERT INTO realms (slug, name, region, connected_realm_id, parent_realm_slug) ││ + VALUES (?, ?, ?, ?, ?) ││ + `, realmSlug, realmInfo.Name, realmInfo.Region, realmInfo.ID, realmInfo.ParentR ││ ealmSlug) ││ if err != nil { ││ return 0, err ││ } ││ @@ -308,9 +308,9 @@ func (ds *DatabaseService) getOrCreateRealm(tx *sql.Tx, realmS ││ lug string, allRea ││ } else { ││ // insert unknown realm as placeholder ││ result, err := tx.Exec(` ││ - INSERT INTO realms (slug, name, region, connected_realm_id) ││ - VALUES (?, ?, ?, ?) ││ - `, realmSlug, utils.Slugify(realmSlug), "unknown", 0) ││ + INSERT INTO realms (slug, name, region, connected_realm_id, parent_realm_slug) ││ + VALUES (?, ?, ?, NULL, ?) ││ + `, realmSlug, utils.Slugify(realmSlug), "unknown", "") ││ if err != nil { ││ return 0, err ││ } ││ @@ -552,8 +552,8 @@ func (ds *DatabaseService) processBatch(batch []blizzard.Fetch ││ Result) (int, int, ││ // ensureReferenceDataTx ensures reference data within a transaction ││ func (ds *DatabaseService) ensureReferenceDataTx(tx *sql.Tx, realmInfo blizzard.R ││ ealmInfo, dungeons []blizzard.DungeonInfo) error { ││ // insert realm data ││ - realmQuery := `INSERT OR IGNORE INTO realms (slug, name, region, connected_realm ││ _id) VALUES (?, ?, ?, ?)` ││ - _, err := tx.Exec(realmQuery, realmInfo.Slug, realmInfo.Name, realmInfo.Region, ││ realmInfo.ID) ││ + realmQuery := `INSERT OR IGNORE INTO realms (slug, name, region, connected_realm ││ _id, parent_realm_slug) VALUES (?, ?, ?, ?, ?)` ││ + _, err := tx.Exec(realmQuery, realmInfo.Slug, realmInfo.Name, realmInfo.Region, ││ realmInfo.ID, realmInfo.ParentRealmSlug) ││ if err != nil { ││ return fmt.Errorf("failed to insert realm data: %w", err) ││ } ││ @@ -686,8 +686,8 @@ func (ds *DatabaseService) insertLeaderboardDataTx(tx *sql.Tx, ││ leaderboard *bliz ││ return 0, 0, fmt.Errorf("failed to resolve player realm: %w", ││ err) ││ } ││ if playerRealmID == 0 { ││ - if _, err := tx.Exec(`INSERT OR IGNORE INTO realms (slug, nam ││ e, region, connected_realm_id) VALUES (?, ?, ?, ?)`, ││ - playerRealmSlug, playerRealmSlug, realmInfo.Region, 0); e ││ rr != nil { ││ + if _, err := tx.Exec(`INSERT OR IGNORE INTO realms (slug, nam ││ e, region, connected_realm_id, parent_realm_slug) VALUES (?, ?, ?, NULL, ?)`, ││ + playerRealmSlug, playerRealmSlug, realmInfo.Region, ""); ││ err != nil { ││ return 0, 0, fmt.Errorf("failed to create placeholder rea ││ lm: %w", err) ││ } ││ id2, err := ds.getRealmIDTx(tx, playerRealmSlug, realmInfo.Re ││ gion) ││ @@ -794,9 +794,9 @@ func (ds *DatabaseService) getOrCreateRealmTx(tx *sql.Tx, real ││ mSlug string, allR ││ // realm doesn't exist, create it ││ if realmInfo, exists := allRealms[realmSlug]; exists { ││ result, err := tx.Exec(` ││ - INSERT INTO realms (slug, name, region, connected_realm_id) ││ - VALUES (?, ?, ?, ?) ││ - `, realmSlug, realmInfo.Name, realmInfo.Region, realmInfo.ID) ││ + INSERT INTO realms (slug, name, region, connected_realm_id, parent_realm_slug) ││ + VALUES (?, ?, ?, ?, ?) ││ + `, realmSlug, realmInfo.Name, realmInfo.Region, realmInfo.ID, realmInfo.ParentR ││ ealmSlug) ││ if err != nil { ││ return 0, err ││ } ││ @@ -805,9 +805,9 @@ func (ds *DatabaseService) getOrCreateRealmTx(tx *sql.Tx, real ││ mSlug string, allR ││ return int(id), err ││ } else { ││ result, err := tx.Exec(` ││ - INSERT INTO realms (slug, name, region, connected_realm_id) ││ - VALUES (?, ?, ?, ?) ││ - `, realmSlug, utils.Slugify(realmSlug), "unknown", 0) ││ + INSERT INTO realms (slug, name, region, connected_realm_id, parent_realm_slug) ││ + VALUES (?, ?, ?, NULL, ?) ││ + `, realmSlug, utils.Slugify(realmSlug), "unknown", "") ││ if err != nil { ││ return 0, err ││ } ││ @@ -832,9 +832,9 @@ func (ds *DatabaseService) getOrCreateRealmByRegion(tx *sql.Tx ││ , realmSlug string ││ // If we know this realm (by slug) from constants, use its canonical region/n ││ ame/id ││ if realmInfo, ok := allRealms[realmSlug]; ok { ││ result, err := tx.Exec(` ││ - INSERT INTO realms (slug, name, region, connected_realm_id) ││ - VALUES (?, ?, ?, ?) ││ - `, realmSlug, realmInfo.Name, realmInfo.Region, realmInfo.ID) ││ + INSERT INTO realms (slug, name, region, connected_realm_id, parent_re ││ alm_slug) ││ + VALUES (?, ?, ?, ?, ?) ││ + `, realmSlug, realmInfo.Name, realmInfo.Region, realmInfo.ID, realmInfo.P ││ arentRealmSlug) ││ if err != nil { ││ return 0, err ││ } ││ @@ -844,9 +844,9 @@ func (ds *DatabaseService) getOrCreateRealmByRegion(tx *sql.Tx ││ , realmSlug string ││ ││ // otherwise create a placeholder scoped to the provided region ││ result, err := tx.Exec(` ││ - INSERT INTO realms (slug, name, region, connected_realm_id) ││ - VALUES (?, ?, ?, ?) ││ - `, realmSlug, utils.Slugify(realmSlug), region, 0) ││ + INSERT INTO realms (slug, name, region, connected_realm_id, parent_realm_ ││ slug) ││ + VALUES (?, ?, ?, NULL, ?) ││ + `, realmSlug, utils.Slugify(realmSlug), region, "") ││ if err != nil { ││ return 0, err ││ } ││ @@ -1375,3 +1375,54 @@ func (ds *DatabaseService) GetPeriodsForSeason(seasonID int ││ ) ([]int, error) { ││ } ││ return periods, rows.Err() ││ } ││ + ││ +// GetRealmPoolIDs returns all realm IDs in a realm pool (parent + all children) ││ +// For a child realm, it returns the parent and all siblings ││ +// For a parent realm, it returns itself and all children ││ +// For an independent realm, it returns just itself ││ +func (ds *DatabaseService) GetRealmPoolIDs(region, slug string) ([]int, error) { ││ + // First, get the realm's parent_realm_slug ││ + var parentSlug sql.NullString ││ + err := ds.db.QueryRow(` ││ + SELECT parent_realm_slug ││ + FROM realms ││ + WHERE region = ? AND slug = ? ││ + `, region, slug).Scan(&parentSlug) ││ + if err != nil { ││ + if err == sql.ErrNoRows { ││ + return []int{}, nil ││ + } ││ + return nil, fmt.Errorf("failed to query realm: %w", err) ││ + } ││ + ││ + // Determine the pool leader slug ││ + poolLeaderSlug := slug ││ + if parentSlug.Valid && parentSlug.String != "" { ││ + // This is a child realm, use the parent as pool leader ││ + poolLeaderSlug = parentSlug.String ││ + } ││ + ││ + // Get all realms in the pool: the pool leader + all realms that have it as pare ││ nt ││ + query := ` ││ + SELECT id FROM realms ││ + WHERE region = ? AND ( ││ + slug = ? OR parent_realm_slug = ? ││ + ) ││ + ORDER BY id ││ + ` ││ + rows, err := ds.db.Query(query, region, poolLeaderSlug, poolLeaderSlug) ││ + if err != nil { ││ + return nil, fmt.Errorf("failed to query realm pool: %w", err) ││ + } ││ + defer rows.Close() ││ + ││ + var poolIDs []int ││ + for rows.Next() { ││ + var id int ││ + if err := rows.Scan(&id); err != nil { ││ + return nil, err ││ + } ││ + poolIDs = append(poolIDs, id) ││ + } ││ + return poolIDs, rows.Err() ││ +} │└────────────────────────────────────────────────────────────────────────────────────┘
┌─ ...ternal/database/operations.go ───┐│ diff --git a/nix/pkgs/ookstats/src/internal/ ││ database/operations.go b/nix/pkgs/ookstats/s ││ rc/internal/database/operations.go ││ index ce0fb11..1518169 100644 ││ --- a/nix/pkgs/ookstats/src/internal/databas ││ e/operations.go ││ +++ b/nix/pkgs/ookstats/src/internal/databas ││ e/operations.go ││ @@ -16,10 +16,10 @@ import ( ││ func (ds *DatabaseService) EnsureReferenceD ││ ata(realmInfo blizzard.RealmInfo, dungeons [ ││ ]blizzard.DungeonInfo) error { ││ // insert realm data ││ realmQuery := ` ││ - INSERT OR IGNORE INTO realms (slug, name, ││ region, connected_realm_id) ││ - VALUES (?, ?, ?, ?) ││ + INSERT OR IGNORE INTO realms (slug, name, ││ region, connected_realm_id, parent_realm_sl ││ ug) ││ + VALUES (?, ?, ?, ?, ?) ││ ` ││ - _, err := ds.db.Exec(realmQuery, realmInfo ││ .Slug, realmInfo.Name, realmInfo.Region, rea ││ lmInfo.ID) ││ + _, err := ds.db.Exec(realmQuery, realmInfo ││ .Slug, realmInfo.Name, realmInfo.Region, rea ││ lmInfo.ID, realmInfo.ParentRealmSlug) ││ if err != nil { ││ return fmt.Errorf("failed to insert realm ││ data: %w", err) ││ } ││ @@ -76,8 +76,8 @@ func (ds *DatabaseService) ││ EnsureRealmsBatch(realms map[string]blizzar ││ d.RealmInf ││ defer tx.Rollback() ││ ││ stmt, err := tx.Prepare(` ││ - INSERT OR IGNORE INTO realms (slug, ││ name, region, connected_realm_id) ││ - VALUES (?, ?, ?, ?) ││ + INSERT OR IGNORE INTO realms (slug, ││ name, region, connected_realm_id, parent_re ││ alm_slug) ││ + VALUES (?, ?, ?, ?, ?) ││ `) ││ if err != nil { ││ return err ││ @@ -97,7 +97,7 @@ func (ds *DatabaseService) ││ EnsureRealmsBatch(realms map[string]blizzar ││ d.RealmInf ││ total := len(keys) ││ for i, slug := range keys { ││ ri := realms[slug] ││ - if _, err := stmt.Exec(ri.Slug, ri. ││ Name, ri.Region, ri.ID); err != nil { ││ + if _, err := stmt.Exec(ri.Slug, ri. ││ Name, ri.Region, ri.ID, ri.ParentRealmSlug); ││ err != nil { ││ return fmt.Errorf("failed to in ││ sert realm %s: %w", ri.Slug, err) ││ } ││ // light progress every 10 items ││ @@ -296,9 +296,9 @@ func (ds *DatabaseServic ││ e) getOrCreateRealm(tx *sql.Tx, realmSlug st ││ ring, allRea ││ if realmInfo, exists := allRealms[realmSlu ││ g]; exists { ││ // insert known realm ││ result, err := tx.Exec(` ││ - INSERT INTO realms (slug, name, region, ││ connected_realm_id) ││ - VALUES (?, ?, ?, ?) ││ - `, realmSlug, realmInfo.Name, realmInfo.R ││ egion, realmInfo.ID) ││ + INSERT INTO realms (slug, name, region, ││ connected_realm_id, parent_realm_slug) ││ + VALUES (?, ?, ?, ?, ?) ││ + `, realmSlug, realmInfo.Name, realmInfo.R ││ egion, realmInfo.ID, realmInfo.ParentRealmSl ││ ug) ││ if err != nil { ││ return 0, err ││ } ││ @@ -308,9 +308,9 @@ func (ds *DatabaseServic ││ e) getOrCreateRealm(tx *sql.Tx, realmSlug st ││ ring, allRea ││ } else { ││ // insert unknown realm as placeholder ││ result, err := tx.Exec(` ││ - INSERT INTO realms (slug, name, region, ││ connected_realm_id) ││ - VALUES (?, ?, ?, ?) ││ - `, realmSlug, utils.Slugify(realmSlug), " ││ unknown", 0) ││ + INSERT INTO realms (slug, name, region, ││ connected_realm_id, parent_realm_slug) ││ + VALUES (?, ?, ?, NULL, ?) ││ + `, realmSlug, utils.Slugify(realmSlug), " ││ unknown", "") ││ if err != nil { ││ return 0, err ││ } ││ @@ -552,8 +552,8 @@ func (ds *DatabaseServic ││ e) processBatch(batch []blizzard.FetchResult ││ ) (int, int, ││ // ensureReferenceDataTx ensures reference ││ data within a transaction ││ func (ds *DatabaseService) ensureReferenceD ││ ataTx(tx *sql.Tx, realmInfo blizzard.RealmIn ││ fo, dungeons []blizzard.DungeonInfo) error { ││ // insert realm data ││ - realmQuery := `INSERT OR IGNORE INTO realm ││ s (slug, name, region, connected_realm_id) V ││ ALUES (?, ?, ?, ?)` ││ - _, err := tx.Exec(realmQuery, realmInfo.Sl ││ ug, realmInfo.Name, realmInfo.Region, realmI ││ nfo.ID) ││ + realmQuery := `INSERT OR IGNORE INTO realm ││ s (slug, name, region, connected_realm_id, p ││ arent_realm_slug) VALUES (?, ?, ?, ?, ?)` ││ + _, err := tx.Exec(realmQuery, realmInfo.Sl ││ ug, realmInfo.Name, realmInfo.Region, realmI ││ nfo.ID, realmInfo.ParentRealmSlug) ││ if err != nil { ││ return fmt.Errorf("failed to insert realm ││ data: %w", err) ││ } ││ @@ -686,8 +686,8 @@ func (ds *DatabaseServic ││ e) insertLeaderboardDataTx(tx *sql.Tx, leade ││ rboard *bliz ││ return 0, 0, fmt.Errorf ││ ("failed to resolve player realm: %w", err) ││ } ││ if playerRealmID == 0 { ││ - if _, err := tx.Exec(`I ││ NSERT OR IGNORE INTO realms (slug, name, reg ││ ion, connected_realm_id) VALUES (?, ?, ?, ?) ││ `, ││ - playerRealmSlug, pl ││ ayerRealmSlug, realmInfo.Region, 0); err != ││ nil { ││ + if _, err := tx.Exec(`I ││ NSERT OR IGNORE INTO realms (slug, name, reg ││ ion, connected_realm_id, parent_realm_slug) ││ VALUES (?, ?, ?, NULL, ?)`, ││ + playerRealmSlug, pl ││ ayerRealmSlug, realmInfo.Region, ""); err != ││ nil { ││ return 0, 0, fmt.Er ││ rorf("failed to create placeholder realm: %w ││ ", err) ││ } ││ id2, err := ds.getRealm ││ IDTx(tx, playerRealmSlug, realmInfo.Region) ││ @@ -794,9 +794,9 @@ func (ds *DatabaseServic ││ e) getOrCreateRealmTx(tx *sql.Tx, realmSlug ││ string, allR ││ // realm doesn't exist, create it ││ if realmInfo, exists := allRealms[realmSlu ││ g]; exists { ││ result, err := tx.Exec(` ││ - INSERT INTO realms (slug, name, region, ││ connected_realm_id) ││ - VALUES (?, ?, ?, ?) ││ - `, realmSlug, realmInfo.Name, realmInfo.R ││ egion, realmInfo.ID) ││ + INSERT INTO realms (slug, name, region, ││ connected_realm_id, parent_realm_slug) ││ + VALUES (?, ?, ?, ?, ?) ││ + `, realmSlug, realmInfo.Name, realmInfo.R ││ egion, realmInfo.ID, realmInfo.ParentRealmSl ││ ug) ││ if err != nil { ││ return 0, err ││ } ││ @@ -805,9 +805,9 @@ func (ds *DatabaseServic ││ e) getOrCreateRealmTx(tx *sql.Tx, realmSlug ││ string, allR ││ return int(id), err ││ } else { ││ result, err := tx.Exec(` ││ - INSERT INTO realms (slug, name, region, ││ connected_realm_id) ││ - VALUES (?, ?, ?, ?) ││ - `, realmSlug, utils.Slugify(realmSlug), " ││ unknown", 0) ││ + INSERT INTO realms (slug, name, region, ││ connected_realm_id, parent_realm_slug) ││ + VALUES (?, ?, ?, NULL, ?) ││ + `, realmSlug, utils.Slugify(realmSlug), " ││ unknown", "") ││ if err != nil { ││ return 0, err ││ } ││ @@ -832,9 +832,9 @@ func (ds *DatabaseServic ││ e) getOrCreateRealmByRegion(tx *sql.Tx, real ││ mSlug string ││ // If we know this realm (by slug) from ││ constants, use its canonical region/name/id ││ if realmInfo, ok := allRealms[realmSlug ││ ]; ok { ││ result, err := tx.Exec(` ││ - INSERT INTO realms (slug, name, ││ region, connected_realm_id) ││ - VALUES (?, ?, ?, ?) ││ - `, realmSlug, realmInfo.Name, realm ││ Info.Region, realmInfo.ID) ││ + INSERT INTO realms (slug, name, ││ region, connected_realm_id, parent_realm_sl ││ ug) ││ + VALUES (?, ?, ?, ?, ?) ││ + `, realmSlug, realmInfo.Name, realm ││ Info.Region, realmInfo.ID, realmInfo.ParentR ││ ealmSlug) ││ if err != nil { ││ return 0, err ││ } ││ @@ -844,9 +844,9 @@ func (ds *DatabaseServic ││ e) getOrCreateRealmByRegion(tx *sql.Tx, real ││ mSlug string ││ ││ // otherwise create a placeholder scope ││ d to the provided region ││ result, err := tx.Exec(` ││ - INSERT INTO realms (slug, name, reg ││ ion, connected_realm_id) ││ - VALUES (?, ?, ?, ?) ││ - `, realmSlug, utils.Slugify(realmSlug), ││ region, 0) ││ + INSERT INTO realms (slug, name, reg ││ ion, connected_realm_id, parent_realm_slug) ││ + VALUES (?, ?, ?, NULL, ?) ││ + `, realmSlug, utils.Slugify(realmSlug), ││ region, "") ││ if err != nil { ││ return 0, err ││ } ││ @@ -1375,3 +1375,54 @@ func (ds *DatabaseSer ││ vice) GetPeriodsForSeason(seasonID int) ([]i ││ nt, error) { ││ } ││ return periods, rows.Err() ││ } ││ + ││ +// GetRealmPoolIDs returns all realm IDs in ││ a realm pool (parent + all children) ││ +// For a child realm, it returns the parent ││ and all siblings ││ +// For a parent realm, it returns itself an ││ d all children ││ +// For an independent realm, it returns jus ││ t itself ││ +func (ds *DatabaseService) GetRealmPoolIDs( ││ region, slug string) ([]int, error) { ││ + // First, get the realm's parent_realm_slu ││ g ││ + var parentSlug sql.NullString ││ + err := ds.db.QueryRow(` ││ + SELECT parent_realm_slug ││ + FROM realms ││ + WHERE region = ? AND slug = ? ││ + `, region, slug).Scan(&parentSlug) ││ + if err != nil { ││ + if err == sql.ErrNoRows { ││ + return []int{}, nil ││ + } ││ + return nil, fmt.Errorf("failed to query r ││ ealm: %w", err) ││ + } ││ + ││ + // Determine the pool leader slug ││ + poolLeaderSlug := slug ││ + if parentSlug.Valid && parentSlug.String ! ││ = "" { ││ + // This is a child realm, use the parent ││ as pool leader ││ + poolLeaderSlug = parentSlug.String ││ + } ││ + ││ + // Get all realms in the pool: the pool le ││ ader + all realms that have it as parent ││ + query := ` ││ + SELECT id FROM realms ││ + WHERE region = ? AND ( ││ + slug = ? OR parent_realm_slug = ? ││ + ) ││ + ORDER BY id ││ + ` ││ + rows, err := ds.db.Query(query, region, po ││ olLeaderSlug, poolLeaderSlug) ││ + if err != nil { ││ + return nil, fmt.Errorf("failed to query r ││ ealm pool: %w", err) ││ + } ││ + defer rows.Close() ││ + ││ + var poolIDs []int ││ + for rows.Next() { ││ + var id int ││ + if err := rows.Scan(&id); err != nil { ││ + return nil, err ││ + } ││ + poolIDs = append(poolIDs, id) ││ + } ││ + return poolIDs, rows.Err() ││ +} │└──────────────────────────────────────────────┘
┌─ nix/pkgs/ookstats/src/internal/generator/player_leaderboards.go ──────────┐│ diff --git a/nix/pkgs/ookstats/src/internal/generator/player_leaderboards.go b/nix ││ /pkgs/ookstats/src/internal/generator/player_leaderboards.go ││ index bcd6b1c..ce0d26b 100644 ││ --- a/nix/pkgs/ookstats/src/internal/generator/player_leaderboards.go ││ +++ b/nix/pkgs/ookstats/src/internal/generator/player_leaderboards.go ││ @@ -172,7 +172,13 @@ func generateRegionalPlayerLeaderboard(db *sql.DB, out, regio ││ n string, pageSize ││ ││ // generateRealmPlayerLeaderboards generates realm-specific player rankings for a ││ season ││ func generateRealmPlayerLeaderboards(db *sql.DB, out, region string, pageSize int ││ , seasonID int) error { ││ - rrows, err := db.Query(`SELECT slug FROM realms WHERE region = ? ORDER BY slug`, ││ region) ││ + // Only generate leaderboards for parent/independent realms (skip child realms) ││ + rrows, err := db.Query(` ││ + SELECT slug ││ + FROM realms ││ + WHERE region = ? AND (parent_realm_slug IS NULL OR parent_realm_slug = '') ││ + ORDER BY slug ││ + `, region) ││ if err != nil { ││ return fmt.Errorf("players realms list: %w", err) ││ } ││ @@ -193,14 +199,17 @@ func generateRealmPlayerLeaderboards(db *sql.DB, out, region ││ string, pageSize in ││ return err ││ } ││ ││ + // Include players from entire pool (parent + all children) ││ var total int ││ if err := db.QueryRow(` ││ SELECT COUNT(*) ││ FROM players p ││ JOIN realms r ON p.realm_id = r.id ││ JOIN player_profiles pp ON p.id = pp.player_id ││ - WHERE pp.season_id = ? AND r.region = ? AND r.slug = ? AND pp.has_complete_cov ││ erage = 1 AND pp.combined_best_time IS NOT NULL ││ - `, seasonID, region, rslug).Scan(&total); err != nil { ││ + WHERE pp.season_id = ? AND r.region = ? ││ + AND (r.slug = ? OR r.parent_realm_slug = ?) ││ + AND pp.has_complete_coverage = 1 AND pp.combined_best_time IS NOT NULL ││ + `, seasonID, region, rslug, rslug).Scan(&total); err != nil { ││ return fmt.Errorf("players total (realm, season %d): %w", seasonID, err) ││ } ││ ││ @@ -216,10 +225,12 @@ func generateRealmPlayerLeaderboards(db *sql.DB, out, region ││ string, pageSize in ││ 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 ││ - WHERE pp.season_id = ? AND r.region = ? AND r.slug = ? AND pp.has_complete_co ││ verage = 1 AND pp.combined_best_time IS NOT NULL ││ + WHERE pp.season_id = ? AND r.region = ? ││ + AND (r.slug = ? OR r.parent_realm_slug = ?) ││ + AND pp.has_complete_coverage = 1 AND pp.combined_best_time IS NOT NULL ││ ORDER BY pp.combined_best_time ASC, p.name ASC ││ LIMIT ? OFFSET ? ││ - `, seasonID, region, rslug, pageSize, offset) ││ + `, seasonID, region, rslug, rslug, pageSize, offset) ││ if err != nil { ││ return err ││ } ││ @@ -252,8 +263,13 @@ func generateClassPlayerLeaderboards(db *sql.DB, out, classKe ││ y string, pageSize ││ return err ││ } ││ ││ - // Realm class leaderboards ││ - rrows, err := db.Query(`SELECT slug FROM realms WHERE region = ? ORDER BY slug` ││ , reg) ││ + // Realm class leaderboards - only for parent/independent realms (skip child re ││ alms) ││ + 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("players class realms list: %w", err) ││ } ││ @@ -325,6 +341,7 @@ func generateClassScope(db *sql.DB, out, scope, region, realmS ││ lug, classKey stri ││ ORDER BY pp.combined_best_time ASC, p.name ASC ││ `, seasonID, region) ││ } else { ││ + // Realm scope - include entire pool (parent + all children) ││ bracketColumn = "pp.realm_ranking_bracket" ││ rows, err = db.Query(` ││ SELECT p.id, p.name, r.slug, r.name, r.region, ││ @@ -335,9 +352,11 @@ func generateClassScope(db *sql.DB, out, scope, region, realm ││ Slug, classKey stri ││ 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 ││ - WHERE pp.season_id = ? AND r.region = ? AND r.slug = ? AND pp.has_complete_cov ││ erage = 1 AND pp.combined_best_time IS NOT NULL ││ + WHERE pp.season_id = ? AND r.region = ? ││ + AND (r.slug = ? OR r.parent_realm_slug = ?) ││ + AND pp.has_complete_coverage = 1 AND pp.combined_best_time IS NOT NULL ││ ORDER BY pp.combined_best_time ASC, p.name ASC ││ - `, seasonID, region, realmSlug) ││ + `, seasonID, region, realmSlug, realmSlug) ││ } ││ _ = bracketColumn // unused for now, but documents intent ││ if err != nil { │└────────────────────────────────────────────────────────────────────────────────────┘
┌─ ...erator/player_leaderboards.go ───┐│ diff --git a/nix/pkgs/ookstats/src/internal/ ││ generator/player_leaderboards.go b/nix/pkgs/ ││ ookstats/src/internal/generator/player_leade ││ rboards.go ││ index bcd6b1c..ce0d26b 100644 ││ --- a/nix/pkgs/ookstats/src/internal/generat ││ or/player_leaderboards.go ││ +++ b/nix/pkgs/ookstats/src/internal/generat ││ or/player_leaderboards.go ││ @@ -172,7 +172,13 @@ func generateRegionalPl ││ ayerLeaderboard(db *sql.DB, out, region stri ││ ng, pageSize ││ ││ // generateRealmPlayerLeaderboards generate ││ s realm-specific player rankings for a seaso ││ n ││ func generateRealmPlayerLeaderboards(db *sq ││ l.DB, out, region string, pageSize int, seas ││ onID int) error { ││ - rrows, err := db.Query(`SELECT slug FROM r ││ ealms WHERE region = ? ORDER BY slug`, regio ││ n) ││ + // Only generate leaderboards for parent/i ││ ndependent realms (skip child realms) ││ + rrows, err := db.Query(` ││ + SELECT slug ││ + FROM realms ││ + WHERE region = ? AND (parent_realm_slug I ││ S NULL OR parent_realm_slug = '') ││ + ORDER BY slug ││ + `, region) ││ if err != nil { ││ return fmt.Errorf("players realms list: % ││ w", err) ││ } ││ @@ -193,14 +199,17 @@ func generateRealmPlay ││ erLeaderboards(db *sql.DB, out, region strin ││ g, pageSize in ││ return err ││ } ││ ││ + // Include players from entire pool (pare ││ nt + all children) ││ var total int ││ if err := db.QueryRow(` ││ SELECT COUNT(*) ││ FROM players p ││ JOIN realms r ON p.realm_id = r.id ││ JOIN player_profiles pp ON p.id = pp.pla ││ yer_id ││ - WHERE pp.season_id = ? AND r.region = ? ││ AND r.slug = ? AND pp.has_complete_coverage ││ = 1 AND pp.combined_best_time IS NOT NULL ││ - `, seasonID, region, rslug).Scan(&total); ││ err != nil { ││ + WHERE pp.season_id = ? AND r.region = ? ││ + AND (r.slug = ? OR r.parent_realm_slug ││ = ?) ││ + AND pp.has_complete_coverage = 1 AND pp ││ .combined_best_time IS NOT NULL ││ + `, seasonID, region, rslug, rslug).Scan(& ││ total); err != nil { ││ return fmt.Errorf("players total (realm, ││ season %d): %w", seasonID, err) ││ } ││ ││ @@ -216,10 +225,12 @@ func generateRealmPlay ││ erLeaderboards(db *sql.DB, out, region strin ││ g, pageSize in ││ JOIN realms r ON p.realm_id = r.id ││ JOIN player_profiles pp ON p.id = pp.pl ││ ayer_id ││ LEFT JOIN player_details pd ON p.id = p ││ d.player_id ││ - WHERE pp.season_id = ? AND r.region = ? ││ AND r.slug = ? AND pp.has_complete_coverage ││ = 1 AND pp.combined_best_time IS NOT NULL ││ + WHERE pp.season_id = ? AND r.region = ? ││ + AND (r.slug = ? OR r.parent_realm_slug ││ = ?) ││ + AND pp.has_complete_coverage = 1 AND p ││ p.combined_best_time IS NOT NULL ││ ORDER BY pp.combined_best_time ASC, p.n ││ ame ASC ││ LIMIT ? OFFSET ? ││ - `, seasonID, region, rslug, pageSize, of ││ fset) ││ + `, seasonID, region, rslug, rslug, pageS ││ ize, offset) ││ if err != nil { ││ return err ││ } ││ @@ -252,8 +263,13 @@ func generateClassPlaye ││ rLeaderboards(db *sql.DB, out, classKey stri ││ ng, pageSize ││ return err ││ } ││ ││ - // Realm class leaderboards ││ - rrows, err := db.Query(`SELECT slug FROM ││ realms WHERE region = ? ORDER BY slug`, reg) ││ + // Realm class leaderboards - only for pa ││ rent/independent realms (skip child realms) ││ + 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("players class realms ││ list: %w", err) ││ } ││ @@ -325,6 +341,7 @@ func generateClassScope( ││ db *sql.DB, out, scope, region, realmSlug, c ││ lassKey stri ││ ORDER BY pp.combined_best_time ASC, p.na ││ me ASC ││ `, seasonID, region) ││ } else { ││ + // Realm scope - include entire pool (par ││ ent + all children) ││ bracketColumn = "pp.realm_ranking_bracket ││ " ││ rows, err = db.Query(` ││ SELECT p.id, p.name, r.slug, r.name, r.r ││ egion, ││ @@ -335,9 +352,11 @@ func generateClassScope ││ (db *sql.DB, out, scope, region, realmSlug, ││ classKey stri ││ JOIN realms r ON p.realm_id = r.id ││ JOIN player_profiles pp ON p.id = pp.pla ││ yer_id ││ LEFT JOIN player_details pd ON p.id = pd ││ .player_id ││ - WHERE pp.season_id = ? AND r.region = ? ││ AND r.slug = ? AND pp.has_complete_coverage ││ = 1 AND pp.combined_best_time IS NOT NULL ││ + WHERE pp.season_id = ? AND r.region = ? ││ + AND (r.slug = ? OR r.parent_realm_slug ││ = ?) ││ + AND pp.has_complete_coverage = 1 AND pp ││ .combined_best_time IS NOT NULL ││ ORDER BY pp.combined_best_time ASC, p.na ││ me ASC ││ - `, seasonID, region, realmSlug) ││ + `, seasonID, region, realmSlug, realmSlug ││ ) ││ } ││ _ = bracketColumn // unused for now, but d ││ ocuments intent ││ if err != nil { │└──────────────────────────────────────────────┘
┌─ nix/pkgs/ookstats/src/internal/pipeline/fetch.go ─────────────────────────┐│ diff --git a/nix/pkgs/ookstats/src/internal/pipeline/fetch.go b/nix/pkgs/ookstats/ ││ src/internal/pipeline/fetch.go ││ index ec7568f..f6aa51c 100644 ││ --- a/nix/pkgs/ookstats/src/internal/pipeline/fetch.go ││ +++ b/nix/pkgs/ookstats/src/internal/pipeline/fetch.go ││ @@ -84,23 +84,39 @@ func FetchChallengeMode(db *database.DatabaseService, client * ││ blizzard.Client, o ││ } ││ } ││ ││ - // Pre-populate reference data ││ + // Pre-populate reference data (includes ALL realms, even children) ││ fmt.Printf("Pre-populating reference data...\n") ││ fmt.Printf(" - Ensuring dungeons (%d)\n", len(dungeons)) ││ if err := db.EnsureDungeonsOnce(dungeons); err != nil { ││ return nil, fmt.Errorf("failed to ensure dungeons: %w", err) ││ } ││ fmt.Printf(" [OK] Dungeons ensured\n") ││ - fmt.Printf(" - Ensuring realms (%d)\n", len(allRealms)) ││ + fmt.Printf(" - Ensuring realms (%d, including child realms)\n", len(allRealms)) ││ if err := db.EnsureRealmsBatch(allRealms); err != nil { ││ return nil, fmt.Errorf("failed to ensure realms: %w", err) ││ } ││ fmt.Printf(" [OK] Realms ensured\n") ││ - fmt.Printf("Reference data populated for %d realms and %d dungeons\n", len(allRe ││ alms), len(dungeons)) ││ ││ - // Group realms by region ││ - realmsByRegion := make(map[string]map[string]blizzard.RealmInfo) ││ + // Filter out child realms for fetching (they don't have their own leaderboards) ││ + // Players from child realms appear on parent realm leaderboards ││ + fetchRealms := make(map[string]blizzard.RealmInfo) ││ + childRealmsFiltered := 0 ││ for slug, info := range allRealms { ││ + if info.ParentRealmSlug != "" { ││ + childRealmsFiltered++ ││ + } else { ││ + fetchRealms[slug] = info ││ + } ││ + } ││ + if childRealmsFiltered > 0 { ││ + fmt.Printf("Filtered out %d child realms from fetch (no leaderboards to fetch)\ ││ n", childRealmsFiltered) ││ + } ││ + fmt.Printf("Reference data populated for %d realms (%d to fetch from) and %d dun ││ geons\n", ││ + len(allRealms), len(fetchRealms), len(dungeons)) ││ + ││ + // Group realms by region (only fetch realms, excluding children) ││ + realmsByRegion := make(map[string]map[string]blizzard.RealmInfo) ││ + for slug, info := range fetchRealms { ││ if realmsByRegion[info.Region] == nil { ││ realmsByRegion[info.Region] = make(map[string]blizzard.RealmInfo) ││ } │└────────────────────────────────────────────────────────────────────────────────────┘
┌─ ...rc/internal/pipeline/fetch.go ───┐│ diff --git a/nix/pkgs/ookstats/src/internal/ ││ pipeline/fetch.go b/nix/pkgs/ookstats/src/in ││ ternal/pipeline/fetch.go ││ index ec7568f..f6aa51c 100644 ││ --- a/nix/pkgs/ookstats/src/internal/pipelin ││ e/fetch.go ││ +++ b/nix/pkgs/ookstats/src/internal/pipelin ││ e/fetch.go ││ @@ -84,23 +84,39 @@ func FetchChallengeMode( ││ db *database.DatabaseService, client *blizza ││ rd.Client, o ││ } ││ } ││ ││ - // Pre-populate reference data ││ + // Pre-populate reference data (includes A ││ LL realms, even children) ││ fmt.Printf("Pre-populating reference data. ││ ..\n") ││ fmt.Printf(" - Ensuring dungeons (%d)\n", ││ len(dungeons)) ││ if err := db.EnsureDungeonsOnce(dungeons); ││ err != nil { ││ return nil, fmt.Errorf("failed to ensure ││ dungeons: %w", err) ││ } ││ fmt.Printf(" [OK] Dungeons ensured\n") ││ - fmt.Printf(" - Ensuring realms (%d)\n", l ││ en(allRealms)) ││ + fmt.Printf(" - Ensuring realms (%d, inclu ││ ding child realms)\n", len(allRealms)) ││ if err := db.EnsureRealmsBatch(allRealms); ││ err != nil { ││ return nil, fmt.Errorf("failed to ensure ││ realms: %w", err) ││ } ││ fmt.Printf(" [OK] Realms ensured\n") ││ - fmt.Printf("Reference data populated for % ││ d realms and %d dungeons\n", len(allRealms), ││ len(dungeons)) ││ ││ - // Group realms by region ││ - realmsByRegion := make(map[string]map[stri ││ ng]blizzard.RealmInfo) ││ + // Filter out child realms for fetching (t ││ hey don't have their own leaderboards) ││ + // Players from child realms appear on par ││ ent realm leaderboards ││ + fetchRealms := make(map[string]blizzard.Re ││ almInfo) ││ + childRealmsFiltered := 0 ││ for slug, info := range allRealms { ││ + if info.ParentRealmSlug != "" { ││ + childRealmsFiltered++ ││ + } else { ││ + fetchRealms[slug] = info ││ + } ││ + } ││ + if childRealmsFiltered > 0 { ││ + fmt.Printf("Filtered out %d child realms ││ from fetch (no leaderboards to fetch)\n", ch ││ ildRealmsFiltered) ││ + } ││ + fmt.Printf("Reference data populated for % ││ d realms (%d to fetch from) and %d dungeons\ ││ n", ││ + len(allRealms), len(fetchRealms), len(dun ││ geons)) ││ + ││ + // Group realms by region (only fetch real ││ ms, excluding children) ││ + realmsByRegion := make(map[string]map[stri ││ ng]blizzard.RealmInfo) ││ + for slug, info := range fetchRealms { ││ if realmsByRegion[info.Region] == nil { ││ realmsByRegion[info.Region] = make(map[s ││ tring]blizzard.RealmInfo) ││ } │└──────────────────────────────────────────────┘
┌─ nix/pkgs/ookstats/src/internal/pipeline/process.go ───────────────────────┐│ diff --git a/nix/pkgs/ookstats/src/internal/pipeline/process.go b/nix/pkgs/ookstat ││ s/src/internal/pipeline/process.go ││ index 544e5d7..2e503b6 100644 ││ --- a/nix/pkgs/ookstats/src/internal/pipeline/process.go ││ +++ b/nix/pkgs/ookstats/src/internal/pipeline/process.go ││ @@ -445,17 +445,22 @@ func computePlayerRankings(tx *sql.Tx) (int, error) { ││ return 0, err ││ } ││ ││ - // step 3: realm rankings for this season ││ - fmt.Printf("Computing realm rankings for season %d...\n", seasonID) ││ + // step 3: realm rankings for this season (pool-based for connected realms) ││ + fmt.Printf("Computing realm rankings for season %d (using realm pools)...\n", s ││ easonID) ││ _, err = tx.Exec(` ││ UPDATE player_profiles ││ SET realm_ranking = ( ││ SELECT ranking FROM ( ││ SELECT ││ - player_id, ││ - ROW_NUMBER() OVER (PARTITION BY realm_id ORDER BY combined_best_time ASC) a ││ s ranking ││ - FROM player_profiles ││ - WHERE season_id = ? AND has_complete_coverage = 1 ││ + pp.player_id, ││ + ROW_NUMBER() OVER ( ││ + PARTITION BY COALESCE(parent_r.id, r.id) ││ + ORDER BY pp.combined_best_time ASC ││ + ) as ranking ││ + FROM player_profiles pp ││ + JOIN realms r ON pp.realm_id = r.id ││ + LEFT JOIN realms parent_r ON r.parent_realm_slug = parent_r.slug AND r.regio ││ n = parent_r.region ││ + WHERE pp.season_id = ? AND pp.has_complete_coverage = 1 ││ ) realm_ranks ││ WHERE realm_ranks.player_id = player_profiles.player_id ││ ) ││ @@ -476,34 +481,36 @@ func computePlayerRankings(tx *sql.Tx) (int, error) { ││ return 0, err ││ } ││ ││ - // update realm ranking brackets for this season ││ - fmt.Printf("Computing realm ranking brackets for season %d...\n", seasonID) ││ + // update realm ranking brackets for this season (pool-based for connected real ││ ms) ││ + fmt.Printf("Computing realm ranking brackets for season %d (using realm pools). ││ ..\n", seasonID) ││ _, err = tx.Exec(` ││ UPDATE player_profiles ││ SET realm_ranking_bracket = ( ││ CASE ││ - WHEN counts.combined_best_time = counts.realm_min_time THEN 'artifact' ││ + WHEN counts.combined_best_time = counts.pool_min_time THEN 'artifact' ││ ELSE ││ CASE ││ - WHEN (CAST(counts.realm_ranking AS REAL) / CAST(counts.realm_total AS REAL ││ ) * 100) <= 1.0 THEN 'excellent' ││ - WHEN (CAST(counts.realm_ranking AS REAL) / CAST(counts.realm_total AS REAL ││ ) * 100) <= 5.0 THEN 'legendary' ││ - WHEN (CAST(counts.realm_ranking AS REAL) / CAST(counts.realm_total AS REAL ││ ) * 100) <= 20.0 THEN 'epic' ││ - WHEN (CAST(counts.realm_ranking AS REAL) / CAST(counts.realm_total AS REAL ││ ) * 100) <= 40.0 THEN 'rare' ││ - WHEN (CAST(counts.realm_ranking AS REAL) / CAST(counts.realm_total AS REAL ││ ) * 100) <= 60.0 THEN 'uncommon' ││ + WHEN (CAST(counts.realm_ranking AS REAL) / CAST(counts.pool_total AS REAL) ││ * 100) <= 1.0 THEN 'excellent' ││ + WHEN (CAST(counts.realm_ranking AS REAL) / CAST(counts.pool_total AS REAL) ││ * 100) <= 5.0 THEN 'legendary' ││ + WHEN (CAST(counts.realm_ranking AS REAL) / CAST(counts.pool_total AS REAL) ││ * 100) <= 20.0 THEN 'epic' ││ + WHEN (CAST(counts.realm_ranking AS REAL) / CAST(counts.pool_total AS REAL) ││ * 100) <= 40.0 THEN 'rare' ││ + WHEN (CAST(counts.realm_ranking AS REAL) / CAST(counts.pool_total AS REAL) ││ * 100) <= 60.0 THEN 'uncommon' ││ ELSE 'common' ││ END ││ END ││ ) ││ FROM ( ││ SELECT ││ - player_id, ││ - realm_ranking, ││ - combined_best_time, ││ - realm_id, ││ - MIN(combined_best_time) OVER (PARTITION BY realm_id) as realm_min_time, ││ - COUNT(*) OVER (PARTITION BY realm_id) as realm_total ││ - FROM player_profiles ││ - WHERE season_id = ? AND has_complete_coverage = 1 AND realm_ranking IS NOT NU ││ LL ││ + pp.player_id, ││ + pp.realm_ranking, ││ + pp.combined_best_time, ││ + COALESCE(parent_r.id, r.id) as pool_id, ││ + MIN(pp.combined_best_time) OVER (PARTITION BY COALESCE(parent_r.id, r.id)) a ││ s pool_min_time, ││ + COUNT(*) OVER (PARTITION BY COALESCE(parent_r.id, r.id)) as pool_total ││ + FROM player_profiles pp ││ + JOIN realms r ON pp.realm_id = r.id ││ + LEFT JOIN realms parent_r ON r.parent_realm_slug = parent_r.slug AND r.region ││ = parent_r.region ││ + WHERE pp.season_id = ? AND pp.has_complete_coverage = 1 AND pp.realm_ranking ││ IS NOT NULL ││ ) counts ││ WHERE player_profiles.player_id = counts.player_id ││ AND player_profiles.season_id = ? │└────────────────────────────────────────────────────────────────────────────────────┘
┌─ .../internal/pipeline/process.go ───┐│ diff --git a/nix/pkgs/ookstats/src/internal/ ││ pipeline/process.go b/nix/pkgs/ookstats/src/ ││ internal/pipeline/process.go ││ index 544e5d7..2e503b6 100644 ││ --- a/nix/pkgs/ookstats/src/internal/pipelin ││ e/process.go ││ +++ b/nix/pkgs/ookstats/src/internal/pipelin ││ e/process.go ││ @@ -445,17 +445,22 @@ func computePlayerRank ││ ings(tx *sql.Tx) (int, error) { ││ return 0, err ││ } ││ ││ - // step 3: realm rankings for this season ││ - fmt.Printf("Computing realm rankings for ││ season %d...\n", seasonID) ││ + // step 3: realm rankings for this season ││ (pool-based for connected realms) ││ + fmt.Printf("Computing realm rankings for ││ season %d (using realm pools)...\n", seasonI ││ D) ││ _, err = tx.Exec(` ││ UPDATE player_profiles ││ SET realm_ranking = ( ││ SELECT ranking FROM ( ││ SELECT ││ - player_id, ││ - ROW_NUMBER() OVER (PARTITION BY realm ││ _id ORDER BY combined_best_time ASC) as rank ││ ing ││ - FROM player_profiles ││ - WHERE season_id = ? AND has_complete_c ││ overage = 1 ││ + pp.player_id, ││ + ROW_NUMBER() OVER ( ││ + PARTITION BY COALESCE(parent_r.id, r ││ .id) ││ + ORDER BY pp.combined_best_time ASC ││ + ) as ranking ││ + FROM player_profiles pp ││ + JOIN realms r ON pp.realm_id = r.id ││ + LEFT JOIN realms parent_r ON r.parent_ ││ realm_slug = parent_r.slug AND r.region = pa ││ rent_r.region ││ + WHERE pp.season_id = ? AND pp.has_comp ││ lete_coverage = 1 ││ ) realm_ranks ││ WHERE realm_ranks.player_id = player_pr ││ ofiles.player_id ││ ) ││ @@ -476,34 +481,36 @@ func computePlayerRank ││ ings(tx *sql.Tx) (int, error) { ││ return 0, err ││ } ││ ││ - // update realm ranking brackets for this ││ season ││ - fmt.Printf("Computing realm ranking brack ││ ets for season %d...\n", seasonID) ││ + // update realm ranking brackets for this ││ season (pool-based for connected realms) ││ + fmt.Printf("Computing realm ranking brack ││ ets for season %d (using realm pools)...\n", ││ seasonID) ││ _, err = tx.Exec(` ││ UPDATE player_profiles ││ SET realm_ranking_bracket = ( ││ CASE ││ - WHEN counts.combined_best_time = count ││ s.realm_min_time THEN 'artifact' ││ + WHEN counts.combined_best_time = count ││ s.pool_min_time THEN 'artifact' ││ ELSE ││ CASE ││ - WHEN (CAST(counts.realm_ranking AS R ││ EAL) / CAST(counts.realm_total AS REAL) * 10 ││ 0) <= 1.0 THEN 'excellent' ││ - WHEN (CAST(counts.realm_ranking AS R ││ EAL) / CAST(counts.realm_total AS REAL) * 10 ││ 0) <= 5.0 THEN 'legendary' ││ - WHEN (CAST(counts.realm_ranking AS R ││ EAL) / CAST(counts.realm_total AS REAL) * 10 ││ 0) <= 20.0 THEN 'epic' ││ - WHEN (CAST(counts.realm_ranking AS R ││ EAL) / CAST(counts.realm_total AS REAL) * 10 ││ 0) <= 40.0 THEN 'rare' ││ - WHEN (CAST(counts.realm_ranking AS R ││ EAL) / CAST(counts.realm_total AS REAL) * 10 ││ 0) <= 60.0 THEN 'uncommon' ││ + WHEN (CAST(counts.realm_ranking AS R ││ EAL) / CAST(counts.pool_total AS REAL) * 100 ││ ) <= 1.0 THEN 'excellent' ││ + WHEN (CAST(counts.realm_ranking AS R ││ EAL) / CAST(counts.pool_total AS REAL) * 100 ││ ) <= 5.0 THEN 'legendary' ││ + WHEN (CAST(counts.realm_ranking AS R ││ EAL) / CAST(counts.pool_total AS REAL) * 100 ││ ) <= 20.0 THEN 'epic' ││ + WHEN (CAST(counts.realm_ranking AS R ││ EAL) / CAST(counts.pool_total AS REAL) * 100 ││ ) <= 40.0 THEN 'rare' ││ + WHEN (CAST(counts.realm_ranking AS R ││ EAL) / CAST(counts.pool_total AS REAL) * 100 ││ ) <= 60.0 THEN 'uncommon' ││ ELSE 'common' ││ END ││ END ││ ) ││ FROM ( ││ SELECT ││ - player_id, ││ - realm_ranking, ││ - combined_best_time, ││ - realm_id, ││ - MIN(combined_best_time) OVER (PARTITIO ││ N BY realm_id) as realm_min_time, ││ - COUNT(*) OVER (PARTITION BY realm_id) ││ as realm_total ││ - FROM player_profiles ││ - WHERE season_id = ? AND has_complete_co ││ verage = 1 AND realm_ranking IS NOT NULL ││ + pp.player_id, ││ + pp.realm_ranking, ││ + pp.combined_best_time, ││ + COALESCE(parent_r.id, r.id) as pool_id ││ , ││ + MIN(pp.combined_best_time) OVER (PARTI ││ TION BY COALESCE(parent_r.id, r.id)) as pool ││ _min_time, ││ + COUNT(*) OVER (PARTITION BY COALESCE(p ││ arent_r.id, r.id)) as pool_total ││ + FROM player_profiles pp ││ + JOIN realms r ON pp.realm_id = r.id ││ + LEFT JOIN realms parent_r ON r.parent_r ││ ealm_slug = parent_r.slug AND r.region = par ││ ent_r.region ││ + WHERE pp.season_id = ? AND pp.has_compl ││ ete_coverage = 1 AND pp.realm_ranking IS NOT ││ NULL ││ ) counts ││ WHERE player_profiles.player_id = counts ││ .player_id ││ AND player_profiles.season_id = ? │└──────────────────────────────────────────────┘