rename natus → sky across the codebase — natal chart abstraction is now sky throughout, since chart inputs aren't birthday-gated
Some checks failed
ci/woodpecker/push/pyswiss Pipeline was successful
ci/woodpecker/push/main Pipeline failed

Mechanical rename: 5 files (sky-wheel.js, _sky.scss, _sky_overlay.html, SkyWheelSpec.js x2), 24 in-place edits across templates/views/urls/SCSS/JS/tests/CLAUDE.md. URL names epic:natus_save → epic:sky_save (epic namespaced, no clash w. dashboard:sky_save), JS module NatusWheel → SkyWheel, DOM ids id_natus_* → id_sky_*, BEM classes natus-* → sky-*, dashboard sky_natus_data/sky_natus_preview collapsed to sky_data/sky_preview_data. No DB migration needed (User.sky_chart_data + GameEvent.SKY_SAVED already used sky-prefix). 778 ITs + Jasmine green.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Disco DeDisco
2026-05-04 20:36:15 -04:00
parent 19b7828ea9
commit cc2a3f3526
29 changed files with 338 additions and 338 deletions

View File

@@ -619,13 +619,13 @@ class SkySaveViewTest(TestCase):
self.assertAlmostEqual(self.user.sky_chart_data["houses"]["asc"], 123.4)
class SkyNatusDataViewTest(TestCase):
class SkyDataViewTest(TestCase):
def setUp(self):
self.user = User.objects.create(email="disco@test.io")
self.client.force_login(self.user)
def test_returns_stored_chart_with_asc_preserved(self):
"""sky_natus_data returns sky_chart_data — asc must match what was saved."""
"""sky_data returns sky_chart_data — asc must match what was saved."""
stored = {
"planets": {},
"houses": {"cusps": [float(i * 30) for i in range(12)], "asc": 236.1, "mc": 159.1},

View File

@@ -17,6 +17,6 @@ urlpatterns = [
path('sky/', views.sky_view, name='sky'),
path('sky/preview', views.sky_preview, name='sky_preview'),
path('sky/save', views.sky_save, name='sky_save'),
path('sky/data', views.sky_natus_data, name='sky_natus_data'),
path('sky/data', views.sky_data, name='sky_data'),
path('set-pronouns', views.set_pronouns, name='set_pronouns'),
]

View File

@@ -287,7 +287,7 @@ def save_payment_method(request):
# ── My Sky (personal natal chart) ────────────────────────────────────────────
def _sky_natus_preview(request):
def _sky_preview_data(request):
"""Shared preview logic — proxies to PySwiss, no DB writes."""
date_str = request.GET.get('date')
time_str = request.GET.get('time', '12:00')
@@ -380,7 +380,7 @@ def sky_view(request):
@login_required(login_url="/")
def sky_preview(request):
return _sky_natus_preview(request)
return _sky_preview_data(request)
@login_required(login_url="/")
@@ -440,7 +440,7 @@ def sky_save(request):
@login_required(login_url="/")
def sky_natus_data(request):
def sky_data(request):
user = request.user
if not user.sky_chart_data:
return HttpResponse(status=404)

View File

@@ -653,7 +653,7 @@ class Character(models.Model):
on_delete=models.SET_NULL, related_name='character_significators',
)
# ── natus input (what the gamer entered) ─────────────────────────────
# ── sky input (what the gamer entered) ─────────────────────────────
birth_dt = models.DateTimeField(null=True, blank=True) # UTC
birth_lat = models.DecimalField(max_digits=9, decimal_places=6, null=True, blank=True)
birth_lon = models.DecimalField(max_digits=9, decimal_places=6, null=True, blank=True)
@@ -662,7 +662,7 @@ class Character(models.Model):
max_length=1, choices=HOUSE_SYSTEM_CHOICES, default=PORPHYRY,
)
# ── computed natus snapshot (full PySwiss response) ───────────────────
# ── computed sky snapshot (full PySwiss response) ───────────────────
chart_data = models.JSONField(null=True, blank=True)
# ── celtic cross spread (added at PICK SEA) ───────────────────────────

View File

@@ -1933,16 +1933,16 @@ class SelectSigViewTest(TestCase):
self.assertEqual(response.status_code, 400)
# ── natus_preview (epic) ──────────────────────────────────────────────────────
# ── sky_preview (epic) ──────────────────────────────────────────────────────
class NatusPreviewViewTest(TestCase):
class SkyPreviewViewTest(TestCase):
def setUp(self):
self.user = User.objects.create(email="pc@natus.io")
self.user = User.objects.create(email="pc@sky.io")
self.client.force_login(self.user)
self.room, _ = _make_sig_room(self.user)
self.room.table_status = Room.SKY_SELECT
self.room.save()
self.url = reverse("epic:natus_preview", kwargs={"room_id": self.room.id})
self.url = reverse("epic:sky_preview", kwargs={"room_id": self.room.id})
def test_missing_params_returns_400(self):
response = self.client.get(self.url, {"date": "1990-06-15"})
@@ -2025,16 +2025,16 @@ class TarotDealViewTest(TestCase):
)
# ── natus_save (epic) ─────────────────────────────────────────────────────────
# ── sky_save (epic) ─────────────────────────────────────────────────────────
class NatusSaveViewTest(TestCase):
class SkySaveViewTest(TestCase):
def setUp(self):
self.user = User.objects.create(email="pc@natussave.io")
self.user = User.objects.create(email="pc@skysave.io")
self.client.force_login(self.user)
self.room, _ = _make_sig_room(self.user)
self.room.table_status = Room.SKY_SELECT
self.room.save()
self.url = reverse("epic:natus_save", kwargs={"room_id": self.room.id})
self.url = reverse("epic:sky_save", kwargs={"room_id": self.room.id})
def _post(self, payload):
import json as _json
@@ -2134,7 +2134,7 @@ class NatusSaveViewTest(TestCase):
def test_confirm_with_dict_shaped_elements_extracts_count(self):
"""Some chart payloads enrich each element to {count, contributors};
natus_save should read .count rather than treating the dict as a value."""
sky_save should read .count rather than treating the dict as a value."""
from apps.drama.models import GameEvent
chart = {
"elements": {
@@ -2156,7 +2156,7 @@ class NatusSaveViewTest(TestCase):
self.assertEqual(event.data.get("top_capacitors"), ["Ardor"])
def test_confirm_copies_seat_significator_to_character(self):
"""natus_save with action=confirm copies seat.significator onto Character."""
"""sky_save with action=confirm copies seat.significator onto Character."""
earthman, _ = DeckVariant.objects.get_or_create(
slug="earthman", defaults={"name": "Earthman Deck", "card_count": 108}
)

View File

@@ -25,8 +25,8 @@ urlpatterns = [
path('room/<uuid:room_id>/abandon', views.abandon_room, name='abandon_room'),
path('room/<uuid:room_id>/tarot/', views.tarot_deck, name='tarot_deck'),
path('room/<uuid:room_id>/tarot/deal', views.tarot_deal, name='tarot_deal'),
path('room/<uuid:room_id>/natus/preview', views.natus_preview, name='natus_preview'),
path('room/<uuid:room_id>/natus/save', views.natus_save, name='natus_save'),
path('room/<uuid:room_id>/sky/preview', views.sky_preview, name='sky_preview'),
path('room/<uuid:room_id>/sky/save', views.sky_save, name='sky_save'),
path('room/<uuid:room_id>/sea/partial', views.sea_partial, name='sea_partial'),
path('room/<uuid:room_id>/sea/deck', views.sea_deck, name='sea_deck'),
]

View File

@@ -25,7 +25,7 @@ def stack_reversal_probability(user=None, room=None):
# Element key → in-game capacitor name (mirrors ELEMENT_INFO in natus-wheel.js).
# Element key → in-game capacitor name (mirrors ELEMENT_INFO in sky-wheel.js).
# Used by the SKY_SAVED provenance event to render prose like
# "yields them a unique Ardor capacity."
ELEMENT_CAPACITOR_NAMES = {

View File

@@ -975,10 +975,10 @@ def tarot_deal(request, room_id):
})
# ── Natus (natal chart) ───────────────────────────────────────────────────────
# ── Sky (natal chart) ───────────────────────────────────────────────────────
@login_required
def natus_preview(request, room_id):
def sky_preview(request, room_id):
"""Proxy GET to PySwiss /api/chart/ and augment with distinction counts.
Query params:
@@ -1064,7 +1064,7 @@ def natus_preview(request, room_id):
@login_required
def natus_save(request, room_id):
def sky_save(request, room_id):
"""Create or update the draft Character for the requesting gamer's seat.
POST body (JSON):

View File

@@ -1,12 +1,12 @@
/**
* natus-wheel.js Self-contained D3 natal-chart module.
* sky-wheel.js Self-contained D3 natal-chart module.
*
* Public API:
* NatusWheel.draw(svgEl, data) first render
* NatusWheel.redraw(data) live update (same SVG)
* NatusWheel.clear() empty the SVG
* SkyWheel.draw(svgEl, data) first render
* SkyWheel.redraw(data) live update (same SVG)
* SkyWheel.clear() empty the SVG
*
* `data` shape matches the /epic/natus/preview/ proxy response:
* `data` shape matches the /epic/sky/preview/ proxy response:
* {
* planets: { Sun: { sign, degree, speed, retrograde }, },
* houses: { cusps: [f×12], asc: f, mc: f },
@@ -28,7 +28,7 @@
* already defined in the page; falls back to neutral colours if absent.
*/
const NatusWheel = (() => {
const SkyWheel = (() => {
'use strict';
// ── Constants ──────────────────────────────────────────────────────────────
@@ -223,8 +223,8 @@ const NatusWheel = (() => {
if (_staticBase) return _staticBase;
const scripts = document.querySelectorAll('script[src]');
for (const s of scripts) {
if (s.src.includes('natus-wheel')) {
_staticBase = s.src.replace(/natus-wheel\.js.*$/, '');
if (s.src.includes('sky-wheel')) {
_staticBase = s.src.replace(/sky-wheel\.js.*$/, '');
return _staticBase;
}
}
@@ -957,7 +957,7 @@ const NatusWheel = (() => {
* Called on every draw() so a fresh innerHTML replaces any stale state.
*/
function _injectTooltipControls() {
_tooltipEl = document.getElementById('id_natus_tooltip');
_tooltipEl = document.getElementById('id_sky_tooltip');
if (!_tooltipEl) return;
_tooltipEl.innerHTML =
`<button type="button" class="btn btn-equip nw-asp-don">DON</button>` +
@@ -1384,8 +1384,8 @@ const NatusWheel = (() => {
(() => {
const scripts = document.querySelectorAll('script[src]');
for (const s of scripts) {
if (s.src.includes('natus-wheel')) {
return s.src.replace(/natus-wheel\.js.*$/, 'icons/zodiac-signs/');
if (s.src.includes('sky-wheel')) {
return s.src.replace(/sky-wheel\.js.*$/, 'icons/zodiac-signs/');
}
}
return '/static/apps/gameboard/icons/zodiac-signs/';
@@ -1394,7 +1394,7 @@ const NatusWheel = (() => {
await Promise.all(SIGNS.map(async sign => {
const url = base + sign.name.toLowerCase() + '.svg';
const resp = await window.fetch(url);
if (!resp.ok) { console.warn(`NatusWheel: failed to load ${url}`); return; }
if (!resp.ok) { console.warn(`SkyWheel: failed to load ${url}`); return; }
const text = await resp.text();
const doc = new DOMParser().parseFromString(text, 'image/svg+xml');
const path = doc.querySelector('path');