fix: feed current dasha into web marriage path and keep chart fences on failure (BUG-609, BUG-610, BUG-611)
Consultation was passing the birth mahadasha into event-class splits, so marriage hits stayed empty and timing still said 缔结. Read live MD/AD from dasha_sub_periods, derive activation copy from the split, and isolate fence failures so one bad varga cannot 500 the whole report. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -39,6 +39,8 @@ if REPO_ROOT not in sys.path:
|
||||
if SCRIPTS_DIR not in sys.path:
|
||||
sys.path.insert(0, SCRIPTS_DIR)
|
||||
|
||||
from jyotish_vedic import __version__
|
||||
|
||||
try:
|
||||
from scripts.local_env import load_local_env
|
||||
from scripts.rectification_policy import (
|
||||
@@ -3399,7 +3401,7 @@ class JyotishAPIHandler(BaseHTTPRequestHandler):
|
||||
swisseph_version = None
|
||||
self._json({
|
||||
'status': 'ok',
|
||||
'version': '6.9.14',
|
||||
'version': __version__,
|
||||
'git_commit': os.environ.get('GITHUB_SHA') or 'unknown',
|
||||
'swisseph_available': swisseph_available,
|
||||
'swisseph_version': swisseph_version,
|
||||
@@ -5254,6 +5256,7 @@ class JyotishAPIHandler(BaseHTTPRequestHandler):
|
||||
dasha_timeline=dasha_timeline,
|
||||
yogas=raw.get('yogas') if isinstance(raw.get('yogas'), list) else [],
|
||||
ashtakavarga=self._safe_dict(raw.get('ashtakavarga')),
|
||||
event_class_split=self._safe_dict(raw.get('event_class_split')),
|
||||
)
|
||||
|
||||
def _safe_dict(self, value):
|
||||
@@ -5287,8 +5290,15 @@ class JyotishAPIHandler(BaseHTTPRequestHandler):
|
||||
'start': start_year,
|
||||
'end': end_year,
|
||||
})
|
||||
live = ((raw.get('modules') or {}).get('dasha_sub_periods') or {}).get('current') or {}
|
||||
md, ad = (live.get('mahadasha') or {}).get('lord'), (live.get('antardasha') or {}).get('lord')
|
||||
for row in timeline:
|
||||
if md and ad and row.get('mahadasha') == md and row.get('antardasha') in ('Unknown', '', None):
|
||||
row['antardasha'] = ad
|
||||
break
|
||||
return timeline
|
||||
|
||||
|
||||
def _thematic_period_lord(self, value):
|
||||
if isinstance(value, str) and value.strip():
|
||||
return value.strip()
|
||||
@@ -5428,7 +5438,7 @@ class JyotishAPIHandler(BaseHTTPRequestHandler):
|
||||
ashtakavarga = full_modules.get('ashtakavarga') or collect('ashtakavarga', lambda: self._compute_ashtakavarga(payload))
|
||||
relationship = collect('relationship', lambda: self._compute_relationship({
|
||||
**payload,
|
||||
'dasha_info': self._thematic_dasha_info(chart, dasha),
|
||||
'dasha_info': self._thematic_dasha_info({**chart, 'modules': {**(chart.get('modules') or {}), **full_modules}}, dasha),
|
||||
}))
|
||||
career = collect('career', lambda: self._compute_career(payload))
|
||||
jaimini = full_modules.get('jaimini') or collect('jaimini', lambda: self._compute_jaimini({**payload, 'mode': 'all'}))
|
||||
@@ -5441,6 +5451,7 @@ class JyotishAPIHandler(BaseHTTPRequestHandler):
|
||||
'yogas': chart.get('yogas') or [],
|
||||
'ashtakavarga': ashtakavarga or {},
|
||||
'modules': full_modules,
|
||||
'event_class_split': (relationship.get('event_class_split') if isinstance(relationship, dict) else {}) or {},
|
||||
}
|
||||
if yogas and isinstance(yogas.get('result'), dict):
|
||||
enriched['yogas'] = list(enriched['yogas']) + (yogas['result'].get('extended_yogas') or [])
|
||||
@@ -6553,17 +6564,19 @@ class JyotishAPIHandler(BaseHTTPRequestHandler):
|
||||
return ''
|
||||
|
||||
def _thematic_dasha_info(self, chart, dasha):
|
||||
current = {}
|
||||
if isinstance(chart.get('dasha'), dict):
|
||||
current.update(chart['dasha'])
|
||||
current = dict(chart['dasha']) if isinstance(chart.get('dasha'), dict) else {}
|
||||
modules = chart.get('modules') if isinstance(chart.get('modules'), dict) else {}
|
||||
live = modules.get('dasha_sub_periods') if isinstance(modules.get('dasha_sub_periods'), dict) else {}
|
||||
live = live.get('current') if isinstance(live.get('current'), dict) else {}
|
||||
analysis = dasha.get('vimshottari_analysis') if isinstance(dasha, dict) else None
|
||||
if isinstance(analysis, dict):
|
||||
md = ((analysis.get('current') or {}).get('mahadasha') or {}).get('lord')
|
||||
ad = ((analysis.get('current') or {}).get('antardasha') or {}).get('lord')
|
||||
if md:
|
||||
current['maha_dasha'] = md
|
||||
if ad:
|
||||
current['antar_dasha'] = ad
|
||||
analysis = (analysis.get('current') if isinstance(analysis, dict) else None) or {}
|
||||
for source in (analysis, live):
|
||||
if not isinstance(source, dict):
|
||||
continue
|
||||
for src, dest in (('mahadasha', 'maha_dasha'), ('antardasha', 'antar_dasha'), ('pratyantardasha', 'pratyantar_dasha')):
|
||||
lord = (source.get(src) or {}).get('lord') if isinstance(source.get(src), dict) else None
|
||||
if lord:
|
||||
current[dest] = lord
|
||||
return current
|
||||
|
||||
def _parse_birth_datetime(self, body):
|
||||
@@ -6997,7 +7010,7 @@ class JyotishAPIHandler(BaseHTTPRequestHandler):
|
||||
sign = house.get('cusp_sign', SIGNS[(asc_sign_idx + h - 1) % 12])
|
||||
houses[h] = {'sign': sign, 'sign_idx': SIGNS.index(sign)}
|
||||
result = {
|
||||
'success': True, 'version': '6.9.15',
|
||||
'success': True, 'version': __version__,
|
||||
'birth': {
|
||||
'date': f'{year}-{month:02d}-{day:02d}',
|
||||
'time': self._format_birth_time(hour, minute, second),
|
||||
@@ -7117,7 +7130,7 @@ class JyotishAPIHandler(BaseHTTPRequestHandler):
|
||||
special_lagnas = jaimini.calc_special_lagnas(asc_sign_idx, int(hour), minute + second / 60.0)
|
||||
|
||||
result = {
|
||||
'success': True, 'version': '6.9.15-fallback',
|
||||
'success': True, 'version': f'{__version__}-fallback',
|
||||
'warning': 'Swiss Ephemeris未安装,使用简化计算',
|
||||
'birth': {
|
||||
'date': f'{year}-{month:02d}-{day:02d}',
|
||||
|
||||
@@ -38,6 +38,7 @@
|
||||
import argparse
|
||||
import hashlib
|
||||
import json
|
||||
import logging
|
||||
import sys
|
||||
import os
|
||||
import csv
|
||||
@@ -4246,13 +4247,17 @@ def render_pl9_markdown(packet: dict) -> str:
|
||||
for name in PLANET_ORDER
|
||||
for row in [natal_planets.get(name)]
|
||||
}
|
||||
block = build_chart_block(
|
||||
resolve_chart_id(chart_id, title),
|
||||
title,
|
||||
positions if isinstance(positions, dict) else {},
|
||||
ascendant_row if isinstance(ascendant_row, dict) else {},
|
||||
retrograde_by_planet,
|
||||
)
|
||||
try:
|
||||
block = build_chart_block(
|
||||
resolve_chart_id(chart_id, title),
|
||||
title,
|
||||
positions if isinstance(positions, dict) else {},
|
||||
ascendant_row if isinstance(ascendant_row, dict) else {},
|
||||
retrograde_by_planet,
|
||||
)
|
||||
except Exception:
|
||||
logging.warning('report chart fence failed id=%s', chart_id or 'unknown')
|
||||
return svg
|
||||
return f"{svg}\n\n{block}"
|
||||
|
||||
def _d1_chart_svg() -> str | None:
|
||||
|
||||
@@ -241,6 +241,7 @@ class BirthChartData:
|
||||
dasha_timeline: List[Dict[str, Any]] = field(default_factory=list)
|
||||
yogas: List[Dict[str, Any]] = field(default_factory=list)
|
||||
ashtakavarga: Dict[str, Any] = field(default_factory=dict)
|
||||
event_class_split: Dict[str, Any] = field(default_factory=dict)
|
||||
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════
|
||||
@@ -685,9 +686,15 @@ class TimingAnchorBuilder:
|
||||
|
||||
# 根据主题生成激活描述
|
||||
activation = self._activation_text(theme, current)
|
||||
md = str(current.get("mahadasha") or "").strip()
|
||||
ad = str(current.get("antardasha") or "").strip()
|
||||
if ad and ad != "Unknown":
|
||||
dasha_period = f"{md}-{ad}" if md else ad
|
||||
else:
|
||||
dasha_period = md or "Unknown"
|
||||
|
||||
return TimingAnchor(
|
||||
dasha_period=current.get("mahadasha", "Unknown") + "-" + current.get("antardasha", "Unknown"),
|
||||
dasha_period=dasha_period,
|
||||
start_year=current.get("start", now),
|
||||
end_year=current.get("end", now + 3),
|
||||
activation_description=activation,
|
||||
@@ -695,10 +702,11 @@ class TimingAnchorBuilder:
|
||||
)
|
||||
|
||||
def _activation_text(self, theme: ThemeName, period: Dict[str, Any]) -> str:
|
||||
if theme == ThemeName.MARRIAGE:
|
||||
return self._marriage_activation_text()
|
||||
md = period.get("mahadasha", "")
|
||||
ad = period.get("antardasha", "")
|
||||
theme_desc = {
|
||||
ThemeName.MARRIAGE: "婚姻相关宫位与 karaka 将被激活,是缔结或调整伴侣关系的关键期",
|
||||
ThemeName.CAREER: "事业宫位能量被引动,可能出现职位变动、项目突破或行业转换的契机",
|
||||
ThemeName.WEALTH: "财富宫位与 Dhana Yoga 被触发,收入结构可能发生变化",
|
||||
ThemeName.HEALTH: "健康宫位能量显现,是关注身体信号、调整作息的重要时期",
|
||||
@@ -706,6 +714,20 @@ class TimingAnchorBuilder:
|
||||
}
|
||||
return f"{md} 主运配合 {ad} 副运,{theme_desc.get(theme, '相关能量将被激活')}"
|
||||
|
||||
def _marriage_activation_text(self) -> str:
|
||||
split = self.chart.event_class_split if isinstance(getattr(self.chart, "event_class_split", None), dict) else {}
|
||||
legal = split.get("legal_marriage") if isinstance(split.get("legal_marriage"), dict) else {}
|
||||
romantic = split.get("romantic_activation") if isinstance(split.get("romantic_activation"), dict) else {}
|
||||
formation = split.get("relationship_formation") if isinstance(split.get("relationship_formation"), dict) else {}
|
||||
legal_hits = legal.get("hits") if isinstance(legal.get("hits"), list) else []
|
||||
romantic_hits = romantic.get("hits") if isinstance(romantic.get("hits"), list) else []
|
||||
formation_hits = formation.get("hits") if isinstance(formation.get("hits"), list) else []
|
||||
if legal_hits:
|
||||
return "婚姻相关宫位与 karaka 将被激活,是缔结或调整伴侣关系的关键期"
|
||||
if romantic_hits or formation_hits:
|
||||
return "感情被激活的观察期,不等于领证"
|
||||
return "本期无婚恋事件类命中"
|
||||
|
||||
|
||||
# ═══════════════════════════════════════════════════════════════
|
||||
# 主题化解盘编排器(主控类)
|
||||
|
||||
@@ -91,6 +91,8 @@ CORE_PYTEST_TARGETS = [
|
||||
"tests/test_mcp_strict_workflow_relationship.py",
|
||||
"tests/test_punarphoo_observation.py",
|
||||
"tests/test_relationship_event_class_evidence.py",
|
||||
"tests/test_thematic_dasha_info_source.py",
|
||||
"tests/test_report_orchestrator_marriage_timing.py",
|
||||
"tests/test_full_reading_conditional_dashas.py",
|
||||
"tests/test_capability_evidence_pool.py",
|
||||
"tests/test_readme_badges.py",
|
||||
|
||||
Reference in New Issue
Block a user