Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017eEAG8HD3mm8gsKXgk8uU8
63 lines
2.8 KiB
Python
63 lines
2.8 KiB
Python
#!/usr/bin/env python3
|
|
"""Sidereal MC for `/api/bhava_chalit` (BUG-1077).
|
|
|
|
Sripati and Porphyry trisect the quadrants between the ascendant and the MC.
|
|
The endpoint used to default a missing `mc_lon` to `asc + 270`, which makes
|
|
every quadrant exactly 90 degrees: the chart silently became equal houses and
|
|
no planet could ever change house. This module is the only place the endpoint
|
|
gets an MC from:
|
|
|
|
1. an explicit `mc_lon` in the body wins (existing callers and tests);
|
|
2. otherwise, with the birth moment and place in the body, the MC comes from
|
|
Swiss Ephemeris `houses_ex` in the sidereal frame of the request's ayanamsa,
|
|
the same call the full-reading producer uses
|
|
(`jyotish_engine._build_natal_foundation_modules`);
|
|
3. otherwise a house system that needs the MC is refused, never guessed.
|
|
|
|
House systems that do not read the MC keep the old placeholder, which only
|
|
appears as the echoed `mc_lon`.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import Any
|
|
|
|
from ayanamsa_utils import normalize_ayanamsa_name, temporary_ayanamsa
|
|
|
|
MC_HOUSE_SYSTEMS = frozenset({'sripati', 'porphyry'})
|
|
_BIRTH_KEYS = ('year', 'month', 'day', 'hour', 'lat', 'lon')
|
|
|
|
|
|
def _has_birth_moment(body: dict) -> bool:
|
|
return all(body.get(key) is not None for key in _BIRTH_KEYS)
|
|
|
|
|
|
def sidereal_mc_from_birth(handler: Any, body: dict) -> float:
|
|
"""MC longitude in the request's sidereal frame, from the birth moment."""
|
|
import swisseph as swe
|
|
|
|
year = handler._get_int(body, 'year', 1990, 1800, 2400)
|
|
month = handler._get_int(body, 'month', 6, 1, 12)
|
|
day = handler._get_int(body, 'day', 15, 1, 31)
|
|
hour = handler._get_float(body, 'hour', 12, 0, 23)
|
|
minute = handler._get_float(body, 'minute', 0, 0, 59)
|
|
second = handler._get_birth_second(body)
|
|
lat = handler._get_float(body, 'lat', 0, -90, 90)
|
|
lon = handler._get_float(body, 'lon', 0, -180, 180)
|
|
tz = handler._parse_timezone(body, lat, lon, year, month, day, hour, minute, second)
|
|
jd = swe.julday(year, month, day, handler._birth_hour_decimal(hour, minute, second) - tz)
|
|
name = normalize_ayanamsa_name(body.get('ayanamsa') or body.get('ayanamsa_name'))
|
|
with temporary_ayanamsa(name, swe):
|
|
_, ascmc = swe.houses_ex(jd, lat, lon, b'R', swe.FLG_SWIEPH | swe.FLG_SIDEREAL)
|
|
return float(ascmc[1]) % 360
|
|
|
|
|
|
def resolve_bhava_mc_lon(handler: Any, body: dict, house_system: str, asc_lon: float, bad_request: type[Exception]) -> float:
|
|
if body.get('mc_lon') is not None:
|
|
return handler._normalize_degree(body, 'mc_lon', 0)
|
|
if _has_birth_moment(body):
|
|
return sidereal_mc_from_birth(handler, body)
|
|
if house_system in MC_HOUSE_SYSTEMS:
|
|
raise bad_request(f'{house_system} needs mc_lon or the birth date, time and place (year, month, day, hour, lat, lon)')
|
|
return (asc_lon + 270) % 360
|