Earthman deck: new TarotCard fields + full 49-card major arcana reseed
- TarotCard: add reversal, levity/gravity_qualifier, levity/gravity_emanation, levity/gravity_reversal, mechanisms, articulations; emanation_for(polarity) + reversal_for(polarity) methods - 0035: schema migration for new fields - 0036: reseed major arcana — 52 cards → 50 (Nomad untouched); delete Wheel of Fortune/Junkboat/Great Hunt; insert Asteroid Belt; rename/renumber all into Pope/Horseman, Elements, Realms, Virtues, Zodiac, Lunars, Planets, Inner Rings; levity/gravity qualifiers throughout; cards 48-49 polarity-split levity/gravity emanation + reversal - 0037: Polestar qualifiers → Precessional / Recessional - 0038: correspondences → Fiorentine card names (Magician–Hierophant for 1-5; sign names for zodiac 22-33) - 0039: reversals — Pope/Horseman 1-5 → Territoriality/Despotism/Capitalism/Fascism; Zodiac 22-33 → House of Self … House of Reprisal - 0040: trump 21 → Intent (was Jovent) ⚠ pending: cards 1-2 reversal both 'Territoriality' — confirm if distinct; Implicit/Explicit Virtue qualifiers blank Code architected by Disco DeDisco <discodedisco@outlook.com> Git commit message Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
58
src/apps/epic/migrations/0035_earthman_deck_new_fields.py
Normal file
58
src/apps/epic/migrations/0035_earthman_deck_new_fields.py
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
# Generated by Django 6.0 on 2026-04-27 05:11
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('epic', '0034_character_model'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='tarotcard',
|
||||||
|
name='articulations',
|
||||||
|
field=models.JSONField(default=list),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='tarotcard',
|
||||||
|
name='gravity_emanation',
|
||||||
|
field=models.CharField(blank=True, default='', max_length=200),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='tarotcard',
|
||||||
|
name='gravity_qualifier',
|
||||||
|
field=models.CharField(blank=True, default='', max_length=100),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='tarotcard',
|
||||||
|
name='gravity_reversal',
|
||||||
|
field=models.CharField(blank=True, default='', max_length=200),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='tarotcard',
|
||||||
|
name='levity_emanation',
|
||||||
|
field=models.CharField(blank=True, default='', max_length=200),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='tarotcard',
|
||||||
|
name='levity_qualifier',
|
||||||
|
field=models.CharField(blank=True, default='', max_length=100),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='tarotcard',
|
||||||
|
name='levity_reversal',
|
||||||
|
field=models.CharField(blank=True, default='', max_length=200),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='tarotcard',
|
||||||
|
name='mechanisms',
|
||||||
|
field=models.JSONField(default=list),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='tarotcard',
|
||||||
|
name='reversal',
|
||||||
|
field=models.CharField(blank=True, default='', max_length=200),
|
||||||
|
),
|
||||||
|
]
|
||||||
307
src/apps/epic/migrations/0036_earthman_deck_reseed.py
Normal file
307
src/apps/epic/migrations/0036_earthman_deck_reseed.py
Normal file
@@ -0,0 +1,307 @@
|
|||||||
|
"""
|
||||||
|
Re-seed the Earthman major arcana from 52 cards (0-51) to 50 cards (0 + 1-49).
|
||||||
|
|
||||||
|
Card 0 (The Nomad) is left completely untouched.
|
||||||
|
|
||||||
|
Changes:
|
||||||
|
DELETE old #10 Wheel of Fortune, #11 The Junkboat, #14 The Great Hunt
|
||||||
|
INSERT new #41 The Asteroid Belt
|
||||||
|
UPDATE all other major arcana with new numbers, names, slugs, groups,
|
||||||
|
correspondences, reversals, and levity/gravity qualifiers.
|
||||||
|
|
||||||
|
Items flagged ⚠ below were not clearly legible from the source image and are
|
||||||
|
left blank; the user should fill them in a follow-up migration.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# UPDATE spec: (current_slug, new_number, new_name, new_slug, new_group,
|
||||||
|
# correspondence, reversal,
|
||||||
|
# levity_qualifier, gravity_qualifier,
|
||||||
|
# levity_emanation, gravity_emanation,
|
||||||
|
# levity_reversal, gravity_reversal)
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
_UPDATES = [
|
||||||
|
# ── Pope/Horseman ──────────────────────────────────────────────────────
|
||||||
|
('pope-1-the-schizo', 1, 'The Schizo',
|
||||||
|
'the-schizo', 'Pope/Horseman',
|
||||||
|
'Territoriality', '',
|
||||||
|
'Enlightened', 'Engraven',
|
||||||
|
'', '', '', ''),
|
||||||
|
|
||||||
|
('pope-2-the-occultist', 2, 'The Occultist',
|
||||||
|
'the-occultist', 'Pope/Horseman',
|
||||||
|
'Territoriality', '',
|
||||||
|
'Enlightened', 'Engraven',
|
||||||
|
'', '', '', ''),
|
||||||
|
|
||||||
|
('pope-3-the-despot', 3, 'The Despot',
|
||||||
|
'the-despot', 'Pope/Horseman',
|
||||||
|
'Despotism', '',
|
||||||
|
'Enlightened', 'Engraven',
|
||||||
|
'', '', '', ''),
|
||||||
|
|
||||||
|
('pope-4-the-capitalist', 4, 'The Capitalist',
|
||||||
|
'the-capitalist', 'Pope/Horseman',
|
||||||
|
'Capitalism', '',
|
||||||
|
'Enlightened', 'Engraven',
|
||||||
|
'', '', '', ''),
|
||||||
|
|
||||||
|
('pope-5-the-fascist', 5, 'The Fascist',
|
||||||
|
'the-fascist', 'Pope/Horseman',
|
||||||
|
'Fascism', '',
|
||||||
|
'Enlightened', 'Engraven',
|
||||||
|
'', '', '', ''),
|
||||||
|
|
||||||
|
# ── Implicit Virtues ───────────────────────────────────────────────────
|
||||||
|
# ⚠ levity/gravity qualifiers not determined
|
||||||
|
('implicit-virtue-1-controlled-folly', 6, 'Controlled Folly',
|
||||||
|
'controlled-folly', 'Implicit Virtues',
|
||||||
|
'Fortitude', '',
|
||||||
|
'', '', '', '', '', ''),
|
||||||
|
|
||||||
|
('implicit-virtue-2-not-doing', 7, 'Not Doing',
|
||||||
|
'not-doing', 'Implicit Virtues',
|
||||||
|
'Temperance', '',
|
||||||
|
'', '', '', '', '', ''),
|
||||||
|
|
||||||
|
('implicit-virtue-3-losing-self-importance', 8, 'Losing Self-Importance',
|
||||||
|
'losing-self-importance', 'Implicit Virtues',
|
||||||
|
'Justice', '',
|
||||||
|
'', '', '', '', '', ''),
|
||||||
|
|
||||||
|
('implicit-virtue-4-erasing-personal-history', 9, 'Erasing Personal History',
|
||||||
|
'erasing-personal-history', 'Implicit Virtues',
|
||||||
|
'Prudence', '',
|
||||||
|
'', '', '', '', '', ''),
|
||||||
|
|
||||||
|
# ── Elements (6) — Absolute elements move here alongside Classical ─────
|
||||||
|
# Space: was Absolute Element 2 (#38), Chariot correspondence is new
|
||||||
|
('absolute-element-2-space', 10, 'Space',
|
||||||
|
'space-em', 'Elements',
|
||||||
|
'Chariot', 'Nexus',
|
||||||
|
'Kinetic', 'Potential',
|
||||||
|
'', '', '', ''),
|
||||||
|
|
||||||
|
# Time: was Absolute Element 1 (#37)
|
||||||
|
# ⚠ Hermit correspondence confirmed; "Hunchback" variant noted in image
|
||||||
|
('absolute-element-1-time', 11, 'Time',
|
||||||
|
'time-em', 'Elements',
|
||||||
|
'Hermit', 'Tempo',
|
||||||
|
'Kinetic', 'Potential',
|
||||||
|
'', '', '', ''),
|
||||||
|
|
||||||
|
# Stone: was Classical Element 2 (#22)
|
||||||
|
('classical-element-2-stone', 12, 'Stone',
|
||||||
|
'stone-em', 'Elements',
|
||||||
|
'Earth', 'Ossum',
|
||||||
|
'Kinetic', 'Potential',
|
||||||
|
'', '', '', ''),
|
||||||
|
|
||||||
|
# Fire: was Classical Element 1 (#21)
|
||||||
|
('classical-element-1-fire', 13, 'Fire',
|
||||||
|
'fire-em', 'Elements',
|
||||||
|
'Fire', 'Ardor',
|
||||||
|
'Kinetic', 'Potential',
|
||||||
|
'', '', '', ''),
|
||||||
|
|
||||||
|
# Water: was Classical Element 4 (#24)
|
||||||
|
('classical-element-4-water', 14, 'Water',
|
||||||
|
'water-em', 'Elements',
|
||||||
|
'Water', 'Humor',
|
||||||
|
'Kinetic', 'Potential',
|
||||||
|
'', '', '', ''),
|
||||||
|
|
||||||
|
# Air: was Classical Element 3 (#23)
|
||||||
|
('classical-element-3-air', 15, 'Air',
|
||||||
|
'air-em', 'Elements',
|
||||||
|
'Air', 'Pneuma',
|
||||||
|
'Kinetic', 'Potential',
|
||||||
|
'', '', '', ''),
|
||||||
|
|
||||||
|
# ── Realms ─────────────────────────────────────────────────────────────
|
||||||
|
('disco-inferno', 16, 'Disco Inferno',
|
||||||
|
'disco-inferno', 'Realms',
|
||||||
|
'Devil', 'Shame',
|
||||||
|
'Deasil', 'Widdershins',
|
||||||
|
'', '', '', ''),
|
||||||
|
|
||||||
|
('torre-terrestre', 17, 'Torre Terrestre',
|
||||||
|
'torre-terrestre', 'Realms',
|
||||||
|
'Tower', 'Guilt',
|
||||||
|
'Deasil', 'Widdershins',
|
||||||
|
'', '', '', ''),
|
||||||
|
|
||||||
|
('fantasia-celestia', 18, 'Fantasia Celestia',
|
||||||
|
'fantasia-celestia', 'Realms',
|
||||||
|
'Wheel of Fortune', 'Anxiety',
|
||||||
|
'Deasil', 'Widdershins',
|
||||||
|
'', '', '', ''),
|
||||||
|
|
||||||
|
# ── Explicit Virtues ───────────────────────────────────────────────────
|
||||||
|
# ⚠ levity/gravity qualifiers not determined
|
||||||
|
# Stalking: was Explicit Virtue 1 (#18, Love/Charity)
|
||||||
|
('explicit-virtue-1-stalking', 19, 'Stalking',
|
||||||
|
'stalking', 'Explicit Virtues',
|
||||||
|
'Charity', '',
|
||||||
|
'', '', '', '', '', ''),
|
||||||
|
|
||||||
|
# Dreaming: was Explicit Virtue 3 (#20, Faith) — same number
|
||||||
|
('explicit-virtue-3-dreaming', 20, 'Dreaming',
|
||||||
|
'dreaming', 'Explicit Virtues',
|
||||||
|
'Faith', '',
|
||||||
|
'', '', '', '', '', ''),
|
||||||
|
|
||||||
|
# Jovent: was Explicit Virtue 2 (#19, Intent/Hope) — renamed
|
||||||
|
('explicit-virtue-2-intent', 21, 'Jovent',
|
||||||
|
'jovent', 'Explicit Virtues',
|
||||||
|
'Hope', '',
|
||||||
|
'', '', '', '', '', ''),
|
||||||
|
|
||||||
|
# ── Zodiac Signs & Houses (renumber 25-36 → 22-33) ────────────────────
|
||||||
|
('zodiac-1-aries', 22, 'Aries', 'aries', 'Zodiac Signs & Houses', 'House of Self', '', 'Precessional', 'Recessional', '', '', '', ''),
|
||||||
|
('zodiac-2-taurus', 23, 'Taurus', 'taurus', 'Zodiac Signs & Houses', 'House of Worth', '', 'Precessional', 'Recessional', '', '', '', ''),
|
||||||
|
('zodiac-3-gemini', 24, 'Gemini', 'gemini', 'Zodiac Signs & Houses', 'House of Education', '', 'Precessional', 'Recessional', '', '', '', ''),
|
||||||
|
('zodiac-4-cancer', 25, 'Cancer', 'cancer', 'Zodiac Signs & Houses', 'House of Family', '', 'Precessional', 'Recessional', '', '', '', ''),
|
||||||
|
('zodiac-5-leo', 26, 'Leo', 'leo', 'Zodiac Signs & Houses', 'House of Creation', '', 'Precessional', 'Recessional', '', '', '', ''),
|
||||||
|
('zodiac-6-virgo', 27, 'Virgo', 'virgo', 'Zodiac Signs & Houses', 'House of Ritual', '', 'Precessional', 'Recessional', '', '', '', ''),
|
||||||
|
('zodiac-7-libra', 28, 'Libra', 'libra', 'Zodiac Signs & Houses', 'House of Cooperation', '', 'Precessional', 'Recessional', '', '', '', ''),
|
||||||
|
('zodiac-8-scorpio', 29, 'Scorpio', 'scorpio', 'Zodiac Signs & Houses', 'House of Regeneration', '', 'Precessional', 'Recessional', '', '', '', ''),
|
||||||
|
('zodiac-9-sagittarius', 30, 'Sagittarius', 'sagittarius', 'Zodiac Signs & Houses', 'House of Enterprise', '', 'Precessional', 'Recessional', '', '', '', ''),
|
||||||
|
('zodiac-10-capricorn', 31, 'Capricorn', 'capricorn', 'Zodiac Signs & Houses', 'House of Career', '', 'Precessional', 'Recessional', '', '', '', ''),
|
||||||
|
('zodiac-11-aquarius', 32, 'Aquarius', 'aquarius', 'Zodiac Signs & Houses', 'House of Reward', '', 'Precessional', 'Recessional', '', '', '', ''),
|
||||||
|
('zodiac-12-pisces', 33, 'Pisces', 'pisces', 'Zodiac Signs & Houses', 'House of Reprisal', '', 'Precessional', 'Recessional', '', '', '', ''),
|
||||||
|
|
||||||
|
# ── Lunars ─────────────────────────────────────────────────────────────
|
||||||
|
# Animal Powers: was The Junkman (#12, Hanged Man) — renamed
|
||||||
|
('the-junkman', 34, 'Animal Powers',
|
||||||
|
'animal-powers', 'Lunars',
|
||||||
|
'Hanged Man', 'Patrilineage',
|
||||||
|
'Centrifugal', 'Centripetal',
|
||||||
|
'', '', '', ''),
|
||||||
|
|
||||||
|
# Seeded Earth: was King Death & the Cosmic Tree (#13, Death) — renamed
|
||||||
|
('king-death-and-the-cosmic-tree', 35, 'Seeded Earth',
|
||||||
|
'seeded-earth', 'Lunars',
|
||||||
|
'Death', 'Matrilineage',
|
||||||
|
'Centrifugal', 'Centripetal',
|
||||||
|
'', '', '', ''),
|
||||||
|
|
||||||
|
# Twins of Pluto: was Wanderer 11 King & Queen of Hades (#49) — renamed
|
||||||
|
('wanderer-11-king-queen-hades', 36, 'The Twins of Pluto',
|
||||||
|
'twins-of-pluto', 'Lunars',
|
||||||
|
'Pluto', '',
|
||||||
|
'Prograde', 'Retrograde',
|
||||||
|
'', '', '', ''),
|
||||||
|
|
||||||
|
# ── Planets ────────────────────────────────────────────────────────────
|
||||||
|
# ⚠ correspondences left blank — image unclear; user to confirm
|
||||||
|
# Reversals: user confirmed all planet cards reverse to same (blank = same)
|
||||||
|
('wanderer-10-neptune', 37, 'Neptune', 'neptune', 'Planets', 'Neptune', '', 'Prograde', 'Retrograde', '', '', '', ''),
|
||||||
|
('wanderer-9-uranus', 38, 'Uranus', 'uranus', 'Planets', 'Uranus', '', 'Prograde', 'Retrograde', '', '', '', ''),
|
||||||
|
('wanderer-8-saturn', 39, 'Saturn', 'saturn', 'Planets', 'Saturn', '', 'Prograde', 'Retrograde', '', '', '', ''),
|
||||||
|
('wanderer-7-jupiter', 40, 'Jupiter', 'jupiter', 'Planets', 'Jupiter', '', 'Prograde', 'Retrograde', '', '', '', ''),
|
||||||
|
# #41 The Asteroid Belt — INSERT separately below
|
||||||
|
('wanderer-6-mars', 42, 'Mars', 'mars', 'Planets', 'Mars', '', 'Prograde', 'Retrograde', '', '', '', ''),
|
||||||
|
('wanderer-5-venus', 43, 'Venus', 'venus', 'Planets', 'Venus', '', 'Prograde', 'Retrograde', '', '', '', ''),
|
||||||
|
('wanderer-4-mercury', 44, 'Mercury', 'mercury', 'Planets', 'Mercury', '', 'Prograde', 'Retrograde', '', '', '', ''),
|
||||||
|
|
||||||
|
# ── Inner Rings ────────────────────────────────────────────────────────
|
||||||
|
# ⚠ Polestar levity/gravity qualifiers not confirmed from image
|
||||||
|
('wanderer-1-polestar', 45, 'The Polestar', 'the-polestar', 'Inner Rings', 'Star', '', '', '', '', '', '', ''),
|
||||||
|
('wanderer-2-antichthon',46, 'The Antichthon', 'the-antichthon', 'Inner Rings', 'Moon', '', 'Waxing', 'Waning', '', '', '', ''),
|
||||||
|
# Corestar: user changed Ascendant→Inclining, Descendent→Declining
|
||||||
|
('wanderer-3-corestar', 47, 'The Corestar', 'the-corestar', 'Inner Rings', 'Sun', '', 'Inclining', 'Declining', '', '', '', ''),
|
||||||
|
|
||||||
|
# ── Polarity-split finals ──────────────────────────────────────────────
|
||||||
|
# 48: The Eagle → Father Sky (levity) / Mother Sea (gravity)
|
||||||
|
('the-eagle', 48, 'Father Sky / Mother Sea',
|
||||||
|
'father-sky-mother-sea', '',
|
||||||
|
'World', '',
|
||||||
|
'', '',
|
||||||
|
'Father Sky', 'Mother Sea',
|
||||||
|
'The Storm', 'The Flood'),
|
||||||
|
|
||||||
|
# 49: The Mould of Man → Effulgent Mould of Man (levity) / Devouring Eagle (gravity)
|
||||||
|
('the-mould-of-man', 49, 'The Effulgent Mould of Man / The Devouring Eagle',
|
||||||
|
'effulgent-mould-devouring-eagle', '',
|
||||||
|
'Trumpets', '',
|
||||||
|
'', '',
|
||||||
|
'The Effulgent Mould of Man', 'The Devouring Eagle',
|
||||||
|
'', ''),
|
||||||
|
]
|
||||||
|
|
||||||
|
_DELETE_SLUGS = [
|
||||||
|
'wheel-of-fortune-em', # old #10 — correspondence absorbed by Fantasia Celestia
|
||||||
|
'the-junkboat', # old #11 — Space element takes the Chariot correspondence
|
||||||
|
'the-great-hunt', # old #14 — Disco Inferno takes the Devil correspondence
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def reseed_earthman_majors(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 # no-op in fresh DB without seeded variants
|
||||||
|
|
||||||
|
# 1. Delete retired cards
|
||||||
|
TarotCard.objects.filter(deck_variant=earthman, slug__in=_DELETE_SLUGS).delete()
|
||||||
|
|
||||||
|
# 2. Update existing cards — set new number to a temporary negative to
|
||||||
|
# avoid any incidental ordering conflicts mid-loop, then finalise.
|
||||||
|
# (No unique constraint on number, so straightforward.)
|
||||||
|
for row in _UPDATES:
|
||||||
|
(cur_slug, new_num, new_name, new_slug, new_group,
|
||||||
|
corr, reversal, lq, gq, le, ge, lr, gr) = row
|
||||||
|
TarotCard.objects.filter(
|
||||||
|
deck_variant=earthman, slug=cur_slug,
|
||||||
|
).update(
|
||||||
|
number=new_num,
|
||||||
|
name=new_name,
|
||||||
|
slug=new_slug,
|
||||||
|
group=new_group,
|
||||||
|
correspondence=corr,
|
||||||
|
reversal=reversal,
|
||||||
|
levity_qualifier=lq,
|
||||||
|
gravity_qualifier=gq,
|
||||||
|
levity_emanation=le,
|
||||||
|
gravity_emanation=ge,
|
||||||
|
levity_reversal=lr,
|
||||||
|
gravity_reversal=gr,
|
||||||
|
)
|
||||||
|
|
||||||
|
# 3. Insert The Asteroid Belt (new card, no existing equivalent)
|
||||||
|
TarotCard.objects.get_or_create(
|
||||||
|
deck_variant=earthman,
|
||||||
|
slug='the-asteroid-belt',
|
||||||
|
defaults=dict(
|
||||||
|
number=41,
|
||||||
|
name='The Asteroid Belt',
|
||||||
|
arcana='MAJOR',
|
||||||
|
group='Planets',
|
||||||
|
correspondence='',
|
||||||
|
reversal='Ouroboros',
|
||||||
|
levity_qualifier='Prograde',
|
||||||
|
gravity_qualifier='Retrograde',
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def reverse_reseed(apps, schema_editor):
|
||||||
|
pass # irreversible structural change
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('epic', '0035_earthman_deck_new_fields'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RunPython(reseed_earthman_majors, reverse_reseed),
|
||||||
|
]
|
||||||
24
src/apps/epic/migrations/0037_polestar_qualifiers.py
Normal file
24
src/apps/epic/migrations/0037_polestar_qualifiers.py
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
def set_polestar_qualifiers(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, slug='the-polestar',
|
||||||
|
).update(levity_qualifier='Precessional', gravity_qualifier='Recessional')
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('epic', '0036_earthman_deck_reseed'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RunPython(set_polestar_qualifiers, migrations.RunPython.noop),
|
||||||
|
]
|
||||||
60
src/apps/epic/migrations/0038_fix_correspondences.py
Normal file
60
src/apps/epic/migrations/0038_fix_correspondences.py
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
"""
|
||||||
|
Fix TarotCard.correspondence values for Earthman major arcana to store
|
||||||
|
the Fiorentine/Tarot card name (1:1 correspondence) rather than Earthman
|
||||||
|
conceptual terms.
|
||||||
|
|
||||||
|
Two batches corrected:
|
||||||
|
1. Pope/Horseman (1-5): Territoriality/Despotism/Capitalism/Fascism
|
||||||
|
→ Magician/High Priestess/Empress/Emperor/Hierophant
|
||||||
|
2. Zodiac (22-33): House of Self/Worth/… → Aries/Taurus/…/Pisces
|
||||||
|
(Minchiate Fiorentine has all 12 zodiac sign cards; correspondence = sign name)
|
||||||
|
"""
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
_POPE_FIXES = {
|
||||||
|
'the-schizo': 'Magician',
|
||||||
|
'the-occultist': 'High Priestess',
|
||||||
|
'the-despot': 'Empress',
|
||||||
|
'the-capitalist': 'Emperor',
|
||||||
|
'the-fascist': 'Hierophant',
|
||||||
|
}
|
||||||
|
|
||||||
|
_ZODIAC_FIXES = {
|
||||||
|
'aries': 'Aries',
|
||||||
|
'taurus': 'Taurus',
|
||||||
|
'gemini': 'Gemini',
|
||||||
|
'cancer': 'Cancer',
|
||||||
|
'leo': 'Leo',
|
||||||
|
'virgo': 'Virgo',
|
||||||
|
'libra': 'Libra',
|
||||||
|
'scorpio': 'Scorpio',
|
||||||
|
'sagittarius': 'Sagittarius',
|
||||||
|
'capricorn': 'Capricorn',
|
||||||
|
'aquarius': 'Aquarius',
|
||||||
|
'pisces': 'Pisces',
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def fix_correspondences(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
|
||||||
|
for slug, corr in {**_POPE_FIXES, **_ZODIAC_FIXES}.items():
|
||||||
|
TarotCard.objects.filter(deck_variant=earthman, slug=slug).update(
|
||||||
|
correspondence=corr,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('epic', '0037_polestar_qualifiers'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RunPython(fix_correspondences, migrations.RunPython.noop),
|
||||||
|
]
|
||||||
59
src/apps/epic/migrations/0039_fix_reversals.py
Normal file
59
src/apps/epic/migrations/0039_fix_reversals.py
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
"""
|
||||||
|
Fix TarotCard.reversal values for Pope/Horseman and Zodiac cards.
|
||||||
|
|
||||||
|
Pope/Horseman (1-5): reversed-state names read from image reversal column.
|
||||||
|
⚠ Cards 1-2 (Schizo/Occultist): image showed 'Territoriality*' spanning
|
||||||
|
rows 1-2 — stored as 'Territoriality' for both; user to confirm if
|
||||||
|
they have distinct reversed names.
|
||||||
|
|
||||||
|
Zodiac (22-33): reversed state = corresponding house (e.g. Aries → House of Self).
|
||||||
|
"""
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
_POPE_REVERSALS = {
|
||||||
|
'the-schizo': 'Territoriality', # ⚠ confirm if distinct from Occultist
|
||||||
|
'the-occultist': 'Territoriality', # ⚠ confirm
|
||||||
|
'the-despot': 'Despotism',
|
||||||
|
'the-capitalist': 'Capitalism',
|
||||||
|
'the-fascist': 'Fascism',
|
||||||
|
}
|
||||||
|
|
||||||
|
_ZODIAC_REVERSALS = {
|
||||||
|
'aries': 'House of Self',
|
||||||
|
'taurus': 'House of Worth',
|
||||||
|
'gemini': 'House of Education',
|
||||||
|
'cancer': 'House of Family',
|
||||||
|
'leo': 'House of Creation',
|
||||||
|
'virgo': 'House of Ritual',
|
||||||
|
'libra': 'House of Cooperation',
|
||||||
|
'scorpio': 'House of Regeneration',
|
||||||
|
'sagittarius': 'House of Enterprise',
|
||||||
|
'capricorn': 'House of Career',
|
||||||
|
'aquarius': 'House of Reward',
|
||||||
|
'pisces': 'House of Reprisal',
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def fix_reversals(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
|
||||||
|
for slug, reversal in {**_POPE_REVERSALS, **_ZODIAC_REVERSALS}.items():
|
||||||
|
TarotCard.objects.filter(
|
||||||
|
deck_variant=earthman, slug=slug,
|
||||||
|
).update(reversal=reversal)
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('epic', '0038_fix_correspondences'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RunPython(fix_reversals, migrations.RunPython.noop),
|
||||||
|
]
|
||||||
24
src/apps/epic/migrations/0040_rename_jovent_to_intent.py
Normal file
24
src/apps/epic/migrations/0040_rename_jovent_to_intent.py
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
def rename_jovent_to_intent(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, slug='jovent',
|
||||||
|
).update(name='Intent', slug='intent')
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('epic', '0039_fix_reversals'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RunPython(rename_jovent_to_intent, migrations.RunPython.noop),
|
||||||
|
]
|
||||||
@@ -242,10 +242,19 @@ class TarotCard(models.Model):
|
|||||||
arcana = models.CharField(max_length=6, choices=ARCANA_CHOICES)
|
arcana = models.CharField(max_length=6, choices=ARCANA_CHOICES)
|
||||||
suit = models.CharField(max_length=10, choices=SUIT_CHOICES, null=True, blank=True)
|
suit = models.CharField(max_length=10, choices=SUIT_CHOICES, null=True, blank=True)
|
||||||
icon = models.CharField(max_length=50, blank=True, default='') # FA icon override (e.g. major arcana)
|
icon = models.CharField(max_length=50, blank=True, default='') # FA icon override (e.g. major arcana)
|
||||||
number = models.IntegerField() # 0–21 major (Fiorentine); 0–51 major (Earthman); 1–14 minor
|
number = models.IntegerField() # 0–21 major (Fiorentine); 0–49 major (Earthman); 1–14 minor
|
||||||
slug = models.SlugField(max_length=120)
|
slug = models.SlugField(max_length=120)
|
||||||
correspondence = models.CharField(max_length=200, blank=True) # standard / Italian equivalent
|
correspondence = models.CharField(max_length=200, blank=True) # Tarot / Minchiate equivalent
|
||||||
group = models.CharField(max_length=100, blank=True) # Earthman major grouping
|
group = models.CharField(max_length=100, blank=True) # Earthman major grouping
|
||||||
|
reversal = models.CharField(max_length=200, blank=True, default='') # reversed-state title; blank = same as name
|
||||||
|
levity_qualifier = models.CharField(max_length=100, blank=True, default='')
|
||||||
|
gravity_qualifier = models.CharField(max_length=100, blank=True, default='')
|
||||||
|
levity_emanation = models.CharField(max_length=200, blank=True, default='') # polarity-split upright (cards 48-49)
|
||||||
|
gravity_emanation = models.CharField(max_length=200, blank=True, default='')
|
||||||
|
levity_reversal = models.CharField(max_length=200, blank=True, default='') # polarity-split reversal (card 48)
|
||||||
|
gravity_reversal = models.CharField(max_length=200, blank=True, default='')
|
||||||
|
mechanisms = models.JSONField(default=list) # list of dicts; in-game effects
|
||||||
|
articulations = models.JSONField(default=list) # list of dicts; combinatory effects
|
||||||
keywords_upright = models.JSONField(default=list)
|
keywords_upright = models.JSONField(default=list)
|
||||||
keywords_reversed = models.JSONField(default=list)
|
keywords_reversed = models.JSONField(default=list)
|
||||||
cautions = models.JSONField(default=list)
|
cautions = models.JSONField(default=list)
|
||||||
@@ -274,6 +283,24 @@ class TarotCard(models.Model):
|
|||||||
court = {11: 'M', 12: 'J', 13: 'Q', 14: 'K'}
|
court = {11: 'M', 12: 'J', 13: 'Q', 14: 'K'}
|
||||||
return court.get(self.number, str(self.number))
|
return court.get(self.number, str(self.number))
|
||||||
|
|
||||||
|
def emanation_for(self, polarity):
|
||||||
|
"""Return the upright title for a given polarity ('levity' or 'gravity').
|
||||||
|
Falls back to name for cards without a polarity split."""
|
||||||
|
if polarity == 'levity' and self.levity_emanation:
|
||||||
|
return self.levity_emanation
|
||||||
|
if polarity == 'gravity' and self.gravity_emanation:
|
||||||
|
return self.gravity_emanation
|
||||||
|
return self.name
|
||||||
|
|
||||||
|
def reversal_for(self, polarity):
|
||||||
|
"""Return the reversed title for a given polarity.
|
||||||
|
Falls back to reversal (blank = same as emanation_for)."""
|
||||||
|
if polarity == 'levity' and self.levity_reversal:
|
||||||
|
return self.levity_reversal
|
||||||
|
if polarity == 'gravity' and self.gravity_reversal:
|
||||||
|
return self.gravity_reversal
|
||||||
|
return self.reversal or self.emanation_for(polarity)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def name_group(self):
|
def name_group(self):
|
||||||
"""Returns 'Group N:' prefix if the name contains ': ', else ''."""
|
"""Returns 'Group N:' prefix if the name contains ': ', else ''."""
|
||||||
|
|||||||
Reference in New Issue
Block a user