"""排盘到文字解读的胶水层。""" from __future__ import annotations from dataclasses import dataclass, asdict from typing import Any from .coin import Cast from .hexagram import Hexagram, TRIGRAM_ELEMENT from bazi.five_elements import describe_relation RELATION_TO_LIUQIN = { "同类": "兄弟", "我生": "子孙", "生我": "父母", "我克": "妻财", "克我": "官鬼", } LIU_SHEN_ORDER = ("青龙", "朱雀", "勾陈", "螣蛇", "白虎", "玄武") GAN_TO_LIU_SHEN_START = { "甲": 0, "乙": 0, "丙": 1, "丁": 1, "戊": 2, "己": 2, "庚": 3, "辛": 3, "壬": 4, "癸": 4, } LINE_TITLE = {1: "初", 2: "二", 3: "三", 4: "四", 5: "五", 6: "上"} class LineReading: index: int title: str kind: str is_yang: bool is_changing: bool element: str liu_qin: str liu_shen: str bian: str def __init__(self, **kw): for k, v in kw.items(): setattr(self, k, v) def to_dict(self): return self.__dict__ class CastReading: cast: Cast ben: Hexagram bian: Hexagram changing_indices: list lines: list element: str day_gan: str summary: str def __init__(self, **kw): for k, v in kw.items(): setattr(self, k, v) def to_dict(self): """递归把 namedtuple / LineReading 全部转为 dict, 方便模板层使用。""" ben = self.ben bian = self.bian return { "cast": self.cast.to_dict() if hasattr(self.cast, "to_dict") else None, "ben": ben._asdict() if isinstance(ben, tuple) and hasattr(ben, "_asdict") else ben, "bian": bian._asdict() if isinstance(bian, tuple) and hasattr(bian, "_asdict") else bian, "changing_indices": list(self.changing_indices or []), "lines": [ln.to_dict() if hasattr(ln, "to_dict") else ln for ln in (self.lines or [])], "element": self.element, "day_gan": self.day_gan, "question": getattr(self, "question", "") or "", "summary": getattr(self, "summary", "") or "", } def _line_element(line_idx: int, hexagram: Hexagram) -> str: if line_idx <= 3: return TRIGRAM_ELEMENT[hexagram.lower] return TRIGRAM_ELEMENT[hexagram.upper] def _liu_qin(hex_el: str, line_el: str) -> str: return RELATION_TO_LIUQIN.get(describe_relation(hex_el, line_el), "—") def _liu_shen(day_gan: str, line_idx: int) -> str: start = GAN_TO_LIU_SHEN_START.get(day_gan, 0) return LIU_SHEN_ORDER[(start + line_idx - 1) % 6] def build_reading(cast: Cast, day_gan: str = "甲") -> CastReading: hex_el = cast.ben_hex.element lines = [] for ln in cast.lines: el = _line_element(ln.index, cast.ben_hex) bian_kind = "" if ln.is_changing: bian_kind = "变为阳" if not ln.is_yang else "变为阴" lines.append(LineReading( index=ln.index, title=LINE_TITLE[ln.index], kind=ln.kind, is_yang=ln.is_yang, is_changing=ln.is_changing, element=el, liu_qin=_liu_qin(hex_el, el), liu_shen=_liu_shen(day_gan, ln.index), bian=bian_kind, )) return CastReading( cast=cast, ben=cast.ben_hex, bian=cast.bian_hex, changing_indices=cast.changing_indices, lines=lines, element=hex_el, day_gan=day_gan, question=getattr(cast, "question", ""), )