hensei-api/docs/plans/character-season-series.md
Justin Edmund ef7c158736 add implementation plans for artifacts, character series, weapon series
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-13 20:54:48 -08:00

296 lines
12 KiB
Markdown

# Character Season & Series Implementation Plan
## Overview
Add `season`, `series`, and `gacha_available` columns to the characters table to properly categorize character variants. This replaces the current approach of disambiguating in name fields (e.g., "Vira (SSR)").
**Goals:**
- Clean character names without disambiguation suffixes
- Enable filtering by season/series in the UI
- Support gacha bot queries for pool availability
- Centralize gacha metadata in hensei-api (shared database with siero-bot)
---
## Data Model
### Season (integer, nullable)
Represents the gacha time period when a character can be pulled.
| Value | Name | Notes |
|-------|-----------|-------|
| 1 | Standard | Available year-round (in their respective pool) (1-index to work better with Javascript) |
| 2 | Valentine | February |
| 3 | Formal | June |
| 4 | Summer | July-August (both Summer and Yukata series) |
| 5 | Halloween | October |
| 6 | Holiday | December |
### Series (integer array)
Represents the character's identity/pool membership. A character can belong to multiple series (e.g., Summer Zodiac = [summer, zodiac]).
| Value | Name | Notes |
|-------|-----------|-------|
| 1 | Standard | Regular gacha pool (1-index to work better with Javascript) |
| 2 | Grand | Flash Gala and Legend Gacha exclusive |
| 3 | Zodiac | Legend Fest exclusive, yearly rotation |
| 4 | Promo | Not in gacha - ticket/code redemption |
| 5 | Collab | Collaboration events |
| 6 | Eternal | Free recruitment (Eternals) |
| 7 | Evoker | Free recruitment (Evokers) |
| 8 | Saint | Free recruitment (similar to Eternals), may have gacha alts |
| 9 | Fantasy | Can combine with other series (e.g., Fantasy + Grand) |
| 10 | Summer | Summer seasonal series |
| 11 | Yukata | Yukata seasonal series (same gacha window as Summer) |
| 12 | Valentine | Valentine seasonal series |
| 13 | Halloween | Halloween seasonal series |
| 14 | Formal | Formal seasonal series |
| 15 | Event | Event reward characters |
### Gacha Available (boolean)
Quick filter for characters that can be pulled in gacha vs those that are recruited.
- `true` - Can be pulled from gacha (Standard, Grand, Zodiac, seasonal series, etc.)
- `false` - Must be recruited (Eternals, Evokers, Saints, Event, Collab)
### Promotion (integer array on weapons/summons)
Move promotion data from the `gacha` table to weapons and summons directly.
| Value | Name | Notes |
|-------|------------|-------|
| 1 | Premium | Available in regular Premium draws |
| 2 | Classic | Available in Classic draws |
| 3 | Classic II | Available in Classic II draws |
| 4 | Flash | Available in Flash Gala (6% SSR rate) |
| 5 | Legend | Available in Legend Fest (6% SSR rate) |
| 6 | Valentine | Valentine seasonal gacha |
| 7 | Summer | Summer seasonal gacha |
| 8 | Halloween | Halloween seasonal gacha |
| 9 | Holiday | Holiday seasonal gacha |
| 10 | Collab | Collaboration gacha |
| 11 | Formal | Formal seasonal gacha |
**Decision:** Add `promotions` as integer array on weapons and summons tables, then deprecate the `gacha` table. This eliminates the polymorphic join and simplifies queries.
---
## Implementation Tasks
### Phase 1: Schema Migration
- [x] **Task 1.1:** Create migration to add `season`, `series`, and `gacha_available` columns: `6e62053`
- [x] **Task 1.2:** Add enum constants to `GranblueEnums`: `24d8d20`, `208d1f4`
### Phase 2: Model Updates
- [x] **Task 2.1:** Update `Character` model: `afa1c51`
- [x] **Task 2.2:** Update `CharacterBlueprint`: `a3c33ce`, `cb01658`
### Phase 3: Controller & API Updates
- [x] **Task 3.1:** Update `CharactersController`: `db048dc`
- [x] **Task 3.2:** Update search functionality: `9c5c859`
- Added `season`, `series`, `gacha_available` filters to characters search
- Added `promotions` filter to weapons search
- Added `promotions` filter to summons search
- Array filters use PostgreSQL `&&` (overlap) operator
### Phase 4: Importer/Parser Updates
- [x] **Task 4.1:** Update `CharacterImporter`: `e0a82bc`
- Added `season`, `series`, `gacha_available` to `build_attributes`
- [x] **Task 4.2:** Update `CharacterParser`: `e0a82bc`
- Added `series_from_hash`, `season_from_hash`, `gacha_available_from_hash` methods
- Extracts data from wiki `|series=` and `|obtain=` fields
- [x] **Task 4.3:** Update `WeaponImporter` and `SummonImporter`: `7aa0521`
- Added `promotions` to `build_attributes`
- [x] **Task 4.4:** Update `WeaponParser` and `SummonParser`: `c1a5d62`
- Added `promotions_from_obtain` method
- Added wiki promotions mapping to `Wiki` class
### Phase 5: Data Migration
- [x] **Task 5.1:** Create rake task to export characters to CSV: `284ee44`
- [ ] **Task 5.2:** User curates CSV with season/series assignments (manual step)
- [x] **Task 5.3:** Create rake task to import curated CSV: `284ee44`
- [ ] **Task 5.4:** Clean up character names (deferred until data populated)
### Phase 6: Frontend Updates (hensei-svelte)
- [x] **Task 6.1:** Update TypeScript types: `67b87c7d`
- [x] **Task 6.2:** Update search adapter: `f26d3e38`
- [x] **Task 6.3:** Add filter UI components (CheckboxGroup): `96f040a9`
- [x] **Task 6.4:** Update character display: `cf694bb1`
- [x] **Task 6.5:** Update weapon display: `8c45c219`
- [x] **Task 6.6:** Update summon display: `23ae7f70`
- [x] **Task 6.7:** Update batch import pages: `1933f3d8`
- [x] **Task 6.8:** Add recruited_by to character metadata: `cf694bb1`
### Phase 7: Weapons/Summons Promotion Migration
- [x] **Task 7.1:** Add `promotions` column to weapons table: `6f64610`
- [x] **Task 7.2:** Add `promotions` column to summons table: `6f64610`
- [x] **Task 7.3:** Add PROMOTIONS enum to `GranblueEnums`: `6f64610`, `208d1f4`
- [x] **Task 7.4:** Create data migration rake task: `49e52ff`
- [x] **Task 7.5:** Update Weapon and Summon models: `e81c559`
- [x] **Task 7.6:** Update WeaponBlueprint and SummonBlueprint: `0dba56c`
- [x] **Task 7.7:** Update controllers to permit `promotions`: `05dd899`
- [ ] **Task 7.8:** Deprecate `gacha` table (after siero-bot deployed and tested)
### Phase 8: Siero-bot Integration
- [x] **Task 8.1:** Update siero-bot to use new columns (branch: `feature/use-promotions-column`)
- `210b5dc` - Added `PromotionId` enum mapping to hensei-api values
- `b0e0972` - Added `promotions: number[]` to table interfaces
- `897d86b` - Rewrote `cache.ts` to query weapons/summons directly
- `1b7ac14` - Rewrote `api.ts` to remove gacha table dependency
- `d745b0f` - Changed rateups to use `granblue_id` for stability
---
## Remaining Work (Pre-Deployment Testing Required)
These tasks require testing and/or manual intervention before completion:
### 1. Data Population (Manual)
**Task 5.2: Curate character season/series data**
Options for populating character data:
- **Option A:** Export CSV, manually curate, re-import
```bash
rake characters:export_csv # Creates lib/seeds/characters_export.csv
# Edit CSV to add season/series/gacha_available values
rake characters:import_csv # Imports curated data
```
- **Option B:** Use wiki parser to auto-populate (may need manual review)
```bash
rails runner "Granblue::Parsers::CharacterParser.fetch_all(save: true, overwrite: true)"
```
### 2. Siero-bot Deployment
**Deploy and test siero-bot changes before deprecating gacha table:**
1. Merge `feature/use-promotions-column` branch
2. Deploy to production
3. Test gacha simulation commands work correctly
4. Test rateup functionality works correctly
5. Monitor for errors
### 3. Database Cleanup (Destructive)
**Task 7.8: Deprecate `gacha` table**
Only after siero-bot is deployed and verified working:
1. Create migration to drop `gacha` table
2. Remove any remaining references to `Gacha` model
3. Clean up unused code
### 4. Character Name Cleanup (Optional)
**Task 5.4: Remove disambiguation from character names**
After season/series data is populated and frontend displays badges:
1. Remove suffixes like "(SSR)", "(Summer)", "(Grand)" from `name_en`
2. Update any search indexes if needed
3. Verify frontend displays correctly with badges instead of name suffixes
---
## File Changes Summary
### hensei-api
| File | Change |
|------|--------|
| `db/migrate/*_add_season_series_to_characters.rb` | Add season, series, gacha_available columns |
| `db/migrate/*_add_promotions_to_weapons.rb` | Add promotions array column |
| `db/migrate/*_add_promotions_to_summons.rb` | Add promotions array column |
| `lib/tasks/characters.rake` | Export/import rake tasks for data migration |
| `lib/tasks/gacha.rake` | Migration tasks for promotions data |
| `app/models/concerns/granblue_enums.rb` | Add CHARACTER_SEASONS, CHARACTER_SERIES, PROMOTIONS |
| `app/models/character.rb` | Add validations, scopes, helpers |
| `app/models/weapon.rb` | Add promotions validations, scopes, helpers |
| `app/models/summon.rb` | Add promotions validations, scopes, helpers |
| `app/blueprints/api/v1/character_blueprint.rb` | Serialize new fields, recruited_by |
| `app/blueprints/api/v1/weapon_blueprint.rb` | Serialize promotions |
| `app/blueprints/api/v1/summon_blueprint.rb` | Serialize promotions |
| `app/controllers/api/v1/characters_controller.rb` | Permit new params, filtering |
| `app/controllers/api/v1/weapons_controller.rb` | Permit promotions |
| `app/controllers/api/v1/summons_controller.rb` | Permit promotions |
| `app/controllers/api/v1/search_controller.rb` | Add season/series/promotions filters |
| `lib/granblue/importers/character_importer.rb` | Parse new CSV columns |
| `lib/granblue/importers/weapon_importer.rb` | Parse promotions column |
| `lib/granblue/importers/summon_importer.rb` | Parse promotions column |
| `lib/granblue/parsers/character_parser.rb` | Extract season/series from wiki |
| `lib/granblue/parsers/weapon_parser.rb` | Extract promotions from wiki |
| `lib/granblue/parsers/summon_parser.rb` | Extract promotions from wiki |
| `lib/granblue/parsers/wiki.rb` | Add promotions mapping |
### hensei-svelte
| File | Change |
|------|--------|
| `src/lib/types/Character.d.ts` | Add season/series/gachaAvailable fields |
| `src/lib/types/Weapon.d.ts` | Add promotions fields |
| `src/lib/types/Summon.d.ts` | Add promotions fields |
| `src/lib/types/enums.ts` | Add CharacterSeason, CharacterSeries, Promotion enums |
| `src/lib/api/adapters/entity.adapter.ts` | Add new fields to interfaces |
| `src/lib/api/adapters/search.adapter.ts` | Add filter params |
| `src/lib/api/adapters/types.ts` | Add SearchFilters types |
| `src/lib/components/ui/checkbox/CheckboxGroup.svelte` | New multiselect component |
| `src/lib/components/ui/DetailItem.svelte` | Add multiselect type support |
| `src/lib/features/database/characters/schema.ts` | Add to edit schema |
| `src/lib/features/database/characters/sections/CharacterTaxonomySection.svelte` | Add season/series/gacha UI |
| `src/lib/features/database/characters/sections/CharacterMetadataSection.svelte` | Add recruited_by display |
| `src/lib/features/database/weapons/schema.ts` | Add promotions to schema |
| `src/lib/features/database/weapons/sections/WeaponTaxonomySection.svelte` | Add promotions UI |
| `src/lib/features/database/summons/schema.ts` | Add promotions to schema |
| `src/lib/features/database/summons/sections/SummonTaxonomySection.svelte` | Add promotions UI |
| `src/routes/(app)/database/*/import/+page.svelte` | Add new fields to batch import |
### siero-bot (branch: feature/use-promotions-column)
| File | Change |
|------|--------|
| `src/services/cache.ts` | Query promotions from weapons/summons instead of gacha table |
| `src/services/api.ts` | Remove gacha table dependency, use granblue_id for rateups |
| `src/utils/enums.ts` | Add PromotionId enum, mapping helpers |
| `src/services/tables.ts` | Add promotions to WeaponTable and SummonTable |
---
## Notes
- The `character_id` array field will remain unchanged (no rename)
- `gacha_available` boolean added for quick filtering of pullable vs recruited characters
- All enums are 1-indexed to work better with JavaScript/TypeScript
- `gacha` table will be deprecated after siero-bot migration is complete
- Season values represent gacha availability windows (when you can pull)
- Series values represent character identity (what pool/category they belong to)
- Wiki uses "gala" for Flash Gala weapons (mapped to Flash promotion ID 4)