Files
Jyotisha/scripts/rashi_dasha_report.py
T
jesse-ux 9d3eb23ee4 feat: 完整数据版加入六种星座大运
Sthira、Drig、Shoola、Navamsha、Niryaana Shoola 与 K.N. Rao 的 Chara 从本盘算出。Lagna Kendradi 不上表。未推送。
2026-10-09 00:43:00 +08:00

169 lines
6.4 KiB
Python

#!/usr/bin/env python3
"""Markdown chapter for the seven report-only rashi dashas."""
from __future__ import annotations
from typing import Mapping
try:
from rashi_dashas_native import calculate_rashi_dashas
except ImportError: # pragma: no cover - package import
from scripts.rashi_dashas_native import calculate_rashi_dashas
_SIGN_CN = {
"Aries": "白羊座",
"Taurus": "金牛座",
"Gemini": "双子座",
"Cancer": "巨蟹座",
"Leo": "狮子座",
"Virgo": "处女座",
"Libra": "天秤座",
"Scorpio": "天蝎座",
"Sagittarius": "射手座",
"Capricorn": "摩羯座",
"Aquarius": "水瓶座",
"Pisces": "双鱼座",
}
# Chinese headings avoid the token Dasha. The report cleaner turns that token
# into 大运 and then deletes the linebreak before the next Chinese sentence.
_ORDER = (
("sthira", "Sthira", "Sthira Dasha"),
("niryaana_shoola", "Niryaana Shoola", "Niryaana Shoola Dasha"),
("drig", "Drig", "Drig Dasha"),
("navamsha", "Navamsha", "Navamsha Dasha"),
("shoola", "Shoola", "Shoola Dasha"),
("chara", "Chara", "Chara Dasha"),
("lagna_kendradi", "Lagna Kendradi", "Lagna Kendradi Rashi Dasha"),
)
_CLASSICAL = ("Sun", "Moon", "Mars", "Mercury", "Jupiter", "Venus", "Saturn")
def _language(packet: Mapping) -> str:
raw = packet.get("report_language") or packet.get("language") or "zh"
return "en" if str(raw).lower().startswith("en") else "zh"
def _longitude(row: Mapping) -> float | None:
# Ascendant "degree" is the degree inside the sign. Full longitude is degree_raw / lon.
for key in ("degree_raw", "lon", "longitude"):
value = row.get(key)
if isinstance(value, (int, float)):
return float(value)
sign = row.get("sign")
degree = row.get("degree_in_sign")
if sign in _SIGN_CN and isinstance(degree, (int, float)):
signs = list(_SIGN_CN)
return signs.index(str(sign)) * 30.0 + float(degree)
return None
def _birth_date(packet: Mapping) -> str | None:
birth = packet.get("birth_info")
if not isinstance(birth, Mapping):
return None
if birth.get("date"):
return str(birth.get("date"))
year, month, day = birth.get("year"), birth.get("month"), birth.get("day")
if all(isinstance(part, int) for part in (year, month, day)):
return f"{int(year):04d}-{int(month):02d}-{int(day):02d}"
return None
def chart_inputs(packet: Mapping) -> dict | None:
core = packet.get("core_chart")
if not isinstance(core, Mapping):
return None
asc = core.get("ascendant")
planets = core.get("planets")
if not isinstance(asc, Mapping) or not isinstance(planets, Mapping):
return None
asc_lon = _longitude(asc)
birth = _birth_date(packet)
if asc_lon is None or not birth:
return None
longitudes = {}
for name in list(_CLASSICAL) + ["Rahu", "Ketu"]:
row = planets.get(name)
if isinstance(row, Mapping):
value = _longitude(row)
if value is not None:
longitudes[name] = value
if any(name not in longitudes for name in _CLASSICAL):
return None
return {"birth": birth, "ascendant_longitude": asc_lon, "planet_lons": longitudes}
def _sign_label(sign: str, language: str) -> str:
if language == "zh":
return _SIGN_CN.get(sign, sign)
return sign
def _method_line(section: Mapping, language: str) -> str:
if section.get("status") != "listed":
return str(section.get("reason_en") if language == "en" else section.get("reason_zh") or "")
profile = section.get("profile_id") or ""
if language == "en":
return (
f"Method used on this site: {profile}, source: {section.get('source_en')}; "
"not checked one-by-one against external software."
)
return f"本站选用的方法:{profile},出处:{section.get('source_zh')};未与外部软件逐一核对。"
def render_rashi_dasha_chapter(packet: Mapping) -> str:
"""Return the chapter, or an empty string when the packet has no longitudes."""
inputs = chart_inputs(packet)
if inputs is None:
return ""
language = _language(packet)
sections = calculate_rashi_dashas(inputs["birth"], inputs["ascendant_longitude"], inputs["planet_lons"])
if language == "en":
lines = [
"## Sign dashas calculated on this site",
"",
"These tables list signs and dates only.",
"",
]
sign_header = "| Index | Sign | Start | End | Years |"
child_header = "| Index | Maha | Antara | Start | End |"
else:
lines = [
"## 星座大运(本站计算)",
"",
"以下只列出星座和日期。",
"",
]
sign_header = "| 序号 | 星座 | 起始 | 结束 | 年数 |"
child_header = "| 序号 | 主运 | 子运 | 起始 | 结束 |"
for key, title_zh, title_en in _ORDER:
section = sections[key]
title = title_en if language == "en" else title_zh
lines.extend([f"### {title}", "", _method_line(section, language), ""])
if key == "chara" and section.get("status") == "listed":
lines.extend([
"This section is the K.N. Rao Chara table. It is not the Narayana table."
if language == "en"
else "这一节是 K.N. Rao 的 Chara 表,不是 Narayana 表。",
"",
])
periods = section.get("periods") or []
if not periods:
continue
lines.extend([sign_header, "|------|------|------|------|------|"])
child_rows = []
for index, period in enumerate(periods, start=1):
label = _sign_label(str(period.get("sign")), language)
lines.append(
f"| {index} | {label} | {period.get('start')} | {period.get('end')} | {period.get('years')} |"
)
for child in period.get("antardasha") or []:
child_rows.append((label, _sign_label(str(child.get("sign")), language), child.get("start"), child.get("end")))
lines.append("")
if child_rows:
lines.extend([child_header, "|------|------|------|------|------|"])
for index, (parent, child, start, end) in enumerate(child_rows, start=1):
lines.append(f"| {index} | {parent} | {child} | {start} | {end} |")
lines.append("")
return "\n".join(lines).rstrip() + "\n"