Repair divisional dignity context handling
This commit is contained in:
@@ -41,4 +41,9 @@ This file is the small index for the current engineering fronts that still drive
|
||||
|
||||
- `/Users/wuyongnaren/Documents/印度占星/docs/superpowers/specs/2026-06-28-dignity-guardrail-v1-design.md`
|
||||
- `/Users/wuyongnaren/Documents/印度占星/docs/research/dignity_guardrail_v1_boundary_audit_2026_06_28.md`
|
||||
- D1-only dignity guardrail is now landed; divisional dignity repair and a separate functional-role guardrail remain open fronts.
|
||||
- `/Users/wuyongnaren/Documents/印度占星/docs/research/divisional_dignity_context_repair_audit_2026_06_28.md`
|
||||
- D1-only dignity guardrail is landed.
|
||||
- Divisional dignity context repair for D9/DK/Vimsopaka Navamsa is also landed.
|
||||
- Still open:
|
||||
- Vimsopaka semantic mapping for `NEECHA_BHANGA / GREAT_FRIEND / GREAT_ENEMY`
|
||||
- a separate functional-role guardrail
|
||||
|
||||
@@ -0,0 +1,121 @@
|
||||
# Divisional Dignity Context Repair Audit (2026-06-28)
|
||||
|
||||
## What Landed
|
||||
|
||||
This pass repairs a specific boundary bug in `/Users/wuyongnaren/Documents/印度占星/scripts/jyotish_engine.py`:
|
||||
|
||||
- `full-reading.modules.d9_navamsa_expanded[*].dignity`
|
||||
- `full-reading.modules.jaimini.darakaraka.d9_dignity`
|
||||
- `full-reading.modules.vimsopaka[*].varga_scores.Navamsa.dignity`
|
||||
|
||||
were previously calling `_get_dignity_level(...)` with natal `D1 planets` as `planets_data`, even when the dignity being judged belonged to `D9` or another varga.
|
||||
|
||||
This pass adds `_build_dignity_context(chart_data)` and routes divisional dignity calls through the current chart context instead of borrowing `D1`.
|
||||
|
||||
## Root Cause
|
||||
|
||||
The helper `_get_dignity_level(planet, sign, deg_in_sign, planets_data)` was already context-sensitive:
|
||||
|
||||
- temporary friendship (`Tatkalika Maitri`)
|
||||
- compound friendship (`Panchadha Maitri`)
|
||||
- `Neecha Bhanga`
|
||||
|
||||
all depend on the `planets_data` passed in.
|
||||
|
||||
The bug was not in the helper itself. The bug was in the callers that fed `D1 planets` into divisional dignity paths.
|
||||
|
||||
## Regression Cases Locked
|
||||
|
||||
### 1. D9 expanded dignity uses D9 context
|
||||
|
||||
Case:
|
||||
|
||||
- `1990-01-01 12:00`
|
||||
- `lat=39.9`
|
||||
- `lon=116.4`
|
||||
- `tz=8`
|
||||
|
||||
Expected:
|
||||
|
||||
- `modules.d9_navamsa_expanded.Jupiter.sign == Capricorn`
|
||||
- `modules.d9_navamsa_expanded.Jupiter.dignity == NEECHA_BHANGA`
|
||||
|
||||
Before fix:
|
||||
|
||||
- dignity incorrectly surfaced as `DEBILITATED`
|
||||
|
||||
### 2. Darakaraka D9 dignity uses D9 context
|
||||
|
||||
Case:
|
||||
|
||||
- `1992-08-25 23:10`
|
||||
- `lat=35.6895`
|
||||
- `lon=139.6917`
|
||||
- `tz=9`
|
||||
|
||||
Expected:
|
||||
|
||||
- `modules.jaimini.darakaraka.dk_planet == Sun`
|
||||
- `modules.jaimini.darakaraka.d9_sign == Gemini`
|
||||
- `modules.jaimini.darakaraka.d9_dignity == ENEMY`
|
||||
|
||||
Before fix:
|
||||
|
||||
- dignity incorrectly surfaced as `FRIEND`
|
||||
|
||||
### 3. Vimsopaka Navamsa dignity uses D9 context
|
||||
|
||||
Same `1992-08-25 23:10` case:
|
||||
|
||||
Expected:
|
||||
|
||||
- `modules.vimsopaka.Sun.varga_scores.Navamsa.dignity == Enemy`
|
||||
|
||||
Before fix:
|
||||
|
||||
- dignity incorrectly surfaced as `Friend`
|
||||
|
||||
## Tests Added / Updated
|
||||
|
||||
- `/Users/wuyongnaren/Documents/印度占星/tests/test_dasha.py`
|
||||
- helper-level context-sensitive dignity regression tests
|
||||
- `/Users/wuyongnaren/Documents/印度占星/tests/test_cli_smoke.py`
|
||||
- D9 expanded dignity regression
|
||||
- Darakaraka D9 dignity regression
|
||||
- Vimsopaka Navamsa dignity regression
|
||||
- updated D1 smoke expectation for richer `Great Enemy` output
|
||||
|
||||
## Verification
|
||||
|
||||
Ran:
|
||||
|
||||
```bash
|
||||
python3 -m pytest tests/test_dasha.py tests/test_cli_smoke.py tests/test_mcp_strict_workflow_relationship.py tests/test_mcp_strict_workflow_finance.py -q
|
||||
```
|
||||
|
||||
Result:
|
||||
|
||||
- `75 passed`
|
||||
|
||||
## Boundary Notes
|
||||
|
||||
This pass **does fix**:
|
||||
|
||||
- divisional dignity borrowing `D1` context
|
||||
- D9 exposed dignity fields in `full-reading`
|
||||
- Navamsa dignity inside Vimsopaka when the dignity category already maps cleanly to the current `DignityLevel` enum
|
||||
|
||||
This pass **does not fix**:
|
||||
|
||||
- full semantic mapping of `NEECHA_BHANGA`, `GREAT_FRIEND`, `GREAT_ENEMY` into Vimsopaka virupa policy
|
||||
- broader D10/D12/D60 dignity benchmark closure
|
||||
- any adjudicator logic that consumes divisional dignity later
|
||||
- historical P0/P1 items unrelated to this context bug
|
||||
|
||||
## Fragment Reuse / Traceability
|
||||
|
||||
This repair closes one concrete slice of the earlier audit trail in:
|
||||
|
||||
- `/Users/wuyongnaren/Documents/印度占星/references/audit-deep-data-audit-2026-05-04.md`
|
||||
|
||||
where Vimsopaka and divisional dignity context leakage had already been identified as a high-severity trust issue.
|
||||
+121
-16
@@ -129,10 +129,13 @@ DIGNITY_LABELS = {
|
||||
'EXALTED': '入旺(Exalted)',
|
||||
'MOOLATRIKONA': '本垣(Moolatrikona)',
|
||||
'OWN_SIGN': '入庙(Own Sign)',
|
||||
'GREAT_FRIEND': '极友(Great Friend)',
|
||||
'FRIEND': '入友(Friendly Sign)',
|
||||
'NEUTRAL': '中性(Neutral)',
|
||||
'ENEMY': '入敌(Enemy Sign)',
|
||||
'GREAT_ENEMY': '极敌(Great Enemy)',
|
||||
'DEBILITATED': '落陷(Debilitated)',
|
||||
'NEUTRAL': '中性',
|
||||
'NEECHA_BHANGA': '落陷取消(Neecha Bhanga)',
|
||||
}
|
||||
PUSHKARA_NAVAMSA_RANGES = {
|
||||
'fire': [(6 + 40/60, 10), (23 + 20/60, 26 + 40/60)],
|
||||
@@ -371,31 +374,125 @@ def calc_all_inter_chart_linkages(d1_data, d9_data, d10_data, d12_data=None, d1_
|
||||
|
||||
|
||||
|
||||
def _get_dignity_level(planet, sign, deg_in_sign=None):
|
||||
"""判断行星在某星座的尊严等级 (用于 Vimsopaka 映射)"""
|
||||
def _get_temporary_relationship(planet1, planet2, planets_data):
|
||||
"""Calculate Temporary Friendship (Tatkalika Maitri)."""
|
||||
if not planets_data or planet1 not in planets_data or planet2 not in planets_data:
|
||||
return 'NEUTRAL'
|
||||
|
||||
p1_sign = planets_data[planet1].get('sign')
|
||||
p2_sign = planets_data[planet2].get('sign')
|
||||
if not p1_sign or not p2_sign:
|
||||
return 'NEUTRAL'
|
||||
|
||||
idx1 = SIGNS.index(p1_sign)
|
||||
idx2 = SIGNS.index(p2_sign)
|
||||
distance = (idx2 - idx1) % 12 + 1
|
||||
|
||||
# 2, 3, 4, 10, 11, 12 from planet are temporary friends
|
||||
if distance in [2, 3, 4, 10, 11, 12]:
|
||||
return 'FRIEND'
|
||||
# 1 (conjunct), 5, 6, 7, 8, 9 are temporary enemies
|
||||
else:
|
||||
return 'ENEMY'
|
||||
|
||||
def _check_neecha_bhanga(planet, sign, planets_data):
|
||||
"""Check for Cancellation of Debilitation."""
|
||||
if not planets_data:
|
||||
return False
|
||||
|
||||
sign_lord = SIGN_LORDS.get(sign)
|
||||
|
||||
# Condition 1: Dispositor is exalted.
|
||||
if sign_lord and sign_lord in planets_data:
|
||||
lord_sign = planets_data[sign_lord].get('sign')
|
||||
if lord_sign and EXALTATION.get(sign_lord) == lord_sign:
|
||||
return True
|
||||
|
||||
# Condition 2: Planet exalted in this sign is conjunct.
|
||||
exalted_planet = None
|
||||
for p, ex_sign in EXALTATION.items():
|
||||
if ex_sign == sign:
|
||||
exalted_planet = p
|
||||
break
|
||||
|
||||
if exalted_planet and exalted_planet in planets_data:
|
||||
ex_p_sign = planets_data[exalted_planet].get('sign')
|
||||
if ex_p_sign == sign:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
def _build_dignity_context(chart_data):
|
||||
"""Normalize a chart/varga payload into the per-planet context used by dignity checks."""
|
||||
context = {}
|
||||
if not isinstance(chart_data, dict):
|
||||
return context
|
||||
|
||||
for pn, pd in chart_data.items():
|
||||
if pn in ('_meta', 'Ascendant') or not isinstance(pd, dict) or 'sign' not in pd:
|
||||
continue
|
||||
context[pn] = {
|
||||
'sign': pd.get('sign'),
|
||||
'degree_in_sign': pd.get('degree_in_sign', pd.get('degree', 0) % 30),
|
||||
}
|
||||
return context
|
||||
|
||||
def _get_dignity_level(planet, sign, deg_in_sign=None, planets_data=None):
|
||||
"""判断行星在某星座的尊严等级 (用于 Vimsopaka 映射), 包含五重敌友和落陷取消"""
|
||||
if EXALTATION.get(planet) == sign:
|
||||
return 'EXALTED'
|
||||
# Moolatrikona: 需要检查度数范围
|
||||
|
||||
mt = MOOLATRIKONA.get(planet)
|
||||
if mt and mt[0] == sign:
|
||||
if deg_in_sign is not None and mt[1] <= deg_in_sign < mt[2]:
|
||||
return 'MOOLATRIKONA'
|
||||
|
||||
if SIGN_LORDS.get(sign) == planet:
|
||||
return 'OWN_SIGN'
|
||||
|
||||
if DEBILITATION.get(planet) == sign:
|
||||
if _check_neecha_bhanga(planet, sign, planets_data):
|
||||
return 'NEECHA_BHANGA'
|
||||
return 'DEBILITATED'
|
||||
# Natural dignity is judged by the planet's attitude toward the sign lord.
|
||||
|
||||
sign_lord = SIGN_LORDS.get(sign, '')
|
||||
if not sign_lord:
|
||||
return 'NEUTRAL'
|
||||
|
||||
# Naisargika (Permanent)
|
||||
perm = 'NEUTRAL'
|
||||
if sign_lord in PERMANENT_FRIENDS.get(planet, []):
|
||||
perm = 'FRIEND'
|
||||
elif sign_lord in PERMANENT_ENEMIES.get(planet, []):
|
||||
perm = 'ENEMY'
|
||||
|
||||
if not planets_data:
|
||||
return perm
|
||||
|
||||
# Tatkalika (Temporary)
|
||||
temp = _get_temporary_relationship(planet, sign_lord, planets_data)
|
||||
|
||||
# Panchadha Maitri (Compound)
|
||||
if perm == 'FRIEND' and temp == 'FRIEND':
|
||||
return 'GREAT_FRIEND'
|
||||
elif perm == 'ENEMY' and temp == 'ENEMY':
|
||||
return 'GREAT_ENEMY'
|
||||
elif perm == 'FRIEND' and temp == 'ENEMY':
|
||||
return 'NEUTRAL'
|
||||
elif perm == 'NEUTRAL' and temp == 'FRIEND':
|
||||
return 'FRIEND'
|
||||
if sign_lord in PERMANENT_ENEMIES.get(planet, []):
|
||||
elif perm == 'NEUTRAL' and temp == 'ENEMY':
|
||||
return 'ENEMY'
|
||||
elif perm == 'ENEMY' and temp == 'FRIEND':
|
||||
return 'NEUTRAL'
|
||||
|
||||
return 'NEUTRAL'
|
||||
|
||||
|
||||
def _get_planet_status_label(planet, sign, deg_in_sign=None):
|
||||
def _get_planet_status_label(planet, sign, deg_in_sign=None, planets_data=None):
|
||||
"""Return the user-facing D1 dignity label for chart output."""
|
||||
return DIGNITY_LABELS.get(_get_dignity_level(planet, sign, deg_in_sign), '中性')
|
||||
return DIGNITY_LABELS.get(_get_dignity_level(planet, sign, deg_in_sign, planets_data), '中性')
|
||||
PLANET_CN = {"Ketu": "南交点Ketu", "Venus": "金星Venus", "Sun": "太阳Sun", "Moon": "月亮Moon", "Mars": "火星Mars", "Rahu": "北交点Rahu", "Jupiter": "木星Jupiter", "Saturn": "土星Saturn", "Mercury": "水星Mercury"}
|
||||
BASE_PLANETS_SWE = {'Sun': swe.SUN, 'Moon': swe.MOON, 'Mars': swe.MARS, 'Mercury': swe.MERCURY, 'Jupiter': swe.JUPITER, 'Venus': swe.VENUS, 'Saturn': swe.SATURN} if HAS_SWE else {}
|
||||
PLANETS_SWE = {**BASE_PLANETS_SWE, 'Rahu': swe.MEAN_NODE} if HAS_SWE else {}
|
||||
@@ -1033,14 +1130,13 @@ def compute_chart_data(year, month, day, hour, minute, lat, lon, tz, node_mode='
|
||||
si = int(lon_p / 30); d_in_s = lon_p - si * 30; sign = SIGNS[si]
|
||||
retro = spd < 0
|
||||
house = ((si - asc_idx) % 12) + 1
|
||||
status = _get_planet_status_label(pname, sign, d_in_s)
|
||||
ni = int(lon_p / nak_span); pada = int((lon_p % nak_span) / (nak_span / 4)) + 1
|
||||
nak_n, nak_l, _ = NAKSHATRA_LIST[ni % 27]
|
||||
result["planets"][pname] = {
|
||||
"sign": sign, "sign_cn": SIGNS_CN[sign], "degree": round(lon_p, 4),
|
||||
"degree_raw": lon_p,
|
||||
"degree_in_sign": round(d_in_s, 4), "degree_in_sign_raw": d_in_s,
|
||||
"house": house, "status": status,
|
||||
"house": house, "status": "",
|
||||
"retrograde": retro, "speed": round(spd, 6),
|
||||
"nakshatra": nak_n, "nakshatra_pada": pada, "nakshatra_lord": nak_l}
|
||||
if pname == 'Rahu':
|
||||
@@ -1051,11 +1147,16 @@ def compute_chart_data(year, month, day, hour, minute, lat, lon, tz, node_mode='
|
||||
"sign": SIGNS[ksi], "sign_cn": SIGNS_CN[SIGNS[ksi]],
|
||||
"degree": round(klon, 4), "degree_raw": klon,
|
||||
"degree_in_sign": round(kd, 4), "degree_in_sign_raw": kd,
|
||||
"house": ((ksi - asc_idx) % 12) + 1, "status": "中性",
|
||||
"house": ((ksi - asc_idx) % 12) + 1, "status": "",
|
||||
"retrograde": True, "speed": round(spd, 6),
|
||||
"nakshatra": kn, "nakshatra_pada": kp, "nakshatra_lord": kl}
|
||||
except Exception as e:
|
||||
result["planets"][pname] = {"error": str(e)}
|
||||
|
||||
for pn, p_data in result["planets"].items():
|
||||
if "error" not in p_data and pn != "Lagna":
|
||||
p_data["status"] = _get_planet_status_label(pn, p_data["sign"], p_data["degree_in_sign"], result["planets"])
|
||||
|
||||
return result, asc_idx, jd, ayanamsa
|
||||
|
||||
|
||||
@@ -3861,14 +3962,16 @@ def cmd_full_reading(args):
|
||||
# 不使用 _dignity 字典(只有 Exalted/Debilitated/Own Sign)
|
||||
pinfo = vdata.get(pn, {})
|
||||
if isinstance(pinfo, dict) and 'sign' in pinfo:
|
||||
dignity_context = planets if div == 1 else _build_dignity_context(vdata)
|
||||
dl_key = _get_dignity_level(pn, pinfo['sign'],
|
||||
pinfo.get('degree_in_sign', pinfo.get('degree', 0) % 30))
|
||||
pinfo.get('degree_in_sign', pinfo.get('degree', 0) % 30),
|
||||
dignity_context)
|
||||
planet_vargas_input[pn][vt] = dignity_map.get(dl_key, DignityLevel.NEUTRAL)
|
||||
elif div == 1:
|
||||
# D1 直接用本命盘数据
|
||||
p_sign = SIGNS[int(planet_lons.get(pn, 0) / 30) % 12]
|
||||
d_in_s = planet_lons.get(pn, 0) % 30
|
||||
dl_key = _get_dignity_level(pn, p_sign, d_in_s)
|
||||
dl_key = _get_dignity_level(pn, p_sign, d_in_s, planets)
|
||||
planet_vargas_input[pn][vt] = dignity_map.get(dl_key, DignityLevel.NEUTRAL)
|
||||
else:
|
||||
planet_vargas_input[pn][vt] = DignityLevel.NEUTRAL
|
||||
@@ -3876,7 +3979,7 @@ def cmd_full_reading(args):
|
||||
# fallback: D1 从 planet_lons 计算
|
||||
p_sign = SIGNS[int(planet_lons.get(pn, 0) / 30) % 12]
|
||||
d_in_s = planet_lons.get(pn, 0) % 30
|
||||
dl_key = _get_dignity_level(pn, p_sign, d_in_s)
|
||||
dl_key = _get_dignity_level(pn, p_sign, d_in_s, planets)
|
||||
planet_vargas_input[pn][vt] = dignity_map.get(dl_key, DignityLevel.NEUTRAL)
|
||||
else:
|
||||
planet_vargas_input[pn][vt] = DignityLevel.NEUTRAL
|
||||
@@ -4260,6 +4363,7 @@ def cmd_full_reading(args):
|
||||
d9_data = varga_data.get('D9_Navamsa', {}) if isinstance(varga_data, dict) else {}
|
||||
d9_asc = d9_data.get('Ascendant', {}) if isinstance(d9_data, dict) else {}
|
||||
d9_asc_idx = SIGNS.index(d9_asc.get('sign', 'Aries')) if isinstance(d9_asc, dict) and d9_asc.get('sign') in SIGNS else 0
|
||||
d9_context = _build_dignity_context(d9_data)
|
||||
if isinstance(d9_data, dict):
|
||||
for d9_pn, d9_pd in d9_data.items():
|
||||
if d9_pn == '_meta' or d9_pn == 'Ascendant' or not isinstance(d9_pd, dict) or 'sign' not in d9_pd:
|
||||
@@ -4271,7 +4375,7 @@ def cmd_full_reading(args):
|
||||
'sign': d9_sign,
|
||||
'house': ((d9_sign_idx - d9_asc_idx) % 12) + 1,
|
||||
'degree_in_sign': d9_deg,
|
||||
'dignity': _get_dignity_level(d9_pn, d9_sign, d9_deg),
|
||||
'dignity': _get_dignity_level(d9_pn, d9_sign, d9_deg, d9_context),
|
||||
}
|
||||
jaimini_result['darakaraka'] = analyze_darakaraka({
|
||||
'ascendant': chart.get('ascendant', {}),
|
||||
@@ -4578,12 +4682,13 @@ def cmd_full_reading(args):
|
||||
d9_data = varga_data.get('D9_Navamsa', {}) if isinstance(varga_data, dict) else {}
|
||||
if d9_data:
|
||||
d9_expanded = {}
|
||||
d9_context = _build_dignity_context(d9_data)
|
||||
for pn, pd in d9_data.items():
|
||||
if pn == '_meta' or not isinstance(pd, dict) or 'sign' not in pd:
|
||||
continue
|
||||
d9_sign = pd['sign']
|
||||
d9_deg = pd.get('degree_in_sign', pd.get('degree', 0) % 30)
|
||||
dignity = _get_dignity_level(pn, d9_sign, d9_deg)
|
||||
dignity = _get_dignity_level(pn, d9_sign, d9_deg, d9_context)
|
||||
# D9 宫位(从D9 Asc计算)
|
||||
d9_asc_data = d9_data.get('Ascendant', {})
|
||||
d9_asc_sign_idx = SIGNS.index(d9_asc_data.get('sign', 'Aries')) if isinstance(d9_asc_data, dict) and d9_asc_data.get('sign') in SIGNS else 0
|
||||
|
||||
+46
-2
@@ -126,7 +126,7 @@ def test_chart_accepts_second_and_preserves_birth_time_precision() -> None:
|
||||
assert with_seconds["birth_info"]["julian_day"] > without_seconds["birth_info"]["julian_day"]
|
||||
|
||||
|
||||
def test_chart_reports_friend_and_enemy_sign_dignity_for_user_case() -> None:
|
||||
def test_chart_reports_richer_d1_dignity_labels_for_user_case() -> None:
|
||||
result = run_engine(
|
||||
"chart",
|
||||
"--year", "REDACTED_YEAR",
|
||||
@@ -141,7 +141,7 @@ def test_chart_reports_friend_and_enemy_sign_dignity_for_user_case() -> None:
|
||||
)
|
||||
|
||||
assert result["planets"]["Jupiter"]["sign"] == "Virgo"
|
||||
assert result["planets"]["Jupiter"]["status"] == "入敌(Enemy Sign)"
|
||||
assert result["planets"]["Jupiter"]["status"] == "极敌(Great Enemy)"
|
||||
|
||||
|
||||
def test_chart_table_mode_prints_readable_ascii_table() -> None:
|
||||
@@ -330,6 +330,50 @@ def test_full_reading_exposes_sensitive_point_modules() -> None:
|
||||
assert payload["is_sarpa_drekkana"] is True
|
||||
|
||||
|
||||
def test_full_reading_uses_d9_context_for_navamsa_dignity() -> None:
|
||||
result = run_engine("full-reading", *BASE_BIRTH_ARGS)
|
||||
|
||||
d9_jupiter = result["modules"]["d9_navamsa_expanded"]["Jupiter"]
|
||||
assert d9_jupiter["sign"] == "Capricorn"
|
||||
assert d9_jupiter["dignity"] == "NEECHA_BHANGA"
|
||||
|
||||
|
||||
def test_full_reading_uses_d9_context_for_darakaraka_dignity() -> None:
|
||||
result = run_engine(
|
||||
"full-reading",
|
||||
"--year", "1992",
|
||||
"--month", "8",
|
||||
"--day", "25",
|
||||
"--hour", "23",
|
||||
"--minute", "10",
|
||||
"--lat", "35.6895",
|
||||
"--lon", "139.6917",
|
||||
"--tz", "9",
|
||||
)
|
||||
|
||||
darakaraka = result["modules"]["jaimini"]["darakaraka"]
|
||||
assert darakaraka["dk_planet"] == "Sun"
|
||||
assert darakaraka["d9_sign"] == "Gemini"
|
||||
assert darakaraka["d9_dignity"] == "ENEMY"
|
||||
|
||||
|
||||
def test_full_reading_uses_d9_context_for_vimsopaka_navamsa_dignity() -> None:
|
||||
result = run_engine(
|
||||
"full-reading",
|
||||
"--year", "1992",
|
||||
"--month", "8",
|
||||
"--day", "25",
|
||||
"--hour", "23",
|
||||
"--minute", "10",
|
||||
"--lat", "35.6895",
|
||||
"--lon", "139.6917",
|
||||
"--tz", "9",
|
||||
)
|
||||
|
||||
navamsa = result["modules"]["vimsopaka"]["Sun"]["varga_scores"]["Navamsa"]
|
||||
assert navamsa["dignity"] == "Enemy"
|
||||
|
||||
|
||||
def test_varga_cli_outputs_d9_and_d10() -> None:
|
||||
result = run_engine("varga", *BASE_BIRTH_ARGS, "--d9", "--d10")
|
||||
charts = result.get("divisional_charts", {})
|
||||
|
||||
@@ -91,6 +91,43 @@ class TestDignityLevel(unittest.TestCase):
|
||||
result = _get_dignity_level(planet, sign)
|
||||
self.assertEqual(result, 'DEBILITATED', f"{planet} in {sign} should be DEBILITATED, got {result}")
|
||||
|
||||
def test_temporary_relationship_depends_on_passed_chart_context(self):
|
||||
"""同一落位若误用 D1 planets_data,会改变分盘 dignity 结果。"""
|
||||
d1_like_context = {
|
||||
'Jupiter': {'sign': 'Virgo'},
|
||||
'Mercury': {'sign': 'Libra'},
|
||||
}
|
||||
d9_like_context = {
|
||||
'Jupiter': {'sign': 'Virgo'},
|
||||
'Mercury': {'sign': 'Capricorn'},
|
||||
}
|
||||
d1_result = _get_dignity_level('Jupiter', 'Virgo', 10.0, d1_like_context)
|
||||
d9_result = _get_dignity_level('Jupiter', 'Virgo', 10.0, d9_like_context)
|
||||
|
||||
self.assertEqual(d1_result, 'NEUTRAL')
|
||||
self.assertEqual(d9_result, 'GREAT_ENEMY')
|
||||
|
||||
def test_neecha_bhanga_depends_on_passed_chart_context(self):
|
||||
"""落陷取消必须依赖当前分盘上下文,而不是借用 D1 planets_data。"""
|
||||
with_cancelling_dispositor = {
|
||||
'Mercury': {'sign': 'Pisces'},
|
||||
'Jupiter': {'sign': 'Cancer'},
|
||||
'Venus': {'sign': 'Aries'},
|
||||
}
|
||||
without_cancelling_dispositor = {
|
||||
'Mercury': {'sign': 'Pisces'},
|
||||
'Jupiter': {'sign': 'Capricorn'},
|
||||
'Venus': {'sign': 'Aries'},
|
||||
}
|
||||
self.assertEqual(
|
||||
_get_dignity_level('Mercury', 'Pisces', 12.0, with_cancelling_dispositor),
|
||||
'NEECHA_BHANGA',
|
||||
)
|
||||
self.assertEqual(
|
||||
_get_dignity_level('Mercury', 'Pisces', 12.0, without_cancelling_dispositor),
|
||||
'DEBILITATED',
|
||||
)
|
||||
|
||||
|
||||
class TestDashaCalculation(unittest.TestCase):
|
||||
"""测试 Dasha 时间线计算"""
|
||||
|
||||
Reference in New Issue
Block a user