# scripts/render/html.py """ChartData → 单文件 HTML 盘面(模板 render/templates/paipan.html)。 与 Markdown 输出同为纯计算渲染:十神/藏干/空亡/纳音/十二长生/月令旺衰 均为固定查表,不含任何命理判断(P1)。 """ from datetime import datetime from pathlib import Path from chart.luck import luck_label_for_year GAN = "甲乙丙丁戊己庚辛壬癸" ZHI = "子丑寅卯辰巳午未申酉戌亥" _WX_GAN = {"甲": "木", "乙": "木", "丙": "火", "丁": "火", "戊": "土", "己": "土", "庚": "金", "辛": "金", "壬": "水", "癸": "水"} _WX_ZHI = {"子": "水", "丑": "土", "寅": "木", "卯": "木", "辰": "土", "巳": "火", "午": "火", "未": "土", "申": "金", "酉": "金", "戌": "土", "亥": "水"} _YY = {"甲": 1, "丙": 1, "戊": 1, "庚": 1, "壬": 1, "乙": -1, "丁": -1, "己": -1, "辛": -1, "癸": -1} _SHENG = {"木": "火", "火": "土", "土": "金", "金": "水", "水": "木"} _KE = {"木": "土", "土": "水", "水": "火", "火": "金", "金": "木"} _HIDDEN = {"子": ["癸"], "丑": ["己", "癸", "辛"], "寅": ["甲", "丙", "戊"], "卯": ["乙"], "辰": ["戊", "乙", "癸"], "巳": ["丙", "戊", "庚"], "午": ["丁", "己"], "未": ["己", "丁", "乙"], "申": ["庚", "壬", "戊"], "酉": ["辛"], "戌": ["戊", "辛", "丁"], "亥": ["壬", "甲"]} _NAYIN = {"甲子": "海中金", "乙丑": "海中金", "丙寅": "炉中火", "丁卯": "炉中火", "戊辰": "大林木", "己巳": "大林木", "庚午": "路旁土", "辛未": "路旁土", "壬申": "剑锋金", "癸酉": "剑锋金", "甲戌": "山头火", "乙亥": "山头火", "丙子": "涧下水", "丁丑": "涧下水", "戊寅": "城头土", "己卯": "城头土", "庚辰": "白蜡金", "辛巳": "白蜡金", "壬午": "杨柳木", "癸未": "杨柳木", "甲申": "泉中水", "乙酉": "泉中水", "丙戌": "屋上土", "丁亥": "屋上土", "戊子": "霹雳火", "己丑": "霹雳火", "庚寅": "松柏木", "辛卯": "松柏木", "壬辰": "长流水", "癸巳": "长流水", "甲午": "沙中金", "乙未": "沙中金", "丙申": "山下火", "丁酉": "山下火", "戊戌": "平地木", "己亥": "平地木", "庚子": "壁上土", "辛丑": "壁上土", "壬寅": "金箔金", "癸卯": "金箔金", "甲辰": "覆灯火", "乙巳": "覆灯火", "丙午": "天河水", "丁未": "天河水", "戊申": "大驿土", "己酉": "大驿土", "庚戌": "钗钏金", "辛亥": "钗钏金", "壬子": "桑柘木", "癸丑": "桑柘木", "甲寅": "大溪水", "乙卯": "大溪水", "丙辰": "沙中土", "丁巳": "沙中土", "戊午": "天上火", "己未": "天上火", "庚申": "石榴木", "辛酉": "石榴木", "壬戌": "大海水", "癸亥": "大海水"} _TERRAIN_ORDER = ["长生", "沐浴", "冠带", "临官", "帝旺", "衰", "病", "死", "墓", "绝", "胎", "养"] _TERRAIN_START = {"甲": "亥", "乙": "午", "丙": "寅", "戊": "寅", "庚": "巳", "壬": "申", "丁": "酉", "己": "酉", "辛": "子", "癸": "卯"} _SEASON = {"寅": ("木", "火", "水", "金", "土"), "卯": ("木", "火", "水", "金", "土"), "巳": ("火", "土", "木", "水", "金"), "午": ("火", "土", "木", "水", "金"), "申": ("金", "水", "土", "火", "木"), "酉": ("金", "水", "土", "火", "木"), "亥": ("水", "木", "金", "土", "火"), "子": ("水", "木", "金", "土", "火"), "辰": ("土", "金", "火", "木", "水"), "戌": ("土", "金", "火", "木", "水"), "丑": ("土", "金", "火", "木", "水"), "未": ("土", "金", "火", "木", "水")} _SEASON_LABEL = ("旺", "相", "休", "囚", "死") def _wx(ch: str) -> str: wx = _WX_GAN.get(ch) or _WX_ZHI.get(ch) return {"木": "wx-mu", "火": "wx-huo", "土": "wx-tu", "金": "wx-jin", "水": "wx-shui"}.get(wx, "") def _ten_god(me: str, other: str) -> str: wx_m, wx_o = _WX_GAN[me], _WX_GAN[other] same = _YY[me] == _YY[other] if wx_m == wx_o: return "比肩" if same else "劫财" if _SHENG[wx_m] == wx_o: return "食神" if same else "伤官" if _KE[wx_m] == wx_o: return "偏财" if same else "正财" if _KE[wx_o] == wx_m: return "七杀" if same else "正官" return "偏印" if same else "正印" def _terrain(me: str, zhi: str) -> str: """日干对支的十二长生(阳顺阴逆)。""" start = _ZHI_IDX(_TERRAIN_START[me]) zi = _ZHI_IDX(zhi) off = (zi - start) % 12 if _YY[me] == 1 else (start - zi) % 12 return _TERRAIN_ORDER[off] def _ZHI_IDX(z: str) -> int: return ZHI.index(z) def _gz_void(gz: str) -> str: """一柱干支的旬空两支。""" g, z = GAN.index(gz[0]), ZHI.index(gz[1]) return ZHI[(z - g + 10) % 12] + ZHI[(z - g + 11) % 12] def _z(zhi: str, voids: str) -> str: cls = "voidz" if zhi in voids else "" return f'{zhi}' def _pillar_col(chart, key: str, label: str, cur: bool = False) -> dict: """四柱之一 → 各行的 HTML 单元格内容。""" p = getattr(chart, key) voids = chart.void.get("day", "") + chart.void.get("hour", "") return { "label": label, "cur": cur, "god": p.gan_ten_god or ("日主" if key == "day" else ""), "gan": p.gan, "zhi": p.zhi, "hidden": list(zip(p.hidden_stems, p.hidden_ten_gods)), "terrain": chart.terrain.get(key, ""), "void": _gz_void(p.gan + p.zhi), "nayin": p.na_yin or "", "shensha": chart.shen_sha.get(key, []), "voids": voids, } def _extra_col(chart, gz: str, label: str, cur: bool) -> dict: """流年/大运列(仅干支行星级信息,神煞留空)。""" me = chart.day_master voids = chart.void.get("day", "") + chart.void.get("hour", "") return { "label": label, "cur": cur, "god": _ten_god(me, gz[0]), "gan": gz[0], "zhi": gz[1], "hidden": [(h, _ten_god(me, h)) for h in _HIDDEN[gz[1]]], "terrain": _terrain(me, gz[1]), "void": _gz_void(gz), "nayin": _NAYIN[gz], "shensha": [], "voids": voids, } def _main_table(chart) -> str: this_year = datetime.now().year ly = next(((y, gz) for y, gz, _ in chart.flow_years if y == this_year), None) cur_luck = None for lp in chart.luck_pillars: if lp.start_year <= this_year: cur_luck = lp cols = [] if ly: cols.append(_extra_col(chart, ly[1], f"流年 {ly[0]}", cur=True)) if cur_luck: cols.append(_extra_col(chart, cur_luck.gan + cur_luck.zhi, "大运", cur=True)) for key, label in (("year", "年柱"), ("month", "月柱"), ("day", "日柱"), ("hour", "时柱")): cols.append(_pillar_col(chart, key, label, cur=(key == "day"))) def th(c): tag = '当前' if c["cur"] else "" return f'