PySwiss: - calculate_aspects() in calc.py (conjunction/sextile/square/trine/opposition with orbs) - /api/tz/ endpoint (timezonefinder lat/lon → IANA timezone) - aspects included in /api/chart/ response - timezonefinder==8.2.2 added to requirements - 14 new unit tests (test_calc.py) + 12 new integration tests (TimezoneApiTest, aspect fields) Main app: - Sign, Planet, AspectType, HouseLabel reference models + seeded migrations (0032–0033) - Character model with birth_dt/lat/lon/place, house_system, chart_data, celtic_cross, confirmed_at/retired_at lifecycle (migration 0034) - natus_preview proxy view: calls PySwiss /api/chart/ + optional /api/tz/ auto-resolution, computes planet-in-house distinctions, returns enriched JSON - natus_save view: find-or-create draft Character, confirmed_at on action='confirm' - natus-wheel.js: D3 v7 SVG natal wheel (elements pie, signs, houses, planets, aspects, ASC/MC axes); NatusWheel.draw() / redraw() / clear() - _natus_overlay.html: Nominatim place autocomplete (debounced 400ms), geolocation button with reverse-geocode city name, live chart preview (debounced 300ms), tz auto-fill, NVM / SAVE SKY footer; html.natus-open class toggle pattern - _natus.scss: Gaussian backdrop+modal, two-column form|wheel layout, suggestion dropdown, portrait collapse at 600px, landscape sidebar z-index sink - room.html: include overlay when table_status == SKY_SELECT Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
107 lines
4.5 KiB
Python
107 lines
4.5 KiB
Python
"""
|
|
Data migration: seed Sign, Planet, AspectType, and HouseLabel tables.
|
|
|
|
These are stable astrological reference rows — never user-edited.
|
|
The data matches the constants in pyswiss/apps/charts/calc.py so that
|
|
the proxy view and D3 wheel share a single source of truth.
|
|
"""
|
|
from django.db import migrations
|
|
|
|
|
|
# ── Signs ────────────────────────────────────────────────────────────────────
|
|
# (order, name, symbol, element, modality, start_degree)
|
|
SIGNS = [
|
|
(0, 'Aries', '♈', 'Fire', 'Cardinal', 0.0),
|
|
(1, 'Taurus', '♉', 'Earth', 'Fixed', 30.0),
|
|
(2, 'Gemini', '♊', 'Air', 'Mutable', 60.0),
|
|
(3, 'Cancer', '♋', 'Water', 'Cardinal', 90.0),
|
|
(4, 'Leo', '♌', 'Fire', 'Fixed', 120.0),
|
|
(5, 'Virgo', '♍', 'Earth', 'Mutable', 150.0),
|
|
(6, 'Libra', '♎', 'Air', 'Cardinal', 180.0),
|
|
(7, 'Scorpio', '♏', 'Water', 'Fixed', 210.0),
|
|
(8, 'Sagittarius', '♐', 'Fire', 'Mutable', 240.0),
|
|
(9, 'Capricorn', '♑', 'Earth', 'Cardinal', 270.0),
|
|
(10, 'Aquarius', '♒', 'Air', 'Fixed', 300.0),
|
|
(11, 'Pisces', '♓', 'Water', 'Mutable', 330.0),
|
|
]
|
|
|
|
# ── Planets ───────────────────────────────────────────────────────────────────
|
|
# (order, name, symbol)
|
|
PLANETS = [
|
|
(0, 'Sun', '☉'),
|
|
(1, 'Moon', '☽'),
|
|
(2, 'Mercury', '☿'),
|
|
(3, 'Venus', '♀'),
|
|
(4, 'Mars', '♂'),
|
|
(5, 'Jupiter', '♃'),
|
|
(6, 'Saturn', '♄'),
|
|
(7, 'Uranus', '♅'),
|
|
(8, 'Neptune', '♆'),
|
|
(9, 'Pluto', '♇'),
|
|
]
|
|
|
|
# ── Aspect types ──────────────────────────────────────────────────────────────
|
|
# (name, symbol, angle, orb) — mirrors ASPECTS constant in pyswiss calc.py
|
|
ASPECT_TYPES = [
|
|
('Conjunction', '☌', 0, 8.0),
|
|
('Sextile', '⚹', 60, 6.0),
|
|
('Square', '□', 90, 8.0),
|
|
('Trine', '△', 120, 8.0),
|
|
('Opposition', '☍', 180, 10.0),
|
|
]
|
|
|
|
# ── House labels (distinctions) ───────────────────────────────────────────────
|
|
# (number, name, keywords)
|
|
HOUSE_LABELS = [
|
|
(1, 'Self', 'identity, appearance, first impressions'),
|
|
(2, 'Worth', 'possessions, values, finances'),
|
|
(3, 'Education', 'communication, siblings, short journeys'),
|
|
(4, 'Family', 'home, roots, ancestry'),
|
|
(5, 'Creation', 'creativity, romance, children, pleasure'),
|
|
(6, 'Ritual', 'service, health, daily routines'),
|
|
(7, 'Cooperation', 'partnerships, marriage, open enemies'),
|
|
(8, 'Regeneration', 'transformation, shared resources, death'),
|
|
(9, 'Enterprise', 'philosophy, travel, higher learning'),
|
|
(10, 'Career', 'public life, reputation, authority'),
|
|
(11, 'Reward', 'friends, groups, aspirations'),
|
|
(12, 'Reprisal', 'hidden matters, karma, self-undoing'),
|
|
]
|
|
|
|
|
|
def forward(apps, schema_editor):
|
|
Sign = apps.get_model('epic', 'Sign')
|
|
Planet = apps.get_model('epic', 'Planet')
|
|
AspectType = apps.get_model('epic', 'AspectType')
|
|
HouseLabel = apps.get_model('epic', 'HouseLabel')
|
|
|
|
for order, name, symbol, element, modality, start_degree in SIGNS:
|
|
Sign.objects.create(
|
|
order=order, name=name, symbol=symbol,
|
|
element=element, modality=modality, start_degree=start_degree,
|
|
)
|
|
|
|
for order, name, symbol in PLANETS:
|
|
Planet.objects.create(order=order, name=name, symbol=symbol)
|
|
|
|
for name, symbol, angle, orb in ASPECT_TYPES:
|
|
AspectType.objects.create(name=name, symbol=symbol, angle=angle, orb=orb)
|
|
|
|
for number, name, keywords in HOUSE_LABELS:
|
|
HouseLabel.objects.create(number=number, name=name, keywords=keywords)
|
|
|
|
|
|
def reverse(apps, schema_editor):
|
|
for model_name in ('Sign', 'Planet', 'AspectType', 'HouseLabel'):
|
|
apps.get_model('epic', model_name).objects.all().delete()
|
|
|
|
|
|
class Migration(migrations.Migration):
|
|
|
|
dependencies = [
|
|
('epic', '0032_astro_reference_tables'),
|
|
]
|
|
|
|
operations = [
|
|
migrations.RunPython(forward, reverse_code=reverse),
|
|
]
|