Files
Jyotisha/scripts/bhava_chalit_mc.py
T

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