A.0 image-rendering schema + RWS rename + canonical-Earthman suit collapse — TDD. Sprint A.0 of [[project-image-based-deck-face-rendering]]. Adds three DeckVariant fields: has_card_images (BooleanField default=True — Earthman keeps False until its artwork ships, every new deck defaults True), family (CharField choices=[earthman, italian, english, playing] default=earthman — drives per-family display + filename slug mapping per [[reference-card-image-naming-convention]]), is_polarized (BooleanField default=False — Earthman is True today; Sprint A.4 game_kit applet will render "(×2)" in --terUser for polarized decks; Sprint C+B segment model uses it for segment-count logic). TarotCard.SUIT_CHOICES collapses from 8 values to 4 canonical Earthman values (BRANDS / CROWNS / GRAILS / BLADES); WANDS / CUPS / SWORDS / PENTACLES dropped — they were duplicative at the structural level since sig_deck_cards + levity/gravity_sig_cards already treated [WANDS, BRANDS, CROWNS] as one segment and [SWORDS, BLADES, CUPS, GRAILS] as another (so the project already *functionally* equated them; the lock just makes that explicit). Per-family display vocab (batons for Italian, wands for English, clubs for Playing) lives in Sprint A.2's display_suit_name property, not in the enum. Audit 2026-05-25 revealed the existing fiorentine-minchiate DeckVariant is actually 78-card RWS Tarot in disguise (22 majors numbered 0-21 w. RWS names: The Fool / The Magician / ... / The World; 56 minors in 4 suits × 14 cards) — NOT Minchiate (which has 40 trumps + 1 Il Matto + 56 minors = 97 cards). Migration 0012 renames the slug → tarot-rider-waite-smith, name → "Tarot (Rider-Waite-Smith)", sets family='english', has_card_images=False, is_polarized=False — and revocabs its 56 minor cards' suits in-place (WANDS→BRANDS, CUPS→GRAILS, SWORDS→BLADES, PENTACLES→CROWNS) so they match the new canonical enum. FKs (User.equipped_deck, User.unlocked_decks, TableSeat.deck_variant, etc.) survive untouched — slug-only changes don't break referential integrity. Earthman fields set explicitly in 0012 too (family=earthman, has_card_images=False, is_polarized=True). Companion code simplifications: sig_deck_cards + _sig_unique_cards_for_deck queries shrink from suit__in=[3 values] and [4 values] to [2 values] each (one per segment); TarotCard.suit_icon mapping shrinks from 8 entries to 4; gameboard.views.tarot_fan._suit_order shrinks from 8 keys to 4. Existing test files updated: test_game_room_tray.py (largest update — self.fiorentine → self.rws, id_kit_fiorentine_deck → id_kit_tarot_deck (template-id derives from deck.short_key = first slug segment), assertion "Fiorentine" → "Rider-Waite-Smith"); test_game_room_deck_contrib.py (same pattern, smaller); lyric/test_models.py + gameboard/test_views.py (slug literal swaps only); epic/test_models.py _make_sig_card test fixtures: "WANDS"→"BRANDS", "CUPS"→"GRAILS". 14 new ITs in DeckSchemaA0Test cover the schema additions + migration outcomes (field existence + choice values + earthman has all three fields set correctly + RWS rename verified + RWS cards use canonical suits + dropped enum values absent from SUIT_CHOICES). Tests: 14 new green; 1255/1255 IT+UT total green (38s); no regressions. Out of scope: Sprint A.1 will seed the actual Minchiate Fiorentine 1860-1890 (97-card) DeckVariant + TarotCard rows w. family='italian', has_card_images=True; A.2 adds the image_filename + display_suit_name properties that consume the new family field; A.3+ wires the render branches across 6 surfaces
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,33 @@
|
||||
# Generated by Django 6.0 on 2026-05-25 03:15
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('epic', '0010_set_reversal_drops_qualifier_realms'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='deckvariant',
|
||||
name='family',
|
||||
field=models.CharField(choices=[('earthman', 'Earthman'), ('italian', 'Italian / Minchiate'), ('english', 'English Tarot'), ('playing', 'Playing card')], default='earthman', max_length=10),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='deckvariant',
|
||||
name='has_card_images',
|
||||
field=models.BooleanField(default=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='deckvariant',
|
||||
name='is_polarized',
|
||||
field=models.BooleanField(default=False),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='tarotcard',
|
||||
name='suit',
|
||||
field=models.CharField(blank=True, choices=[('BRANDS', 'Brands'), ('CROWNS', 'Crowns'), ('GRAILS', 'Grails'), ('BLADES', 'Blades')], max_length=10, null=True),
|
||||
),
|
||||
]
|
||||
70
src/apps/epic/migrations/0012_rws_rename_and_suit_revocab.py
Normal file
70
src/apps/epic/migrations/0012_rws_rename_and_suit_revocab.py
Normal file
@@ -0,0 +1,70 @@
|
||||
"""Sprint A.0 data migration.
|
||||
|
||||
The existing `fiorentine-minchiate` DeckVariant is actually 78-card Rider-Waite-Smith
|
||||
Tarot (22 majors numbered 0-21 with RWS names + 56 minors in WANDS/CUPS/SWORDS/PENTACLES).
|
||||
Rename to its true identity, set the new schema fields (family / has_card_images /
|
||||
is_polarized), and revocab card suits to the canonical Earthman vocabulary that
|
||||
SUIT_CHOICES now requires. Earthman gets its new-field values set too. FKs remain
|
||||
intact since slug-only changes don't break referential integrity.
|
||||
|
||||
The actual Minchiate Fiorentine deck (97 cards, 1860-1890 publication, image set
|
||||
already imported per [[reference-card-image-naming-convention]]) is seeded in
|
||||
Sprint A.1's follow-up migration.
|
||||
"""
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
SUIT_REVOCAB = {
|
||||
"WANDS": "BRANDS",
|
||||
"CUPS": "GRAILS",
|
||||
"SWORDS": "BLADES",
|
||||
"PENTACLES": "CROWNS",
|
||||
}
|
||||
|
||||
|
||||
def forward(apps, schema_editor):
|
||||
DeckVariant = apps.get_model("epic", "DeckVariant")
|
||||
TarotCard = apps.get_model("epic", "TarotCard")
|
||||
|
||||
# Earthman: set new-field values explicitly (default=True for has_card_images
|
||||
# would have left it True after the schema migration — wrong for Earthman).
|
||||
DeckVariant.objects.filter(slug="earthman").update(
|
||||
family="earthman", has_card_images=False, is_polarized=True,
|
||||
)
|
||||
|
||||
# fiorentine-minchiate → RWS Tarot rename + new-field values.
|
||||
rws = DeckVariant.objects.filter(slug="fiorentine-minchiate").first()
|
||||
if rws:
|
||||
rws.slug = "tarot-rider-waite-smith"
|
||||
rws.name = "Tarot (Rider-Waite-Smith)"
|
||||
rws.family = "english"
|
||||
rws.has_card_images = False
|
||||
rws.is_polarized = False
|
||||
rws.save()
|
||||
# Revocab the 56 minor cards' suits to canonical Earthman vocab.
|
||||
for old_suit, new_suit in SUIT_REVOCAB.items():
|
||||
TarotCard.objects.filter(deck_variant=rws, suit=old_suit).update(
|
||||
suit=new_suit,
|
||||
)
|
||||
|
||||
|
||||
def backward(apps, schema_editor):
|
||||
DeckVariant = apps.get_model("epic", "DeckVariant")
|
||||
TarotCard = apps.get_model("epic", "TarotCard")
|
||||
|
||||
rws = DeckVariant.objects.filter(slug="tarot-rider-waite-smith").first()
|
||||
if rws:
|
||||
for new_suit, old_suit in {v: k for k, v in SUIT_REVOCAB.items()}.items():
|
||||
TarotCard.objects.filter(deck_variant=rws, suit=new_suit).update(
|
||||
suit=old_suit,
|
||||
)
|
||||
rws.slug = "fiorentine-minchiate"
|
||||
rws.name = "Fiorentine Minchiate"
|
||||
rws.save()
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
("epic", "0011_deckvariant_family_deckvariant_has_card_images_and_more"),
|
||||
]
|
||||
operations = [migrations.RunPython(forward, backward)]
|
||||
Reference in New Issue
Block a user