#!/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