| """排盘到文字解读的胶水层。""" | |
| 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", ""), | |
| ) | |