fix(chart): Sripati Bhava from the real sidereal MC; Bhava chart and tables (T5, BUG-1077)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017eEAG8HD3mm8gsKXgk8uU8
This commit is contained in:
Jesse_Chen
2026-09-28 15:38:24 +08:00
co-authored by Claude Opus 5.5
parent 3f6bcb081a
commit c35e0195e1
16 changed files with 743 additions and 9 deletions
+52
View File
@@ -3281,6 +3281,58 @@ def test_bhava_chalit_endpoint_exposes_user_selected_house_systems() -> None:
assert result['result']['calculation_note']
def _golden_natal_body() -> dict:
"""Real engine natal packet the chart page uses (fictional 1990-06-15 12:00 Beijing, Raman, mean node)."""
golden = json.loads((Path(__file__).resolve().parents[1] / 'frontend/tests/fixtures/chart-view-golden.json').read_text(encoding='utf-8'))
chart = golden['chart']
return {
'planets': {name: {'lon': row['lon']} for name, row in chart['planets'].items()},
'ascendant': {'lon': chart['ascendant']['lon']},
'year': 1990, 'month': 6, 'day': 15, 'hour': 12, 'minute': 0, 'second': 0,
'lat': 39.9042, 'lon': 116.4074, 'tz': 8,
'ayanamsa': 'raman',
'house_system': 'sripati',
'mode': 'compare',
}
def test_bhava_chalit_sripati_without_mc_or_birth_moment_is_rejected_not_equal_houses() -> None:
"""BUG-1077: a missing MC used to default to asc+270, silently turning Sripati into equal houses."""
body = _golden_natal_body()
for key in ('year', 'month', 'day', 'hour', 'minute', 'second', 'lat', 'lon', 'tz'):
body.pop(key)
with pytest.raises(BadRequest, match='mc_lon'):
_handler()._compute_bhava_chalit(body)
def test_bhava_chalit_sripati_derives_the_sidereal_mc_from_the_birth_moment() -> None:
import swisseph as swe
from ayanamsa_utils import temporary_ayanamsa
body = _golden_natal_body()
result = _handler()._compute_bhava_chalit(body)['result']
spans = [house['span_degrees'] for house in result['boundaries']['houses']]
assert len(spans) == 12
# Before BUG-1077 every span was exactly 30.0 (equal houses).
assert any(abs(span - 30.0) > 0.1 for span in spans), spans
jd = swe.julday(1990, 6, 15, 12 - 8)
with temporary_ayanamsa('raman', swe):
_, ascmc = swe.houses_ex(jd, 39.9042, 116.4074, b'R', swe.FLG_SWIEPH | swe.FLG_SIDEREAL)
assert abs(result['mc_lon'] - ascmc[1] % 360) < 1e-3
# Same frame as the natal chart: the ascendant from that call matches the engine's.
assert abs(ascmc[0] % 360 - body['ascendant']['lon']) < 0.01
def test_bhava_chalit_non_mc_house_systems_keep_working_without_birth_moment() -> None:
body = _golden_natal_body()
for key in ('year', 'month', 'day', 'hour', 'minute', 'second', 'lat', 'lon', 'tz'):
body.pop(key)
result = _handler()._compute_bhava_chalit({**body, 'house_system': 'equal'})
assert result['success'] is True
assert [house['span_degrees'] for house in result['result']['boundaries']['houses']] == [30.0] * 12
def test_sudarshana_endpoint_returns_three_reference_points() -> None:
handler = _handler()
result = handler._compute_sudarshana({