Files
Jyotisha/tests/test_annual_native_geometry.py
T

153 lines
6.7 KiB
Python

"""T6: whole-sign annual houses, Tripataki, and Saham traces use our longitudes."""
from __future__ import annotations
import json
from datetime import datetime
from scripts.annual_native.annual_houses import build_annual_whole_sign_houses
from scripts.annual_native.monthly_charts import build_monthly_charts
from scripts.annual_native.tripataki_chakra import build_tripataki_chakra
from scripts.jyotish_engine import compute_chart_data
from scripts.report_render.parity_chapters import render_parity_chapters
from scripts.tajika import calc_all_sahams
from tests.report_english_fictional import fictional_case
LIMIT = 1e-6
def _delta(left: float, right: float) -> float:
return abs(((left - right + 180.0) % 360.0) - 180.0)
def _chart():
birth = fictional_case(0)
chart, *_rest = compute_chart_data(
birth["year"], birth["month"], birth["day"], birth["hour"], birth["minute"],
birth["lat"], birth["lon"], birth["tz"],
node_mode=birth["node_mode"], second=birth["second"], ayanamsa_name=birth["ayanamsa"],
)
return birth, chart
def test_new_longitudes_match_the_chart() -> None:
_birth, chart = _chart()
houses = build_annual_whole_sign_houses(chart)
assert houses["status"] == "partial_verified"
assert houses["house_system"] == "whole_sign"
assert len(houses["houses"]) == 12
raw = {
name: float(chart["planets"][name]["degree_raw"]) % 360.0
for name in ("Sun", "Moon")
}
for name, longitude in raw.items():
assert _delta(houses["source_longitudes"][name], longitude) <= LIMIT
asc = float(chart["ascendant"]["degree_raw"])
tripataki = build_tripataki_chakra(
{name: data["degree_raw"] for name, data in chart["planets"].items() if isinstance(data, dict) and "degree_raw" in data},
asc,
)
assert tripataki["status"] == "partial_verified"
for name, longitude in raw.items():
assert _delta(tripataki["source_longitudes"][name], longitude) <= LIMIT
sun = next(row for row in tripataki["placements"] if row["body"] == "Sun")
assert _delta(sun["longitude"], raw["Sun"]) <= LIMIT
def test_monthly_row_sun_matches_the_chart_function() -> None:
import swisseph as swe
from scripts.annual_native.sidereal_bodies import sun_moon_longitudes
birth, chart = _chart()
hour = birth["hour"] + birth["minute"] / 60.0
jd = swe.julday(birth["year"], birth["month"], birth["day"], hour - birth["tz"])
sun, moon = sun_moon_longitudes(jd, node_mode=birth["node_mode"], ayanamsa_name=birth["ayanamsa"])
assert _delta(sun, float(chart["planets"]["Sun"]["degree_raw"])) <= LIMIT
assert _delta(moon, float(chart["planets"]["Moon"]["degree_raw"])) <= LIMIT
monthly = build_monthly_charts(
annual_jd=jd,
birth_sun_longitude=sun,
latitude=birth["lat"],
longitude=birth["lon"],
tz_hours=birth["tz"],
ayanamsa=birth["ayanamsa"],
node_mode=birth["node_mode"],
target_year=birth["year"],
)
assert len(monthly["rows"]) == 12
first = monthly["rows"][0]
assert first["status"] == "partial_verified"
again, *_rest = compute_chart_data(
int(first["utc_datetime"][0:4]), int(first["utc_datetime"][5:7]), int(first["utc_datetime"][8:10]),
int(first["utc_datetime"][11:13]), int(first["utc_datetime"][14:16]),
birth["lat"], birth["lon"], 0,
node_mode=birth["node_mode"], second=int(first["utc_datetime"][17:19]),
ayanamsa_name=birth["ayanamsa"],
)
assert _delta(float(first["planet_longitudes"]["Sun"]), float(again["planets"]["Sun"]["degree_raw"])) <= LIMIT
assert _delta(float(first["planet_longitudes"]["Moon"]), float(again["planets"]["Moon"]["degree_raw"])) <= LIMIT
def test_saham_traces_keep_the_stored_longitude() -> None:
birth, chart = _chart()
asc = chart["ascendant"]
asc_lon = float(asc.get("lon") if isinstance(asc.get("lon"), (int, float)) else asc["degree_raw"])
planet_lons = {
name: float(data.get("degree", data["degree_raw"]))
for name, data in chart["planets"].items()
if isinstance(data, dict) and "degree" in data
}
result = calc_all_sahams(
planet_lons,
asc_lon,
datetime(birth["year"], birth["month"], birth["day"], birth["hour"], birth["minute"]),
lat=birth["lat"],
lon=birth["lon"],
tz=birth["tz"],
)
assert result.get("status") != "blocked"
traces = result["formula_traces"]
assert len(traces) >= 30
for trace in traces:
assert trace["longitude"] == result[trace["key"]]["longitude"]
without = {key: value for key, value in result.items() if key != "formula_traces"}
assert len(json.dumps(without, ensure_ascii=False, default=str)) > 800
is_day = result["daynight_evidence"]["is_daytime"]
karya = next(item for item in traces if item["key"] == "karya_saham")
first, second = (("Moon", "Mars") if is_day else ("Mars", "Moon"))
expected = round((asc_lon + planet_lons[second] - planet_lons[first]) % 360.0, 4)
assert karya["longitude"] == expected
assert karya["rule"] == f"Asc + {second} - {first}"
def test_four_sections_render_in_both_languages() -> None:
packet = {
"worksheets": {
"advanced_systems": {"sahams": {"formula_traces": [
{"name": "Punya Saham", "key": "punya_saham", "rule": "Asc + Moon - Sun", "phase": "day", "longitude": 12.5},
]}},
"timing_and_predictive_systems": {"annual_tajika_pack": {
"annual_houses": {"houses": [
{"house": 1, "sign": "Aries", "lord": "Mars", "occupants": []},
]},
"tripataki_chakra": {
"placements": [{"body": "Sun", "sign": "Leo", "degree_in_sign": 1.25, "longitude": 121.25}],
"vedha_rows": [{"target": "Sun", "available_sources": ["Moon"], "status": "partial_verified"}],
},
"sahams": {"data": {"formula_traces": [
{"name": "Vivah Saham", "key": "vivah_saham", "rule": "Asc + Venus - Mars", "phase": "night", "longitude": 40.0},
]}},
}},
}
}
chinese = render_parity_chapters(packet, "zh")
english = render_parity_chapters(packet, "en")
assert "年运整宫框架" in chinese and "无" in chinese
assert "Annual Return House Framework" in english and "None" in english
assert "Tripataki 轮盘明细" in chinese and "Tripataki Chakra Detail" in english
assert "年度特殊点公式轨迹" in chinese and "夜间" in chinese
assert "Annual Saham Formula Trace" in english and "Night" in english
assert "本命特殊点公式轨迹" in chinese and "日间" in chinese
assert "Natal Saham Formula Trace" in english and "Day" in english
assert "blocked" not in chinese.lower()