fix(special-lagnas): compute eight birth points without PyJHora
Independent Staging Quality Gate / validate (push) Successful in 19m4s
Independent Staging Quality Gate / publish (push) Successful in 3m51s

Production images do not install PyJHora, so those eight points were blocked.
Swiss Ephemeris now computes them in-process. Rectification keeps the old
Hora and Ghati formulas.

BUG-1273.
This commit is contained in:
jesse-ux
2026-10-08 15:01:44 +08:00
parent fe833b3576
commit 795b8845c7
16 changed files with 1270 additions and 245 deletions
@@ -0,0 +1,108 @@
#!/usr/bin/env python3
"""Freeze PyJHora 4.8.7 special-lagna longitudes. Offline only.
Run from the repo root, with a Python that has PyJHora 4.8.7 installed:
python scripts/research/freeze_special_lagnas_pyjhora_fixture.py
The runtime calculator does not import this module. Tests read the JSON.
"""
from __future__ import annotations
import importlib.metadata
import io
import json
import sys
from contextlib import redirect_stdout
from datetime import datetime
from pathlib import Path
ROOT = Path(__file__).resolve().parents[2]
sys.path[:0] = [str(ROOT), str(ROOT / "scripts")]
import swisseph as swe # noqa: E402
from jhora.horoscope.chart import charts # noqa: E402
from jhora.panchanga import drik # noqa: E402
from jhora.panchanga.drik import Place # noqa: E402
OUT = ROOT / "tests" / "fixtures" / "special_lagnas_pyjhora_487.json"
CASES = {
"steve_jobs_1955_aa": {
"source": "references/public_oracle_cases.json id steve_jobs_1955_aa",
"birth": dict(year=1955, month=2, day=24, hour=19, minute=15, second=0, lat=37.7749, lon=-122.4194, tz=-8),
},
"barack_obama_1961_aa": {
"source": "references/public_oracle_cases.json id barack_obama_1961_aa",
"birth": dict(year=1961, month=8, day=4, hour=19, minute=24, second=0, lat=21.3069, lon=-157.8583, tz=-10),
},
"fictional_reader_main": {
"source": "tests/test_report_reader_main.py FICTIONAL",
"birth": dict(year=1991, month=4, day=7, hour=9, minute=15, second=0, lat=30.5728, lon=104.0668, tz=8.0),
},
}
MODES = {"lahiri": "LAHIRI", "raman": "RAMAN"}
FUNCS = (
("Bhava_Lagna", "bhava_lagna"),
("Hora_Lagna", "hora_lagna"),
("Ghati_Lagna", "ghati_lagna"),
("ViGhati_Lagna", "vighati_lagna"),
("Sree_Lagna", "sree_lagna"),
("Indu_Lagna", "indu_lagna"),
("Pranapada_Lagna", "pranapada_lagna"),
)
SIGNS = [
"Aries", "Taurus", "Gemini", "Cancer", "Leo", "Virgo",
"Libra", "Scorpio", "Sagittarius", "Capricorn", "Aquarius", "Pisces",
]
def _point(raw) -> dict:
sign = int(raw[0]) % 12
degree = float(raw[1]) % 30.0
return {
"sign": SIGNS[sign],
"sign_index": sign,
"sign_degree": degree,
"longitude": sign * 30.0 + degree,
}
def _case(birth: dict, mode: str) -> dict:
local_hour = birth["hour"] + birth["minute"] / 60.0 + birth["second"] / 3600.0
with redirect_stdout(io.StringIO()):
drik.set_ayanamsa_mode(mode)
jd = swe.julday(birth["year"], birth["month"], birth["day"], local_hour)
place = Place("birth_place", birth["lat"], birth["lon"], birth["tz"])
points = {key: _point(getattr(drik, name)(jd, place)) for key, name in FUNCS}
raw = charts.varnada_lagna(
(birth["year"], birth["month"], birth["day"]),
(birth["hour"], birth["minute"], birth["second"]),
place,
varnada_method=1,
)
points["Varnada_Lagna"] = _point(raw)
return points
def main() -> None:
version = importlib.metadata.version("PyJHora")
payload = {
"pyjhora_version": version,
"generator": "python scripts/research/freeze_special_lagnas_pyjhora_fixture.py",
"generated_on": datetime.now().strftime("%Y-%m-%d"),
"cases": {},
}
for case_id, spec in CASES.items():
payload["cases"][case_id] = {
"source": spec["source"],
"birth": spec["birth"],
"ayanamsa": {name: _case(spec["birth"], mode) for name, mode in MODES.items()},
}
OUT.parent.mkdir(parents=True, exist_ok=True)
OUT.write_text(json.dumps(payload, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")
print(OUT)
if __name__ == "__main__":
main()