Files
Jyotisha/scripts/rectification/house_table.py
T
Jesse_Chen c8af18e90a
Independent Staging Quality Gate / validate (push) Failing after 9m39s
Independent Staging Quality Gate / publish (push) Has been skipped
fix(rectification): show the house table and hide the activity fold
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-19 20:11:51 +08:00

110 lines
3.3 KiB
Python

"""Compact D1 house table for the public rectification surface.
Sign names and occupants only. Longitudes, coordinates, scores and
fingerprints stay out of this projection.
"""
from __future__ import annotations
from typing import Any
from scripts.jyotish_engine import SIGNS, SIGNS_CN
PLANET_ORDER = (
"Sun",
"Moon",
"Mars",
"Mercury",
"Jupiter",
"Venus",
"Saturn",
"Rahu",
"Ketu",
)
PLANET_ZH = {
"Sun": "太阳",
"Moon": "月亮",
"Mars": "火星",
"Mercury": "水星",
"Jupiter": "木星",
"Venus": "金星",
"Saturn": "土星",
"Rahu": "罗睺",
"Ketu": "计都",
}
def _sign_cn(sign: str | None) -> str | None:
if not isinstance(sign, str) or sign not in SIGNS_CN:
return None
return SIGNS_CN[sign]
def _house_number(value: Any, sign: str | None, asc_idx: int | None) -> int | None:
if isinstance(value, int) and 1 <= value <= 12:
return value
if not sign or sign not in SIGNS or asc_idx is None:
return None
return ((SIGNS.index(sign) - asc_idx) % 12) + 1
def compact_house_table(chart: Any, *, time: str) -> dict[str, Any] | None:
if not isinstance(chart, dict) or not isinstance(time, str):
return None
ascendant = chart.get("ascendant") if isinstance(chart.get("ascendant"), dict) else {}
lagna = _sign_cn(str(ascendant.get("sign") or "") or None)
if not lagna:
return None
try:
asc_idx = SIGNS.index(str(ascendant["sign"]))
except (KeyError, ValueError):
return None
occupants: dict[int, list[str]] = {house: [] for house in range(1, 13)}
planets = chart.get("planets") if isinstance(chart.get("planets"), dict) else {}
for name in PLANET_ORDER:
data = planets.get(name)
if not isinstance(data, dict):
continue
house = _house_number(data.get("house"), str(data.get("sign") or "") or None, asc_idx)
label = PLANET_ZH.get(name)
if house is None or not label or label in occupants[house]:
continue
occupants[house].append(label)
houses = []
for house in range(1, 13):
sign = _sign_cn(SIGNS[(asc_idx + house - 1) % 12])
if not sign:
return None
houses.append({
"house": house,
"sign": sign,
"occupants": occupants[house],
})
return {
"time": time[:5],
"lagna": lagna,
"houses": houses,
}
def compact_house_table_from_contexts(contexts: Any, time: str | None) -> dict[str, Any] | None:
if not isinstance(time, str) or not time or not isinstance(contexts, list):
return None
wanted = time[:5]
for context in contexts:
if not isinstance(context, dict):
continue
feature = context.get("feature") if isinstance(context.get("feature"), dict) else {}
feature_time = feature.get("time")
candidate_at = context.get("candidate_at")
context_time = feature_time if isinstance(feature_time, str) else None
if context_time is None and hasattr(candidate_at, "strftime"):
context_time = candidate_at.strftime("%H:%M")
if context_time != wanted:
continue
return compact_house_table(context.get("chart"), time=wanted)
return None