From 02d2ffd029ec7ea13ab229c85bb0b0803e2b4044 Mon Sep 17 00:00:00 2001 From: Austin Lee Date: Wed, 31 Dec 2025 21:43:12 +0900 Subject: [PATCH 1/9] Change total analysis file path --- cmd/total_analysis/main.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/total_analysis/main.go b/cmd/total_analysis/main.go index f447ae1..80ddc73 100644 --- a/cmd/total_analysis/main.go +++ b/cmd/total_analysis/main.go @@ -60,8 +60,8 @@ func main() { // Upload result err = logic_upload.MarshalAndUpload( result, - "batorment/v3/total-analysis", - "analysis.json", + "batorment/v3", + "total-analysis.json", *dryRun, "Total analysis completed", ) From 6bb027dcac208b9a0cb1124a8c032c142434c34e Mon Sep 17 00:00:00 2001 From: Austin Lee Date: Wed, 31 Dec 2025 23:37:49 +0900 Subject: [PATCH 2/9] Add ranking to total analysis --- internal/logic/analysis/analysis.go | 48 +++++++++++++++++++++++++++++ internal/types/analysis_types.go | 3 ++ 2 files changed, 51 insertions(+) diff --git a/internal/logic/analysis/analysis.go b/internal/logic/analysis/analysis.go index 98046b6..15f779f 100644 --- a/internal/logic/analysis/analysis.go +++ b/internal/logic/analysis/analysis.go @@ -5,6 +5,7 @@ import ( "io" "log" "net/http" + "sort" "time" "ba-torment-data-process/internal/types" @@ -83,9 +84,56 @@ func RunTotalAnalysis(partyDataMap map[string]*types.BATormentPartyData, sortedC characterAnalyses := RunCharacterAnalyses(partyDataMap, sortedContentIDs) log.Printf("Completed character analyses: %d characters", len(characterAnalyses)) + // Calculate and assign overall rankings + log.Println("Calculating overall rankings...") + AssignOverallRankings(characterAnalyses) + log.Println("Completed overall ranking calculation") + return &types.TotalAnalysisOutput{ GeneratedAt: time.Now().Format(time.RFC3339), RaidAnalyses: raidAnalyses, CharacterAnalyses: characterAnalyses, } } + +// AssignOverallRankings calculates and assigns overall usage rankings to characterAnalyses +func AssignOverallRankings(characterAnalyses []types.CharacterAnalysisResult) { + // Calculate total usage for each character + for i := range characterAnalyses { + totalUsage := 0 + for _, ru := range characterAnalyses[i].UsageHistory { + totalUsage += ru.UserCount + } + characterAnalyses[i].TotalUsage = totalUsage + } + + // Create index slice for sorting + indices := make([]int, len(characterAnalyses)) + for i := range indices { + indices[i] = i + } + + // Sort indices by total usage (descending) + sort.Slice(indices, func(i, j int) bool { + return characterAnalyses[indices[i]].TotalUsage > characterAnalyses[indices[j]].TotalUsage + }) + + // Assign overall ranks based on sorted order + for rank, idx := range indices { + characterAnalyses[idx].OverallRank = rank + 1 + } + + // Calculate category ranks (striker: 1xxxx, special: 2xxxx) + strikerRank := 1 + specialRank := 1 + for _, idx := range indices { + studentID := characterAnalyses[idx].StudentID + if studentID >= 10000 && studentID < 20000 { + characterAnalyses[idx].CategoryRank = strikerRank + strikerRank++ + } else if studentID >= 20000 && studentID < 30000 { + characterAnalyses[idx].CategoryRank = specialRank + specialRank++ + } + } +} diff --git a/internal/types/analysis_types.go b/internal/types/analysis_types.go index c08920a..35d3261 100644 --- a/internal/types/analysis_types.go +++ b/internal/types/analysis_types.go @@ -50,6 +50,9 @@ type CharacterAnalysisResult struct { StarDistribution []RaidStarDistribution `json:"starDistribution"` AssistStats AssistUsageStats `json:"assistStats"` TopSynergyChars []CharacterSynergy `json:"topSynergyChars"` + TotalUsage int `json:"totalUsage"` + OverallRank int `json:"overallRank"` + CategoryRank int `json:"categoryRank"` } // TotalAnalysisOutput represents the final output of total analysis From b5f1b2cba4163820f5dba8e2cdce1de407885dff Mon Sep 17 00:00:00 2001 From: Austin Lee Date: Thu, 1 Jan 2026 16:58:04 +0900 Subject: [PATCH 3/9] Group star distribution --- internal/logic/analysis/character_analysis.go | 35 +++++++++++++++---- internal/logic/analysis/helper.go | 9 +++++ 2 files changed, 37 insertions(+), 7 deletions(-) diff --git a/internal/logic/analysis/character_analysis.go b/internal/logic/analysis/character_analysis.go index 833205c..663e9a6 100644 --- a/internal/logic/analysis/character_analysis.go +++ b/internal/logic/analysis/character_analysis.go @@ -31,7 +31,11 @@ func RunCharacterAnalyses(partyDataMap map[string]*types.BATormentPartyData, sor // sortedContentIDs provides the order for usageHistory (sorted by start_date) func AnalyzeCharacter(studentID int, partyDataMap map[string]*types.BATormentPartyData, sortedContentIDs []string) types.CharacterAnalysisResult { var usageHistory []types.RaidUsage - var starDistribution []types.RaidStarDistribution + + // Group star distribution by groupID (e.g., "3S26-1", "3S26-3" -> "3S26") + groupStar := make(map[string]map[string]int) + groupAsOwn := make(map[string]int) + var groupOrder []string // Track order of first appearance totalAsAssist := 0 totalAsOwn := 0 @@ -47,19 +51,24 @@ func AnalyzeCharacter(studentID int, partyDataMap map[string]*types.BATormentPar } raidUsage, raidStar, asAssist, asOwn, coChars, appearances := analyzeCharacterInRaid(studentID, partyData) + // usageHistory: individual raid entries (no grouping) usageHistory = append(usageHistory, types.RaidUsage{ RaidID: raidID, UserCount: raidUsage.UserCount, LunaticUserCount: raidUsage.LunaticUserCount, }) - // Only include star distribution if own usage (excluding assist) >= 200 (1%) - if asOwn >= 200 { - starDistribution = append(starDistribution, types.RaidStarDistribution{ - RaidID: raidID, - Distribution: raidStar, - }) + // starDistribution: group by groupID + groupID := ExtractGroupID(raidID) + if _, exists := groupStar[groupID]; !exists { + groupStar[groupID] = make(map[string]int) + groupOrder = append(groupOrder, groupID) + } + + for key, count := range raidStar { + groupStar[groupID][key] += count } + groupAsOwn[groupID] += asOwn totalAsAssist += asAssist totalAsOwn += asOwn @@ -70,6 +79,18 @@ func AnalyzeCharacter(studentID int, partyDataMap map[string]*types.BATormentPar totalAppearances += appearances } + // Build starDistribution from grouped data + var starDistribution []types.RaidStarDistribution + for _, groupID := range groupOrder { + // Only include star distribution if own usage (excluding assist) >= 200 (1%) + if groupAsOwn[groupID] >= 200 { + starDistribution = append(starDistribution, types.RaidStarDistribution{ + RaidID: groupID, + Distribution: groupStar[groupID], + }) + } + } + // Calculate synergy (top 3, >= 5%) topSynergyChars := calculateTopSynergy(coUsageCount, totalAppearances, 3) diff --git a/internal/logic/analysis/helper.go b/internal/logic/analysis/helper.go index 6d9a88a..2ddeb67 100644 --- a/internal/logic/analysis/helper.go +++ b/internal/logic/analysis/helper.go @@ -2,12 +2,21 @@ package analysis import ( "sort" + "strings" "ba-torment-data-process/internal/types" ) const PlatinumRankLimit = 20000 +// ExtractGroupID extracts group ID from content_id (e.g., "3S26-1" -> "3S26") +func ExtractGroupID(contentID string) string { + if idx := strings.Index(contentID, "-"); idx != -1 { + return contentID[:idx] + } + return contentID +} + // ParseStudentDetailID extracts studentID, star, weaponStar, isAssist from detailID // Format: {studentID(5)}{star(1)}{weaponStar(1)}{isAssist(1)} func ParseStudentDetailID(detailID int) (studentID int, star int, weaponStar int, isAssist bool) { From 5a5171ddf4a5b1e744f71d3317d3d3ad00f462ed Mon Sep 17 00:00:00 2001 From: Austin Lee Date: Fri, 2 Jan 2026 13:49:49 +0900 Subject: [PATCH 4/9] Fix star distribution to use only most recent one --- internal/logic/analysis/character_analysis.go | 13 +++++++------ internal/types/analysis_types.go | 16 ++++++++-------- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/internal/logic/analysis/character_analysis.go b/internal/logic/analysis/character_analysis.go index 663e9a6..d28f595 100644 --- a/internal/logic/analysis/character_analysis.go +++ b/internal/logic/analysis/character_analysis.go @@ -79,15 +79,16 @@ func AnalyzeCharacter(studentID int, partyDataMap map[string]*types.BATormentPar totalAppearances += appearances } - // Build starDistribution from grouped data - var starDistribution []types.RaidStarDistribution - for _, groupID := range groupOrder { - // Only include star distribution if own usage (excluding assist) >= 200 (1%) + // Get latest star distribution (200+ own usage) + var starDistribution *types.RaidStarDistribution + for i := len(groupOrder) - 1; i >= 0; i-- { + groupID := groupOrder[i] if groupAsOwn[groupID] >= 200 { - starDistribution = append(starDistribution, types.RaidStarDistribution{ + starDistribution = &types.RaidStarDistribution{ RaidID: groupID, Distribution: groupStar[groupID], - }) + } + break } } diff --git a/internal/types/analysis_types.go b/internal/types/analysis_types.go index 35d3261..ea63fd5 100644 --- a/internal/types/analysis_types.go +++ b/internal/types/analysis_types.go @@ -45,14 +45,14 @@ type CharacterSynergy struct { // CharacterAnalysisResult represents analysis result for a single character type CharacterAnalysisResult struct { - StudentID int `json:"studentId"` - UsageHistory []RaidUsage `json:"usageHistory"` - StarDistribution []RaidStarDistribution `json:"starDistribution"` - AssistStats AssistUsageStats `json:"assistStats"` - TopSynergyChars []CharacterSynergy `json:"topSynergyChars"` - TotalUsage int `json:"totalUsage"` - OverallRank int `json:"overallRank"` - CategoryRank int `json:"categoryRank"` + StudentID int `json:"studentId"` + UsageHistory []RaidUsage `json:"usageHistory"` + StarDistribution *RaidStarDistribution `json:"starDistribution"` // Latest distribution (200+ usage), null if none + AssistStats AssistUsageStats `json:"assistStats"` + TopSynergyChars []CharacterSynergy `json:"topSynergyChars"` + TotalUsage int `json:"totalUsage"` + OverallRank int `json:"overallRank"` + CategoryRank int `json:"categoryRank"` } // TotalAnalysisOutput represents the final output of total analysis From 242af76dff768a6d19cee57fe1c3a2f466d2eee4 Mon Sep 17 00:00:00 2001 From: Austin Lee Date: Fri, 2 Jan 2026 17:53:22 +0900 Subject: [PATCH 5/9] Fix topSynergyChars logic --- internal/logic/analysis/character_analysis.go | 34 +++++++++++-------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/internal/logic/analysis/character_analysis.go b/internal/logic/analysis/character_analysis.go index d28f595..b20f720 100644 --- a/internal/logic/analysis/character_analysis.go +++ b/internal/logic/analysis/character_analysis.go @@ -134,22 +134,25 @@ func analyzeCharacterInRaid(studentID int, partyData *types.BATormentPartyData) } isLunatic := party.Score >= constants.LunaticMinScore - foundInParty := false - var partyMembers []int + foundInAnySquad := false - for _, members := range party.PartyData { - for _, member := range members { + for _, squad := range party.PartyData { + var squadMembers []int + foundInThisSquad := false + + for _, member := range squad { if member == 0 { continue } memberStudentID, star, weaponStar, isAssist := ParseStudentDetailID(member) - // Collect party members for synergy - partyMembers = append(partyMembers, memberStudentID) + // Collect squad members for synergy + squadMembers = append(squadMembers, memberStudentID) if memberStudentID == studentID { - foundInParty = true + foundInThisSquad = true + foundInAnySquad = true if isAssist { asAssist++ @@ -167,17 +170,20 @@ func analyzeCharacterInRaid(studentID int, partyData *types.BATormentPartyData) } } } - } - if foundInParty { - appearances++ - // Count co-usage characters - for _, memberID := range partyMembers { - if memberID != studentID { - coChars[memberID]++ + // Count co-usage only for squads where this character appears + if foundInThisSquad { + for _, memberID := range squadMembers { + if memberID != studentID { + coChars[memberID]++ + } } } } + + if foundInAnySquad { + appearances++ + } } usage = types.RaidUsage{ From d6b08e405a479a5b9a9fee42f85158f536ab97d3 Mon Sep 17 00:00:00 2001 From: Austin Lee Date: Fri, 2 Jan 2026 21:48:45 +0900 Subject: [PATCH 6/9] Fix schaledb parsing type --- internal/types/schaledb_students.go | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/internal/types/schaledb_students.go b/internal/types/schaledb_students.go index 2bcb447..94272ee 100644 --- a/internal/types/schaledb_students.go +++ b/internal/types/schaledb_students.go @@ -87,24 +87,29 @@ type StudentSkill struct { // Skill effect type SkillEffect struct { Type string `json:"Type"` + Restrictions []SkillRestriction `json:"Restrictions,omitempty"` Target []string `json:"Target,omitempty"` Scale []int `json:"Scale,omitempty"` Value [][]int `json:"Value,omitempty"` AdditionalValue any `json:"AdditionalValue,omitempty"` - Channel int `json:"Channel,omitempty"` Stat string `json:"Stat,omitempty"` Duration int `json:"Duration,omitempty"` Period int `json:"Period,omitempty"` - ApplyFrame int `json:"ApplyFrame,omitempty"` Block int `json:"Block,omitempty"` CriticalCheck string `json:"CriticalCheck,omitempty"` - DescParamId int `json:"DescParamId,omitempty"` Hits []int `json:"Hits,omitempty"` ExtraStatRate []int `json:"ExtraStatRate,omitempty"` ExtraStatSource string `json:"ExtraStatSource,omitempty"` TargetHpRateModifier *TargetHpRateModifier `json:"TargetHpRateModifier,omitempty"` } +// Skill restriction (e.g., BulletType == Pierce) +type SkillRestriction struct { + Property string `json:"Property"` + Operand string `json:"Operand"` + Value string `json:"Value"` +} + // HP-based damage modifier type TargetHpRateModifier struct { MaxHpRate int `json:"MaxHpRate"` From 8c564fbfbfd191742bab0013e1aca61193fcf065 Mon Sep 17 00:00:00 2001 From: Austin Lee Date: Sat, 3 Jan 2026 02:07:50 +0900 Subject: [PATCH 7/9] Add i18n parsing from Schale DB --- cmd/update_from_schaledb/main.go | 7 +- internal/db/postgres/i18n.sql.go | 95 +++++++++++++++++++ internal/db/postgres/models.go | 10 ++ internal/db/postgres/querier.go | 3 + internal/db/postgres/sql/query/i18n.sql | 14 +++ .../db/postgres/sql/schema/schema_251007.sql | 13 +++ internal/logic/schaledb/i18n.go | 68 +++++++++++++ 7 files changed, 209 insertions(+), 1 deletion(-) create mode 100644 internal/db/postgres/i18n.sql.go create mode 100644 internal/db/postgres/sql/query/i18n.sql create mode 100644 internal/logic/schaledb/i18n.go diff --git a/cmd/update_from_schaledb/main.go b/cmd/update_from_schaledb/main.go index bb6a691..1b8cd03 100644 --- a/cmd/update_from_schaledb/main.go +++ b/cmd/update_from_schaledb/main.go @@ -31,5 +31,10 @@ func main() { log.Fatalf("Failed to parse SchaleDB presents: %v", err) } - log.Println("Successfully parsed SchaleDB students") + err = schaledb.SaveI18nData(queries) + if err != nil { + log.Fatalf("Failed to save i18n data: %v", err) + } + + log.Println("Successfully updated from SchaleDB") } diff --git a/internal/db/postgres/i18n.sql.go b/internal/db/postgres/i18n.sql.go new file mode 100644 index 0000000..da9c41b --- /dev/null +++ b/internal/db/postgres/i18n.sql.go @@ -0,0 +1,95 @@ +// Code generated by sqlc. DO NOT EDIT. +// versions: +// sqlc v1.30.0 +// source: i18n.sql + +package postgres + +import ( + "context" +) + +const getI18n = `-- name: GetI18n :one +SELECT category, key, name_ko, name_ja, name_en, created_at, updated_at FROM batorment_v3.i18n WHERE category = $1 AND key = $2 +` + +type GetI18nParams struct { + Category string `json:"category"` + Key string `json:"key"` +} + +func (q *Queries) GetI18n(ctx context.Context, arg GetI18nParams) (BatormentV3I18n, error) { + row := q.db.QueryRow(ctx, getI18n, arg.Category, arg.Key) + var i BatormentV3I18n + err := row.Scan( + &i.Category, + &i.Key, + &i.NameKo, + &i.NameJa, + &i.NameEn, + &i.CreatedAt, + &i.UpdatedAt, + ) + return i, err +} + +const getI18nByCategory = `-- name: GetI18nByCategory :many +SELECT category, key, name_ko, name_ja, name_en, created_at, updated_at FROM batorment_v3.i18n WHERE category = $1 +` + +func (q *Queries) GetI18nByCategory(ctx context.Context, category string) ([]BatormentV3I18n, error) { + rows, err := q.db.Query(ctx, getI18nByCategory, category) + if err != nil { + return nil, err + } + defer rows.Close() + items := []BatormentV3I18n{} + for rows.Next() { + var i BatormentV3I18n + if err := rows.Scan( + &i.Category, + &i.Key, + &i.NameKo, + &i.NameJa, + &i.NameEn, + &i.CreatedAt, + &i.UpdatedAt, + ); err != nil { + return nil, err + } + items = append(items, i) + } + if err := rows.Err(); err != nil { + return nil, err + } + return items, nil +} + +const upsertI18n = `-- name: UpsertI18n :exec +INSERT INTO batorment_v3.i18n (category, key, name_ko, name_ja, name_en, created_at, updated_at) +VALUES ($1, $2, $3, $4, $5, NOW(), NOW()) +ON CONFLICT (category, key) DO UPDATE SET + name_ko = EXCLUDED.name_ko, + name_ja = EXCLUDED.name_ja, + name_en = EXCLUDED.name_en, + updated_at = NOW() +` + +type UpsertI18nParams struct { + Category string `json:"category"` + Key string `json:"key"` + NameKo string `json:"name_ko"` + NameJa string `json:"name_ja"` + NameEn string `json:"name_en"` +} + +func (q *Queries) UpsertI18n(ctx context.Context, arg UpsertI18nParams) error { + _, err := q.db.Exec(ctx, upsertI18n, + arg.Category, + arg.Key, + arg.NameKo, + arg.NameJa, + arg.NameEn, + ) + return err +} diff --git a/internal/db/postgres/models.go b/internal/db/postgres/models.go index 47fbc7e..46732a3 100644 --- a/internal/db/postgres/models.go +++ b/internal/db/postgres/models.go @@ -108,6 +108,16 @@ type BatormentV3Content struct { DeletedAt pgtype.Timestamptz `json:"deleted_at"` } +type BatormentV3I18n struct { + Category string `json:"category"` + Key string `json:"key"` + NameKo string `json:"name_ko"` + NameJa string `json:"name_ja"` + NameEn string `json:"name_en"` + CreatedAt pgtype.Timestamp `json:"created_at"` + UpdatedAt pgtype.Timestamp `json:"updated_at"` +} + type BatormentV3Present struct { PresentID int32 `json:"present_id"` NameKo string `json:"name_ko"` diff --git a/internal/db/postgres/querier.go b/internal/db/postgres/querier.go index 853f61c..9f5988b 100644 --- a/internal/db/postgres/querier.go +++ b/internal/db/postgres/querier.go @@ -10,12 +10,15 @@ import ( type Querier interface { GetContentByID(ctx context.Context, contentID string) (GetContentByIDRow, error) + GetI18n(ctx context.Context, arg GetI18nParams) (BatormentV3I18n, error) + GetI18nByCategory(ctx context.Context, category string) ([]BatormentV3I18n, error) GetVerifiedYoutubeAnalysisByRaidID(ctx context.Context, raidID string) ([]GetVerifiedYoutubeAnalysisByRaidIDRow, error) InsertPresent(ctx context.Context, arg InsertPresentParams) error InsertStudentData(ctx context.Context, arg InsertStudentDataParams) error ListContentIDs(ctx context.Context) ([]string, error) ListContentIDsWithStartDate(ctx context.Context) ([]ListContentIDsWithStartDateRow, error) ListContentsForRaidList(ctx context.Context) ([]ListContentsForRaidListRow, error) + UpsertI18n(ctx context.Context, arg UpsertI18nParams) error } var _ Querier = (*Queries)(nil) diff --git a/internal/db/postgres/sql/query/i18n.sql b/internal/db/postgres/sql/query/i18n.sql new file mode 100644 index 0000000..47dd479 --- /dev/null +++ b/internal/db/postgres/sql/query/i18n.sql @@ -0,0 +1,14 @@ +-- name: UpsertI18n :exec +INSERT INTO batorment_v3.i18n (category, key, name_ko, name_ja, name_en, created_at, updated_at) +VALUES ($1, $2, $3, $4, $5, NOW(), NOW()) +ON CONFLICT (category, key) DO UPDATE SET + name_ko = EXCLUDED.name_ko, + name_ja = EXCLUDED.name_ja, + name_en = EXCLUDED.name_en, + updated_at = NOW(); + +-- name: GetI18nByCategory :many +SELECT * FROM batorment_v3.i18n WHERE category = $1; + +-- name: GetI18n :one +SELECT * FROM batorment_v3.i18n WHERE category = $1 AND key = $2; diff --git a/internal/db/postgres/sql/schema/schema_251007.sql b/internal/db/postgres/sql/schema/schema_251007.sql index 35fe345..f8037d8 100644 --- a/internal/db/postgres/sql/schema/schema_251007.sql +++ b/internal/db/postgres/sql/schema/schema_251007.sql @@ -54,10 +54,23 @@ CREATE TABLE batorment_v3.presents ( updated_at TIMESTAMP ); +-- Table for i18n translations (school, club, etc.) +CREATE TABLE batorment_v3.i18n ( + category VARCHAR(20) NOT NULL, + key VARCHAR(50) NOT NULL, + name_ko VARCHAR(100) NOT NULL, + name_ja VARCHAR(100) NOT NULL, + name_en VARCHAR(100) NOT NULL, + created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP, + PRIMARY KEY (category, key) +); + -- Indexes for better performance CREATE INDEX IF NOT EXISTS idx_students_name ON batorment_v3.students(name_ko); CREATE INDEX IF NOT EXISTS idx_students_name_ja ON batorment_v3.students(name_ja); CREATE INDEX IF NOT EXISTS idx_students_details ON batorment_v3.students USING GIN (detail); CREATE INDEX IF NOT EXISTS idx_presents_name ON batorment_v3.presents(name_ko); CREATE INDEX IF NOT EXISTS idx_presents_tags ON batorment_v3.presents USING GIN (tags); +CREATE INDEX IF NOT EXISTS idx_i18n_category ON batorment_v3.i18n(category); diff --git a/internal/logic/schaledb/i18n.go b/internal/logic/schaledb/i18n.go new file mode 100644 index 0000000..9601ede --- /dev/null +++ b/internal/logic/schaledb/i18n.go @@ -0,0 +1,68 @@ +package schaledb + +import ( + "context" + "encoding/json" + "log" + + "ba-torment-data-process/internal/constants" + "ba-torment-data-process/internal/db/postgres" + logic_download "ba-torment-data-process/internal/logic/download" +) + +type localizationRaw struct { + School map[string]string `json:"School"` + Club map[string]string `json:"Club"` +} + +func loadLocalizationFull(lang string) *localizationRaw { + url := constants.SchaleDBURL + "data/" + lang + "/localization.min.json" + byteValue := logic_download.GetDataFromURL(url) + + var data localizationRaw + if err := json.Unmarshal(byteValue, &data); err != nil { + log.Fatalf("Failed to unmarshal localization (%s): %v", lang, err) + } + + return &data +} + +func SaveI18nData(db *postgres.Queries) error { + kr := loadLocalizationFull("kr") + ja := loadLocalizationFull("jp") + en := loadLocalizationFull("en") + + ctx := context.Background() + + // Save School + for key := range kr.School { + err := db.UpsertI18n(ctx, postgres.UpsertI18nParams{ + Category: "school", + Key: key, + NameKo: kr.School[key], + NameJa: ja.School[key], + NameEn: en.School[key], + }) + if err != nil { + return err + } + log.Printf("Saved i18n school: %s", key) + } + + // Save Club + for key := range kr.Club { + err := db.UpsertI18n(ctx, postgres.UpsertI18nParams{ + Category: "club", + Key: key, + NameKo: kr.Club[key], + NameJa: ja.Club[key], + NameEn: en.Club[key], + }) + if err != nil { + return err + } + log.Printf("Saved i18n club: %s", key) + } + + return nil +} From 6b17a45720aad1c6fb5299aaebd88ac8450c4300 Mon Sep 17 00:00:00 2001 From: Austin Lee Date: Sun, 4 Jan 2026 20:33:11 +0900 Subject: [PATCH 8/9] Fix env variables --- env.yaml | 4 ++-- internal/logic/upload/upload.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/env.yaml b/env.yaml index 0b37ee8..af9c2b0 100644 --- a/env.yaml +++ b/env.yaml @@ -9,8 +9,8 @@ env: - secretKey: "POSTGRES_PASSWORD" remoteRef: "SUPABASE_POSTGRES_PASSWORD" # Service key for personal file manager API. - - secretKey: "FILE_MANAGER_SERVICE_API_KEY" - remoteRef: "FILE_MANAGER_SERVICE_API_KEY" + - secretKey: "BA_ANALYZER_SERVICE_TOKEN" + remoteRef: "BA_ANALYZER_SERVICE_TOKEN" # Base URL for DuckDB download. - secretKey: "BATORMENT_DUCKDB_REMOTE_URL" remoteRef: "BATORMENT_DUCKDB_REMOTE_URL" diff --git a/internal/logic/upload/upload.go b/internal/logic/upload/upload.go index 78f4075..9fe3a86 100644 --- a/internal/logic/upload/upload.go +++ b/internal/logic/upload/upload.go @@ -66,7 +66,7 @@ func UploadFile(path string, fileName string, data []byte, dryRun bool) error { log.Fatalf("API request failed: %v", err) } - req.Header.Set("X-Access-Token", logic.GetEnv("FILE_MANAGER_SERVICE_API_KEY", "")) + req.Header.Set("X-Access-Token", logic.GetEnv("BA_ANALYZER_SERVICE_TOKEN", "")) req.Header.Set("Content-Type", writer.FormDataContentType()) client := &http.Client{} From c20998b8498c9edf1ba0710fd4eec28bb1b172e0 Mon Sep 17 00:00:00 2001 From: Austin Lee Date: Sun, 4 Jan 2026 21:47:37 +0900 Subject: [PATCH 9/9] Update image tag --- build.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.sh b/build.sh index 3b02717..6590d94 100644 --- a/build.sh +++ b/build.sh @@ -1,4 +1,4 @@ -tag=v0.1.3 +tag=v0.1.4 docker build -t ghcr.io/beaverhouse/ba-data-process:$tag . docker push ghcr.io/beaverhouse/ba-data-process:$tag