Files
python-tdd/src/apps/epic/migrations/0017_castanedan_virtues.py
Disco DeDisco da57106d7a
All checks were successful
ci/woodpecker/push/pyswiss Pipeline was successful
ci/woodpecker/push/main Pipeline was successful
castanedan virtues + card 49 tweak; italic_word for trumps 19–21; sig/sea propagation — TDD
- migration 0016: card 49 gravity_reversal All-Bestowing → Bestowing
- migration 0017: implicit virtues (trumps 6–9) Sublimating/Sedimentary qualifiers + shared reversals (Indulged Folly / Indulgent Doing / Self-Indulgence / Indulging Personal History); explicit virtues (trumps 19–21) full-string emanation/reversal overrides (The Hunter's/Sleeper's/Quarry's etc.); canonicalize trump 7 name "Not Doing" → "Not-Doing"
- migrations 0018+0019: TarotCard.italic_word field; populated for trumps 19–21 (Stalking / Dreaming / Intent)
- _tarot_fan.html: data-italic-word + |italicize:card.italic_word filter applied to all rendered title slots
- new templatetags/tarot_filters.py: italicize(text, word) — escape-safe <em> wrapping
- StageCard JS: parse data-italic-word; new _escape / _italicize / _setTitle helpers wrap matching word in <em> via innerHTML when present (textContent otherwise)
- views.py _card_dict: include polarity-split overrides + italic_word so Sea Select stage gets them via fetch JSON
- _sig_select_overlay.html: emit the five new data-* attrs on sig-card markup so Sig Select stage picks them up via StageCard.fromDataset

Code architected by Disco DeDisco <discodedisco@outlook.com>
Git commit message Co-Authored-By:
Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-30 23:36:35 -04:00

115 lines
4.0 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""Populate the seven Castanedan Virtues — trumps 69 (Implicit) + 1921 (Explicit).
Implicit Virtues (69): emanation qualifier differs by polarity (Sublimating /
Sedimentary), name is shared. Reversal is a single full string shared across
both polarities (the agency word — Controlled / Not / Losing / Erasing —
flips to Indulged / Indulgent / Self-Indulgence / Indulging). We fill the
standard `levity_qualifier` / `gravity_qualifier` slots so the major-arcana
upright renders "Controlled Folly,\nSublimating" via the existing template
branch; we fill BOTH `levity_reversal` + `gravity_reversal` with the same
string so a FLIP'd reversal still picks up the override (an empty side falls
through to the default major-arcana rendering).
Explicit Virtues (1921): emanation is shared across polarities (e.g. "The
Hunter's Stalking" — no qualifier + stem decomposition), reversal differs by
polarity. All four polarity-split title fields filled.
Also canonicalizes trump 7's name from "Not Doing" to "Not-Doing" per the spec
doc (slug "not-doing" already correct).
"""
from django.db import migrations
IMPLICIT = [
# (number, levity_qualifier, gravity_qualifier, reversal_title)
(6, "Sublimating", "Sedimentary", "Indulged Folly"),
(7, "Sublimating", "Sedimentary", "Indulgent Doing"),
(8, "Sublimating", "Sedimentary", "Self-Indulgence"),
(9, "Sublimating", "Sedimentary", "Indulging Personal History"),
]
EXPLICIT = [
# (number, levity_emanation, gravity_emanation, levity_reversal, gravity_reversal)
(19, "The Hunter's Stalking", "The Hunter's Stalking", "The Sleeper's Stalking", "The Quarry's Stalking"),
(20, "The Dreamer's Dreaming", "The Dreamer's Dreaming", "The Sleeper's Dreaming", "The Dreamed's Dreaming"),
(21, "The Warrior's Intent", "The Warrior's Intent", "The Sleeper's Intent", "The Predator's Intent"),
]
def forward(apps, schema_editor):
TarotCard = apps.get_model("epic", "TarotCard")
DeckVariant = apps.get_model("epic", "DeckVariant")
try:
earthman = DeckVariant.objects.get(slug="earthman")
except DeckVariant.DoesNotExist:
return
# Trump 7 name canonicalization
TarotCard.objects.filter(
deck_variant=earthman, arcana="MAJOR", number=7,
).update(name="Not-Doing")
for number, lvty, grav, rev in IMPLICIT:
TarotCard.objects.filter(
deck_variant=earthman, arcana="MAJOR", number=number,
).update(
levity_qualifier=lvty,
gravity_qualifier=grav,
levity_reversal=rev,
gravity_reversal=rev,
)
for number, le, ge, lr, gr in EXPLICIT:
TarotCard.objects.filter(
deck_variant=earthman, arcana="MAJOR", number=number,
).update(
levity_emanation=le,
gravity_emanation=ge,
levity_reversal=lr,
gravity_reversal=gr,
)
def reverse(apps, schema_editor):
TarotCard = apps.get_model("epic", "TarotCard")
DeckVariant = apps.get_model("epic", "DeckVariant")
try:
earthman = DeckVariant.objects.get(slug="earthman")
except DeckVariant.DoesNotExist:
return
TarotCard.objects.filter(
deck_variant=earthman, arcana="MAJOR", number=7,
).update(name="Not Doing")
for number, _lvty, _grav, _rev in IMPLICIT:
TarotCard.objects.filter(
deck_variant=earthman, arcana="MAJOR", number=number,
).update(
levity_qualifier="",
gravity_qualifier="",
levity_reversal="",
gravity_reversal="",
)
for number, _le, _ge, _lr, _gr in EXPLICIT:
TarotCard.objects.filter(
deck_variant=earthman, arcana="MAJOR", number=number,
).update(
levity_emanation="",
gravity_emanation="",
levity_reversal="",
gravity_reversal="",
)
class Migration(migrations.Migration):
dependencies = [
("epic", "0016_card49_bestowing_eagle"),
]
operations = [
migrations.RunPython(forward, reverse_code=reverse),
]