PICK SKY overlay: D3 natal wheel, Character model, PySwiss aspects+tz
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>
This commit is contained in:
106
src/apps/epic/migrations/0033_seed_astro_reference_tables.py
Normal file
106
src/apps/epic/migrations/0033_seed_astro_reference_tables.py
Normal file
@@ -0,0 +1,106 @@
|
||||
"""
|
||||
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),
|
||||
]
|
||||
Reference in New Issue
Block a user