#!/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"