"""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.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_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()