kundali-chart-mcp 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +67 -0
- package/azure-function/function_app.py +93 -0
- package/azure-function/host.json +15 -0
- package/azure-function/kundali_bridge.py +952 -0
- package/azure-function/python/kundali_lib/__init__.py +1 -0
- package/azure-function/python/kundali_lib/__pycache__/__init__.cpython-313.pyc +0 -0
- package/azure-function/python/kundali_lib/__pycache__/ephemeris.cpython-313.pyc +0 -0
- package/azure-function/python/kundali_lib/__pycache__/geocoder.cpython-313.pyc +0 -0
- package/azure-function/python/kundali_lib/__pycache__/vedicastro_bridge.cpython-313.pyc +0 -0
- package/azure-function/python/kundali_lib/ephemeris.py +30 -0
- package/azure-function/python/kundali_lib/geocoder.py +82 -0
- package/azure-function/python/kundali_lib/vedic/__init__.py +1 -0
- package/azure-function/python/kundali_lib/vedic/__pycache__/__init__.cpython-313.pyc +0 -0
- package/azure-function/python/kundali_lib/vedic/__pycache__/arishta.cpython-313.pyc +0 -0
- package/azure-function/python/kundali_lib/vedic/__pycache__/ashtakavarga.cpython-313.pyc +0 -0
- package/azure-function/python/kundali_lib/vedic/__pycache__/avasthas.cpython-313.pyc +0 -0
- package/azure-function/python/kundali_lib/vedic/__pycache__/ayanamsa.cpython-313.pyc +0 -0
- package/azure-function/python/kundali_lib/vedic/__pycache__/bhava_chalit.cpython-313.pyc +0 -0
- package/azure-function/python/kundali_lib/vedic/__pycache__/char_dasha.cpython-313.pyc +0 -0
- package/azure-function/python/kundali_lib/vedic/__pycache__/chart.cpython-313.pyc +0 -0
- package/azure-function/python/kundali_lib/vedic/__pycache__/chart_types.cpython-313.pyc +0 -0
- package/azure-function/python/kundali_lib/vedic/__pycache__/compatibility.cpython-313.pyc +0 -0
- package/azure-function/python/kundali_lib/vedic/__pycache__/constants.cpython-313.pyc +0 -0
- package/azure-function/python/kundali_lib/vedic/__pycache__/dasha_extended.cpython-313.pyc +0 -0
- package/azure-function/python/kundali_lib/vedic/__pycache__/dasha_systems.cpython-313.pyc +0 -0
- package/azure-function/python/kundali_lib/vedic/__pycache__/doshas.cpython-313.pyc +0 -0
- package/azure-function/python/kundali_lib/vedic/__pycache__/gandanta.cpython-313.pyc +0 -0
- package/azure-function/python/kundali_lib/vedic/__pycache__/gochara.cpython-313.pyc +0 -0
- package/azure-function/python/kundali_lib/vedic/__pycache__/hora.cpython-313.pyc +0 -0
- package/azure-function/python/kundali_lib/vedic/__pycache__/houses.cpython-313.pyc +0 -0
- package/azure-function/python/kundali_lib/vedic/__pycache__/jaimini.cpython-313.pyc +0 -0
- package/azure-function/python/kundali_lib/vedic/__pycache__/kalachakra.cpython-313.pyc +0 -0
- package/azure-function/python/kundali_lib/vedic/__pycache__/kartari.cpython-313.pyc +0 -0
- package/azure-function/python/kundali_lib/vedic/__pycache__/kurmachakra.cpython-313.pyc +0 -0
- package/azure-function/python/kundali_lib/vedic/__pycache__/lunar_return.cpython-313.pyc +0 -0
- package/azure-function/python/kundali_lib/vedic/__pycache__/muhurta.cpython-313.pyc +0 -0
- package/azure-function/python/kundali_lib/vedic/__pycache__/nabhasha.cpython-313.pyc +0 -0
- package/azure-function/python/kundali_lib/vedic/__pycache__/nakshatra_details.cpython-313.pyc +0 -0
- package/azure-function/python/kundali_lib/vedic/__pycache__/panchanga.cpython-313.pyc +0 -0
- package/azure-function/python/kundali_lib/vedic/__pycache__/planets.cpython-313.pyc +0 -0
- package/azure-function/python/kundali_lib/vedic/__pycache__/shadbala.cpython-313.pyc +0 -0
- package/azure-function/python/kundali_lib/vedic/__pycache__/special_conditions.cpython-313.pyc +0 -0
- package/azure-function/python/kundali_lib/vedic/__pycache__/sudarshana.cpython-313.pyc +0 -0
- package/azure-function/python/kundali_lib/vedic/__pycache__/tajaka.cpython-313.pyc +0 -0
- package/azure-function/python/kundali_lib/vedic/__pycache__/upagrahas.cpython-313.pyc +0 -0
- package/azure-function/python/kundali_lib/vedic/__pycache__/varshaphal.cpython-313.pyc +0 -0
- package/azure-function/python/kundali_lib/vedic/__pycache__/yogas.cpython-313.pyc +0 -0
- package/azure-function/python/kundali_lib/vedic/__pycache__/zodiac.cpython-313.pyc +0 -0
- package/azure-function/python/kundali_lib/vedic/arishta.py +465 -0
- package/azure-function/python/kundali_lib/vedic/ashtakavarga.py +213 -0
- package/azure-function/python/kundali_lib/vedic/avasthas.py +292 -0
- package/azure-function/python/kundali_lib/vedic/ayanamsa.py +106 -0
- package/azure-function/python/kundali_lib/vedic/bhava_chalit.py +137 -0
- package/azure-function/python/kundali_lib/vedic/char_dasha.py +308 -0
- package/azure-function/python/kundali_lib/vedic/chart.py +126 -0
- package/azure-function/python/kundali_lib/vedic/chart_types.py +338 -0
- package/azure-function/python/kundali_lib/vedic/compatibility.py +705 -0
- package/azure-function/python/kundali_lib/vedic/constants.py +108 -0
- package/azure-function/python/kundali_lib/vedic/dasha_extended.py +262 -0
- package/azure-function/python/kundali_lib/vedic/dasha_systems.py +439 -0
- package/azure-function/python/kundali_lib/vedic/doshas.py +453 -0
- package/azure-function/python/kundali_lib/vedic/gandanta.py +213 -0
- package/azure-function/python/kundali_lib/vedic/gochara.py +277 -0
- package/azure-function/python/kundali_lib/vedic/hora.py +263 -0
- package/azure-function/python/kundali_lib/vedic/houses.py +30 -0
- package/azure-function/python/kundali_lib/vedic/jaimini.py +361 -0
- package/azure-function/python/kundali_lib/vedic/kalachakra.py +226 -0
- package/azure-function/python/kundali_lib/vedic/kartari.py +243 -0
- package/azure-function/python/kundali_lib/vedic/kurmachakra.py +383 -0
- package/azure-function/python/kundali_lib/vedic/lunar_return.py +402 -0
- package/azure-function/python/kundali_lib/vedic/muhurta.py +414 -0
- package/azure-function/python/kundali_lib/vedic/nabhasha.py +349 -0
- package/azure-function/python/kundali_lib/vedic/nakshatra_details.py +945 -0
- package/azure-function/python/kundali_lib/vedic/panchanga.py +297 -0
- package/azure-function/python/kundali_lib/vedic/planets.py +55 -0
- package/azure-function/python/kundali_lib/vedic/shadbala.py +500 -0
- package/azure-function/python/kundali_lib/vedic/special_conditions.py +319 -0
- package/azure-function/python/kundali_lib/vedic/sudarshana.py +232 -0
- package/azure-function/python/kundali_lib/vedic/tajaka.py +482 -0
- package/azure-function/python/kundali_lib/vedic/upagrahas.py +229 -0
- package/azure-function/python/kundali_lib/vedic/varshaphal.py +185 -0
- package/azure-function/python/kundali_lib/vedic/yogas.py +935 -0
- package/azure-function/python/kundali_lib/vedic/zodiac.py +42 -0
- package/azure-function/python/kundali_lib/vedicastro_bridge.py +198 -0
- package/azure-function/requirements.txt +9 -0
- package/index.js +747 -0
- package/kundali-chart-mcp.js +159 -0
- package/kundali_bridge.py +952 -0
- package/package.json +41 -0
- package/python/kundali_lib/__init__.py +1 -0
- package/python/kundali_lib/__pycache__/__init__.cpython-313.pyc +0 -0
- package/python/kundali_lib/__pycache__/ephemeris.cpython-313.pyc +0 -0
- package/python/kundali_lib/__pycache__/geocoder.cpython-313.pyc +0 -0
- package/python/kundali_lib/__pycache__/vedicastro_bridge.cpython-313.pyc +0 -0
- package/python/kundali_lib/ephemeris.py +30 -0
- package/python/kundali_lib/geocoder.py +82 -0
- package/python/kundali_lib/vedic/__init__.py +1 -0
- package/python/kundali_lib/vedic/__pycache__/__init__.cpython-313.pyc +0 -0
- package/python/kundali_lib/vedic/__pycache__/arishta.cpython-313.pyc +0 -0
- package/python/kundali_lib/vedic/__pycache__/ashtakavarga.cpython-313.pyc +0 -0
- package/python/kundali_lib/vedic/__pycache__/avasthas.cpython-313.pyc +0 -0
- package/python/kundali_lib/vedic/__pycache__/ayanamsa.cpython-313.pyc +0 -0
- package/python/kundali_lib/vedic/__pycache__/bhava_chalit.cpython-313.pyc +0 -0
- package/python/kundali_lib/vedic/__pycache__/char_dasha.cpython-313.pyc +0 -0
- package/python/kundali_lib/vedic/__pycache__/chart.cpython-313.pyc +0 -0
- package/python/kundali_lib/vedic/__pycache__/chart_types.cpython-313.pyc +0 -0
- package/python/kundali_lib/vedic/__pycache__/compatibility.cpython-313.pyc +0 -0
- package/python/kundali_lib/vedic/__pycache__/constants.cpython-313.pyc +0 -0
- package/python/kundali_lib/vedic/__pycache__/dasha_extended.cpython-313.pyc +0 -0
- package/python/kundali_lib/vedic/__pycache__/dasha_systems.cpython-313.pyc +0 -0
- package/python/kundali_lib/vedic/__pycache__/doshas.cpython-313.pyc +0 -0
- package/python/kundali_lib/vedic/__pycache__/gandanta.cpython-313.pyc +0 -0
- package/python/kundali_lib/vedic/__pycache__/gochara.cpython-313.pyc +0 -0
- package/python/kundali_lib/vedic/__pycache__/hora.cpython-313.pyc +0 -0
- package/python/kundali_lib/vedic/__pycache__/houses.cpython-313.pyc +0 -0
- package/python/kundali_lib/vedic/__pycache__/jaimini.cpython-313.pyc +0 -0
- package/python/kundali_lib/vedic/__pycache__/kalachakra.cpython-313.pyc +0 -0
- package/python/kundali_lib/vedic/__pycache__/kartari.cpython-313.pyc +0 -0
- package/python/kundali_lib/vedic/__pycache__/kurmachakra.cpython-313.pyc +0 -0
- package/python/kundali_lib/vedic/__pycache__/lunar_return.cpython-313.pyc +0 -0
- package/python/kundali_lib/vedic/__pycache__/muhurta.cpython-313.pyc +0 -0
- package/python/kundali_lib/vedic/__pycache__/nabhasha.cpython-313.pyc +0 -0
- package/python/kundali_lib/vedic/__pycache__/nakshatra_details.cpython-313.pyc +0 -0
- package/python/kundali_lib/vedic/__pycache__/panchanga.cpython-313.pyc +0 -0
- package/python/kundali_lib/vedic/__pycache__/planets.cpython-313.pyc +0 -0
- package/python/kundali_lib/vedic/__pycache__/shadbala.cpython-313.pyc +0 -0
- package/python/kundali_lib/vedic/__pycache__/special_conditions.cpython-313.pyc +0 -0
- package/python/kundali_lib/vedic/__pycache__/sudarshana.cpython-313.pyc +0 -0
- package/python/kundali_lib/vedic/__pycache__/tajaka.cpython-313.pyc +0 -0
- package/python/kundali_lib/vedic/__pycache__/upagrahas.cpython-313.pyc +0 -0
- package/python/kundali_lib/vedic/__pycache__/varshaphal.cpython-313.pyc +0 -0
- package/python/kundali_lib/vedic/__pycache__/yogas.cpython-313.pyc +0 -0
- package/python/kundali_lib/vedic/__pycache__/zodiac.cpython-313.pyc +0 -0
- package/python/kundali_lib/vedic/arishta.py +465 -0
- package/python/kundali_lib/vedic/ashtakavarga.py +213 -0
- package/python/kundali_lib/vedic/avasthas.py +292 -0
- package/python/kundali_lib/vedic/ayanamsa.py +106 -0
- package/python/kundali_lib/vedic/bhava_chalit.py +137 -0
- package/python/kundali_lib/vedic/char_dasha.py +308 -0
- package/python/kundali_lib/vedic/chart.py +126 -0
- package/python/kundali_lib/vedic/chart_types.py +338 -0
- package/python/kundali_lib/vedic/compatibility.py +705 -0
- package/python/kundali_lib/vedic/constants.py +108 -0
- package/python/kundali_lib/vedic/dasha_extended.py +262 -0
- package/python/kundali_lib/vedic/dasha_systems.py +439 -0
- package/python/kundali_lib/vedic/doshas.py +453 -0
- package/python/kundali_lib/vedic/gandanta.py +213 -0
- package/python/kundali_lib/vedic/gochara.py +277 -0
- package/python/kundali_lib/vedic/hora.py +263 -0
- package/python/kundali_lib/vedic/houses.py +30 -0
- package/python/kundali_lib/vedic/jaimini.py +361 -0
- package/python/kundali_lib/vedic/kalachakra.py +226 -0
- package/python/kundali_lib/vedic/kartari.py +243 -0
- package/python/kundali_lib/vedic/kurmachakra.py +383 -0
- package/python/kundali_lib/vedic/lunar_return.py +402 -0
- package/python/kundali_lib/vedic/muhurta.py +414 -0
- package/python/kundali_lib/vedic/nabhasha.py +349 -0
- package/python/kundali_lib/vedic/nakshatra_details.py +945 -0
- package/python/kundali_lib/vedic/panchanga.py +297 -0
- package/python/kundali_lib/vedic/planets.py +55 -0
- package/python/kundali_lib/vedic/shadbala.py +500 -0
- package/python/kundali_lib/vedic/special_conditions.py +319 -0
- package/python/kundali_lib/vedic/sudarshana.py +232 -0
- package/python/kundali_lib/vedic/tajaka.py +482 -0
- package/python/kundali_lib/vedic/upagrahas.py +229 -0
- package/python/kundali_lib/vedic/varshaphal.py +185 -0
- package/python/kundali_lib/vedic/yogas.py +935 -0
- package/python/kundali_lib/vedic/zodiac.py +42 -0
- package/python/kundali_lib/vedicastro_bridge.py +198 -0
- package/remote-server.js +590 -0
- package/requirements.txt +8 -0
- package/setup.sh +218 -0
|
@@ -0,0 +1,453 @@
|
|
|
1
|
+
"""Vedic astrological doshas: Mangal, Kaal Sarp, Sade Sati, and others."""
|
|
2
|
+
|
|
3
|
+
# ---------------------------------------------------------------------------
|
|
4
|
+
# Constants
|
|
5
|
+
# ---------------------------------------------------------------------------
|
|
6
|
+
|
|
7
|
+
RASHIS = [
|
|
8
|
+
"Aries",
|
|
9
|
+
"Taurus",
|
|
10
|
+
"Gemini",
|
|
11
|
+
"Cancer",
|
|
12
|
+
"Leo",
|
|
13
|
+
"Virgo",
|
|
14
|
+
"Libra",
|
|
15
|
+
"Scorpio",
|
|
16
|
+
"Sagittarius",
|
|
17
|
+
"Capricorn",
|
|
18
|
+
"Aquarius",
|
|
19
|
+
"Pisces",
|
|
20
|
+
]
|
|
21
|
+
|
|
22
|
+
RASHI_INDEX = {r: i for i, r in enumerate(RASHIS)}
|
|
23
|
+
|
|
24
|
+
MANGAL_DOSHA_HOUSES = {1, 2, 4, 7, 8, 12}
|
|
25
|
+
|
|
26
|
+
DOSHA_LEVEL_MAP = {
|
|
27
|
+
7: "High",
|
|
28
|
+
8: "High",
|
|
29
|
+
1: "Medium",
|
|
30
|
+
4: "Medium",
|
|
31
|
+
12: "Medium",
|
|
32
|
+
2: "Low",
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
KAAL_SARP_NAMES = {
|
|
36
|
+
"Aries": "Anant",
|
|
37
|
+
"Taurus": "Kulik",
|
|
38
|
+
"Gemini": "Vasuki",
|
|
39
|
+
"Cancer": "Shankhpal",
|
|
40
|
+
"Leo": "Padma",
|
|
41
|
+
"Virgo": "Mahapadma",
|
|
42
|
+
"Libra": "Takshak",
|
|
43
|
+
"Scorpio": "Karkotak",
|
|
44
|
+
"Sagittarius": "Shankhnaad",
|
|
45
|
+
"Capricorn": "Patak",
|
|
46
|
+
"Aquarius": "Vishakta",
|
|
47
|
+
"Pisces": "Sheshnag",
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
# Combustion thresholds in degrees (direct, retrograde)
|
|
51
|
+
COMBUSTION_THRESHOLDS = {
|
|
52
|
+
"Moon": (12, 12),
|
|
53
|
+
"Mercury": (14, 12),
|
|
54
|
+
"Venus": (10, 8),
|
|
55
|
+
"Mars": (17, 17),
|
|
56
|
+
"Jupiter": (11, 11),
|
|
57
|
+
"Saturn": (15, 15),
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
MAIN_PLANETS = ["Sun", "Moon", "Mars", "Mercury", "Jupiter", "Venus", "Saturn"]
|
|
61
|
+
WAR_PLANETS = ["Mars", "Mercury", "Jupiter", "Venus", "Saturn"]
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
# ---------------------------------------------------------------------------
|
|
65
|
+
# Helpers
|
|
66
|
+
# ---------------------------------------------------------------------------
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def _get_planet(positions: list, name: str) -> dict | None:
|
|
70
|
+
for p in positions:
|
|
71
|
+
if p.get("name", "").lower() == name.lower():
|
|
72
|
+
return p
|
|
73
|
+
return None
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def _angular_sep(lon_a: float, lon_b: float) -> float:
|
|
77
|
+
"""Shortest angular distance between two longitudes (0-180)."""
|
|
78
|
+
diff = abs(lon_a - lon_b) % 360
|
|
79
|
+
return diff if diff <= 180 else 360 - diff
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def _lon_between_clockwise(lon: float, start: float, end: float) -> bool:
|
|
83
|
+
"""Return True if *lon* lies in the arc from *start* to *end* going clockwise
|
|
84
|
+
(i.e. increasing longitude modulo 360)."""
|
|
85
|
+
lon = lon % 360
|
|
86
|
+
start = start % 360
|
|
87
|
+
end = end % 360
|
|
88
|
+
if start <= end:
|
|
89
|
+
return start <= lon <= end
|
|
90
|
+
# Arc wraps around 0°
|
|
91
|
+
return lon >= start or lon <= end
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def _rashi_from_lon(lon: float) -> str:
|
|
95
|
+
return RASHIS[int(lon % 360 / 30)]
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
# ---------------------------------------------------------------------------
|
|
99
|
+
# 1. Mangal Dosha
|
|
100
|
+
# ---------------------------------------------------------------------------
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def check_mangal_dosha(planetary_positions: list, ascendant: dict) -> dict:
|
|
104
|
+
"""Check for Mangal (Kuja) Dosha.
|
|
105
|
+
|
|
106
|
+
Parameters
|
|
107
|
+
----------
|
|
108
|
+
planetary_positions : list
|
|
109
|
+
List of planet dicts from build_chart().
|
|
110
|
+
ascendant : dict
|
|
111
|
+
Ascendant dict (must contain at least a ``longitude`` key, or a
|
|
112
|
+
``rashi`` key, from build_chart()).
|
|
113
|
+
|
|
114
|
+
Returns
|
|
115
|
+
-------
|
|
116
|
+
dict
|
|
117
|
+
has_dosha, mars_house, affected_houses, dosha_level, cancellation_notes,
|
|
118
|
+
severity.
|
|
119
|
+
"""
|
|
120
|
+
mars = _get_planet(planetary_positions, "Mars")
|
|
121
|
+
if mars is None:
|
|
122
|
+
return {
|
|
123
|
+
"has_dosha": False,
|
|
124
|
+
"mars_house": None,
|
|
125
|
+
"affected_houses": list(MANGAL_DOSHA_HOUSES),
|
|
126
|
+
"dosha_level": "None",
|
|
127
|
+
"cancellation_notes": ["Mars not found in chart"],
|
|
128
|
+
"severity": "None",
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
mars_house = mars.get("house")
|
|
132
|
+
has_dosha = mars_house in MANGAL_DOSHA_HOUSES
|
|
133
|
+
dosha_level = DOSHA_LEVEL_MAP.get(mars_house, "None") if has_dosha else "None"
|
|
134
|
+
|
|
135
|
+
# --- Cancellation conditions ---
|
|
136
|
+
cancellation_notes: list[str] = []
|
|
137
|
+
|
|
138
|
+
mars_rashi = mars.get("rashi", "")
|
|
139
|
+
if mars_rashi in ("Aries", "Scorpio"):
|
|
140
|
+
cancellation_notes.append(
|
|
141
|
+
f"Mars is in its own sign ({mars_rashi}) — Dosha is cancelled."
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
if mars_rashi == "Capricorn":
|
|
145
|
+
cancellation_notes.append("Mars is exalted in Capricorn — Dosha is cancelled.")
|
|
146
|
+
|
|
147
|
+
# Jupiter aspecting Mars: Jupiter aspects house +5 and +9 (and its own house)
|
|
148
|
+
jupiter = _get_planet(planetary_positions, "Jupiter")
|
|
149
|
+
if jupiter is not None:
|
|
150
|
+
jup_house = jupiter.get("house", 0)
|
|
151
|
+
# Jupiter's 5th and 9th aspects (full aspect houses)
|
|
152
|
+
jup_aspects = {
|
|
153
|
+
jup_house,
|
|
154
|
+
(jup_house + 4) % 12 or 12,
|
|
155
|
+
(jup_house + 8) % 12 or 12,
|
|
156
|
+
}
|
|
157
|
+
if mars_house in jup_aspects:
|
|
158
|
+
cancellation_notes.append(
|
|
159
|
+
f"Jupiter (house {jup_house}) aspects Mars — Dosha is mitigated."
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
moon = _get_planet(planetary_positions, "Moon")
|
|
163
|
+
if moon is not None and moon.get("house") == mars_house:
|
|
164
|
+
cancellation_notes.append("Moon is conjunct Mars — Dosha is mitigated.")
|
|
165
|
+
|
|
166
|
+
severity: str
|
|
167
|
+
if cancellation_notes and any("cancelled" in n.lower() for n in cancellation_notes):
|
|
168
|
+
severity = "Cancelled"
|
|
169
|
+
elif not has_dosha:
|
|
170
|
+
severity = "None"
|
|
171
|
+
else:
|
|
172
|
+
severity = dosha_level # "High", "Medium", or "Low"
|
|
173
|
+
|
|
174
|
+
return {
|
|
175
|
+
"has_dosha": has_dosha,
|
|
176
|
+
"mars_house": mars_house,
|
|
177
|
+
"affected_houses": sorted(MANGAL_DOSHA_HOUSES),
|
|
178
|
+
"dosha_level": dosha_level,
|
|
179
|
+
"cancellation_notes": cancellation_notes,
|
|
180
|
+
"severity": severity,
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
# ---------------------------------------------------------------------------
|
|
185
|
+
# 2. Kaal Sarp Dosha
|
|
186
|
+
# ---------------------------------------------------------------------------
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
def check_kaal_sarp_dosha(planetary_positions: list) -> dict:
|
|
190
|
+
"""Check for Kaal Sarp / Kaal Amrit Yoga.
|
|
191
|
+
|
|
192
|
+
Parameters
|
|
193
|
+
----------
|
|
194
|
+
planetary_positions : list
|
|
195
|
+
List of planet dicts from build_chart().
|
|
196
|
+
|
|
197
|
+
Returns
|
|
198
|
+
-------
|
|
199
|
+
dict
|
|
200
|
+
has_dosha, dosha_type, rahu_longitude, ketu_longitude,
|
|
201
|
+
planets_inside, planets_outside, name.
|
|
202
|
+
"""
|
|
203
|
+
rahu = _get_planet(planetary_positions, "Rahu")
|
|
204
|
+
if rahu is None:
|
|
205
|
+
return {
|
|
206
|
+
"has_dosha": False,
|
|
207
|
+
"dosha_type": "None",
|
|
208
|
+
"rahu_longitude": None,
|
|
209
|
+
"ketu_longitude": None,
|
|
210
|
+
"planets_inside": [],
|
|
211
|
+
"planets_outside": list(MAIN_PLANETS),
|
|
212
|
+
"name": "None",
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
rahu_lon = rahu.get("longitude", 0.0) % 360
|
|
216
|
+
ketu_lon = (rahu_lon + 180) % 360
|
|
217
|
+
|
|
218
|
+
planets_inside: list[str] = []
|
|
219
|
+
planets_outside: list[str] = []
|
|
220
|
+
|
|
221
|
+
for pname in MAIN_PLANETS:
|
|
222
|
+
p = _get_planet(planetary_positions, pname)
|
|
223
|
+
if p is None:
|
|
224
|
+
planets_outside.append(pname)
|
|
225
|
+
continue
|
|
226
|
+
plon = p.get("longitude", 0.0) % 360
|
|
227
|
+
if _lon_between_clockwise(plon, rahu_lon, ketu_lon):
|
|
228
|
+
planets_inside.append(pname)
|
|
229
|
+
else:
|
|
230
|
+
planets_outside.append(pname)
|
|
231
|
+
|
|
232
|
+
all_inside_clockwise = len(planets_outside) == 0
|
|
233
|
+
# Kaal Amrit: all planets in the arc from Ketu → Rahu (counter-clockwise)
|
|
234
|
+
planets_inside_ccw: list[str] = []
|
|
235
|
+
planets_outside_ccw: list[str] = []
|
|
236
|
+
for pname in MAIN_PLANETS:
|
|
237
|
+
p = _get_planet(planetary_positions, pname)
|
|
238
|
+
if p is None:
|
|
239
|
+
planets_outside_ccw.append(pname)
|
|
240
|
+
continue
|
|
241
|
+
plon = p.get("longitude", 0.0) % 360
|
|
242
|
+
if _lon_between_clockwise(plon, ketu_lon, rahu_lon):
|
|
243
|
+
planets_inside_ccw.append(pname)
|
|
244
|
+
else:
|
|
245
|
+
planets_outside_ccw.append(pname)
|
|
246
|
+
|
|
247
|
+
all_inside_ccw = len(planets_outside_ccw) == 0
|
|
248
|
+
|
|
249
|
+
rahu_rashi = _rashi_from_lon(rahu_lon)
|
|
250
|
+
ksd_name = KAAL_SARP_NAMES.get(rahu_rashi, "Unknown")
|
|
251
|
+
|
|
252
|
+
if all_inside_clockwise:
|
|
253
|
+
dosha_type = "Kaal Sarp"
|
|
254
|
+
has_dosha = True
|
|
255
|
+
elif all_inside_ccw:
|
|
256
|
+
dosha_type = "Kaal Amrit"
|
|
257
|
+
has_dosha = True
|
|
258
|
+
planets_inside = planets_inside_ccw
|
|
259
|
+
planets_outside = planets_outside_ccw
|
|
260
|
+
else:
|
|
261
|
+
dosha_type = "None"
|
|
262
|
+
has_dosha = False
|
|
263
|
+
|
|
264
|
+
return {
|
|
265
|
+
"has_dosha": has_dosha,
|
|
266
|
+
"dosha_type": dosha_type,
|
|
267
|
+
"rahu_longitude": round(rahu_lon, 4),
|
|
268
|
+
"ketu_longitude": round(ketu_lon, 4),
|
|
269
|
+
"planets_inside": planets_inside,
|
|
270
|
+
"planets_outside": planets_outside,
|
|
271
|
+
"name": ksd_name if has_dosha else "None",
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
# ---------------------------------------------------------------------------
|
|
276
|
+
# 3. Sade Sati
|
|
277
|
+
# ---------------------------------------------------------------------------
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
def check_sade_sati(natal_moon_rashi: str, current_saturn_rashi: str) -> dict:
|
|
281
|
+
"""Check whether Saturn's transit triggers Sade Sati for the natal Moon sign.
|
|
282
|
+
|
|
283
|
+
Parameters
|
|
284
|
+
----------
|
|
285
|
+
natal_moon_rashi : str
|
|
286
|
+
Rashi of Moon at birth (e.g. "Taurus").
|
|
287
|
+
current_saturn_rashi : str
|
|
288
|
+
Rashi Saturn currently occupies (transit chart).
|
|
289
|
+
|
|
290
|
+
Returns
|
|
291
|
+
-------
|
|
292
|
+
dict
|
|
293
|
+
is_sade_sati, phase, natal_moon_rashi, current_saturn_rashi, description.
|
|
294
|
+
"""
|
|
295
|
+
if natal_moon_rashi not in RASHI_INDEX or current_saturn_rashi not in RASHI_INDEX:
|
|
296
|
+
return {
|
|
297
|
+
"is_sade_sati": False,
|
|
298
|
+
"phase": "None",
|
|
299
|
+
"natal_moon_rashi": natal_moon_rashi,
|
|
300
|
+
"current_saturn_rashi": current_saturn_rashi,
|
|
301
|
+
"description": "Invalid rashi name provided.",
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
moon_idx = RASHI_INDEX[natal_moon_rashi]
|
|
305
|
+
saturn_idx = RASHI_INDEX[current_saturn_rashi]
|
|
306
|
+
|
|
307
|
+
rising_rashi = RASHIS[(moon_idx - 1) % 12]
|
|
308
|
+
peak_rashi = natal_moon_rashi
|
|
309
|
+
setting_rashi = RASHIS[(moon_idx + 1) % 12]
|
|
310
|
+
|
|
311
|
+
if current_saturn_rashi == rising_rashi:
|
|
312
|
+
phase = "Rising"
|
|
313
|
+
description = (
|
|
314
|
+
f"Saturn is transiting {rising_rashi}, the sign before your natal Moon "
|
|
315
|
+
f"sign ({natal_moon_rashi}). This is the Rising phase of Sade Sati — "
|
|
316
|
+
"challenges begin to build gradually."
|
|
317
|
+
)
|
|
318
|
+
is_sade_sati = True
|
|
319
|
+
elif current_saturn_rashi == peak_rashi:
|
|
320
|
+
phase = "Peak"
|
|
321
|
+
description = (
|
|
322
|
+
f"Saturn is transiting your natal Moon sign ({natal_moon_rashi}). "
|
|
323
|
+
"This is the Peak phase of Sade Sati — the most intense period."
|
|
324
|
+
)
|
|
325
|
+
is_sade_sati = True
|
|
326
|
+
elif current_saturn_rashi == setting_rashi:
|
|
327
|
+
phase = "Setting"
|
|
328
|
+
description = (
|
|
329
|
+
f"Saturn is transiting {setting_rashi}, the sign after your natal Moon "
|
|
330
|
+
f"sign ({natal_moon_rashi}). This is the Setting phase of Sade Sati — "
|
|
331
|
+
"pressures gradually ease."
|
|
332
|
+
)
|
|
333
|
+
is_sade_sati = True
|
|
334
|
+
else:
|
|
335
|
+
phase = "None"
|
|
336
|
+
description = (
|
|
337
|
+
f"Saturn ({current_saturn_rashi}) is not transiting through any of the "
|
|
338
|
+
f"three signs of Sade Sati for Moon in {natal_moon_rashi} "
|
|
339
|
+
f"({rising_rashi}, {peak_rashi}, {setting_rashi})."
|
|
340
|
+
)
|
|
341
|
+
is_sade_sati = False
|
|
342
|
+
|
|
343
|
+
return {
|
|
344
|
+
"is_sade_sati": is_sade_sati,
|
|
345
|
+
"phase": phase,
|
|
346
|
+
"natal_moon_rashi": natal_moon_rashi,
|
|
347
|
+
"current_saturn_rashi": current_saturn_rashi,
|
|
348
|
+
"description": description,
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
|
|
352
|
+
# ---------------------------------------------------------------------------
|
|
353
|
+
# 4. Graha Yuddha (Planetary War)
|
|
354
|
+
# ---------------------------------------------------------------------------
|
|
355
|
+
|
|
356
|
+
|
|
357
|
+
def check_graha_yuddha(planetary_positions: list) -> list:
|
|
358
|
+
"""Detect Graha Yuddha (planetary war) between non-luminary planets.
|
|
359
|
+
|
|
360
|
+
Two planets are at war when their longitudes are within 1 degree.
|
|
361
|
+
The planet with the lower longitude wins (or higher latitude, but we use
|
|
362
|
+
longitude as the primary metric since latitude data may be absent).
|
|
363
|
+
|
|
364
|
+
Parameters
|
|
365
|
+
----------
|
|
366
|
+
planetary_positions : list
|
|
367
|
+
List of planet dicts from build_chart().
|
|
368
|
+
|
|
369
|
+
Returns
|
|
370
|
+
-------
|
|
371
|
+
list of dict
|
|
372
|
+
Each entry: planet1, planet2, longitude_diff, winner, loser.
|
|
373
|
+
"""
|
|
374
|
+
wars: list[dict] = []
|
|
375
|
+
war_planets = []
|
|
376
|
+
for pname in WAR_PLANETS:
|
|
377
|
+
p = _get_planet(planetary_positions, pname)
|
|
378
|
+
if p is not None:
|
|
379
|
+
war_planets.append(p)
|
|
380
|
+
|
|
381
|
+
for i in range(len(war_planets)):
|
|
382
|
+
for j in range(i + 1, len(war_planets)):
|
|
383
|
+
p1 = war_planets[i]
|
|
384
|
+
p2 = war_planets[j]
|
|
385
|
+
lon1 = p1.get("longitude", 0.0)
|
|
386
|
+
lon2 = p2.get("longitude", 0.0)
|
|
387
|
+
sep = _angular_sep(lon1, lon2)
|
|
388
|
+
if sep <= 1.0:
|
|
389
|
+
# Winner = lower longitude (closer to 0° of the sign)
|
|
390
|
+
if lon1 <= lon2:
|
|
391
|
+
winner, loser = p1["name"], p2["name"]
|
|
392
|
+
else:
|
|
393
|
+
winner, loser = p2["name"], p1["name"]
|
|
394
|
+
wars.append(
|
|
395
|
+
{
|
|
396
|
+
"planet1": p1["name"],
|
|
397
|
+
"planet2": p2["name"],
|
|
398
|
+
"longitude_diff": round(sep, 4),
|
|
399
|
+
"winner": winner,
|
|
400
|
+
"loser": loser,
|
|
401
|
+
}
|
|
402
|
+
)
|
|
403
|
+
|
|
404
|
+
return wars
|
|
405
|
+
|
|
406
|
+
|
|
407
|
+
# ---------------------------------------------------------------------------
|
|
408
|
+
# 5. Combustion
|
|
409
|
+
# ---------------------------------------------------------------------------
|
|
410
|
+
|
|
411
|
+
|
|
412
|
+
def check_combustion(planetary_positions: list) -> list:
|
|
413
|
+
"""Determine which planets are combust (too close to the Sun).
|
|
414
|
+
|
|
415
|
+
Parameters
|
|
416
|
+
----------
|
|
417
|
+
planetary_positions : list
|
|
418
|
+
List of planet dicts from build_chart().
|
|
419
|
+
|
|
420
|
+
Returns
|
|
421
|
+
-------
|
|
422
|
+
list of dict
|
|
423
|
+
Each entry: planet, sun_longitude, planet_longitude,
|
|
424
|
+
angular_separation, threshold, is_combust.
|
|
425
|
+
"""
|
|
426
|
+
sun = _get_planet(planetary_positions, "Sun")
|
|
427
|
+
if sun is None:
|
|
428
|
+
return []
|
|
429
|
+
|
|
430
|
+
sun_lon = sun.get("longitude", 0.0)
|
|
431
|
+
results: list[dict] = []
|
|
432
|
+
|
|
433
|
+
for pname, (thresh_direct, thresh_retro) in COMBUSTION_THRESHOLDS.items():
|
|
434
|
+
p = _get_planet(planetary_positions, pname)
|
|
435
|
+
if p is None:
|
|
436
|
+
continue
|
|
437
|
+
p_lon = p.get("longitude", 0.0)
|
|
438
|
+
is_retro = bool(p.get("is_retrograde", False))
|
|
439
|
+
threshold = thresh_retro if is_retro else thresh_direct
|
|
440
|
+
sep = _angular_sep(sun_lon, p_lon)
|
|
441
|
+
|
|
442
|
+
results.append(
|
|
443
|
+
{
|
|
444
|
+
"planet": pname,
|
|
445
|
+
"sun_longitude": round(sun_lon, 4),
|
|
446
|
+
"planet_longitude": round(p_lon, 4),
|
|
447
|
+
"angular_separation": round(sep, 4),
|
|
448
|
+
"threshold": threshold,
|
|
449
|
+
"is_combust": sep < threshold,
|
|
450
|
+
}
|
|
451
|
+
)
|
|
452
|
+
|
|
453
|
+
return results
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
"""Gandanta: Junction points between water and fire signs — highly sensitive degrees."""
|
|
2
|
+
|
|
3
|
+
# ---------------------------------------------------------------------------
|
|
4
|
+
# Gandanta zone definitions
|
|
5
|
+
# ---------------------------------------------------------------------------
|
|
6
|
+
# Gandanta occurs at the three water→fire sign junctions.
|
|
7
|
+
# The sensitive zone extends GANDANTA_DEGREES on each side of the cusp.
|
|
8
|
+
|
|
9
|
+
GANDANTA_DEGREES = 3.333 # 3°20' = 1 nakshatra pada
|
|
10
|
+
|
|
11
|
+
# Each zone stores:
|
|
12
|
+
# from_sign / to_sign — the water and fire sign names
|
|
13
|
+
# water_end — absolute longitude of the water-sign cusp (end)
|
|
14
|
+
# fire_start — absolute longitude of the fire-sign cusp (start)
|
|
15
|
+
# nakshatras — (last nak of water sign, first nak of fire sign)
|
|
16
|
+
# description — brief spiritual significance
|
|
17
|
+
_GANDANTA_ZONES = [
|
|
18
|
+
{
|
|
19
|
+
"from_sign": "Cancer",
|
|
20
|
+
"to_sign": "Leo",
|
|
21
|
+
"water_end": 120.0, # end of Cancer (4 × 30)
|
|
22
|
+
"fire_start": 120.0, # start of Leo (5 × 30 — same cusp)
|
|
23
|
+
"nakshatras": ("Ashlesha", "Magha"),
|
|
24
|
+
"description": "Ashlesha–Magha junction: dissolution of ancestral karma and royalty.",
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
"from_sign": "Scorpio",
|
|
28
|
+
"to_sign": "Sagittarius",
|
|
29
|
+
"water_end": 240.0, # end of Scorpio (8 × 30)
|
|
30
|
+
"fire_start": 240.0, # start of Sagittarius (9 × 30)
|
|
31
|
+
"nakshatras": ("Jyeshtha", "Mula"),
|
|
32
|
+
"description": "Jyeshtha–Mula junction: most intense Gandanta; roots torn and replanted.",
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
"from_sign": "Pisces",
|
|
36
|
+
"to_sign": "Aries",
|
|
37
|
+
"water_end": 360.0, # end of Pisces (12 × 30 = 360 = 0)
|
|
38
|
+
"fire_start": 0.0, # start of Aries
|
|
39
|
+
"nakshatras": ("Revati", "Ashwini"),
|
|
40
|
+
"description": "Revati–Ashwini junction: end and beginning of the zodiac; liberation and rebirth.",
|
|
41
|
+
},
|
|
42
|
+
]
|
|
43
|
+
|
|
44
|
+
# ---------------------------------------------------------------------------
|
|
45
|
+
# Helpers
|
|
46
|
+
# ---------------------------------------------------------------------------
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def _normalise(lon: float) -> float:
|
|
50
|
+
return lon % 360
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def _intensity_label(degrees_from_edge: float) -> str:
|
|
54
|
+
"""Classify intensity based on distance from the exact junction."""
|
|
55
|
+
if degrees_from_edge < 1.0:
|
|
56
|
+
return "Severe"
|
|
57
|
+
if degrees_from_edge < 2.0:
|
|
58
|
+
return "Moderate"
|
|
59
|
+
return "Mild"
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def _check_planet_in_gandanta(name: str, lon: float) -> dict | None:
|
|
63
|
+
"""Return a Gandanta entry dict if *lon* falls in any Gandanta zone, else None."""
|
|
64
|
+
lon = _normalise(lon)
|
|
65
|
+
deg_in_sign = lon % 30
|
|
66
|
+
|
|
67
|
+
for zone in _GANDANTA_ZONES:
|
|
68
|
+
cusp = zone["water_end"] # water_end == fire_start (same longitude)
|
|
69
|
+
|
|
70
|
+
# --- Water-sign side: last GANDANTA_DEGREES of the water sign ---
|
|
71
|
+
# The water sign ends at `cusp`; a planet is in the water-side zone when
|
|
72
|
+
# its degree within its sign is > (30 - GANDANTA_DEGREES).
|
|
73
|
+
water_zone_start = cusp - GANDANTA_DEGREES # absolute
|
|
74
|
+
# Handle the Pisces→Aries wrap-around
|
|
75
|
+
if cusp == 360.0 or cusp == 0.0:
|
|
76
|
+
# water sign is Pisces; zone is 356.667° – 360°
|
|
77
|
+
in_water = 356.667 <= lon < 360.0
|
|
78
|
+
dist_water = 360.0 - lon if in_water else None
|
|
79
|
+
else:
|
|
80
|
+
in_water = water_zone_start <= lon < cusp
|
|
81
|
+
dist_water = cusp - lon if in_water else None
|
|
82
|
+
|
|
83
|
+
# --- Fire-sign side: first GANDANTA_DEGREES of the fire sign ---
|
|
84
|
+
if cusp == 360.0 or cusp == 0.0:
|
|
85
|
+
# fire sign is Aries; zone is 0° – 3.333°
|
|
86
|
+
fire_zone_end = GANDANTA_DEGREES
|
|
87
|
+
in_fire = 0.0 <= lon < fire_zone_end
|
|
88
|
+
dist_fire = lon if in_fire else None
|
|
89
|
+
else:
|
|
90
|
+
fire_zone_end = cusp + GANDANTA_DEGREES
|
|
91
|
+
in_fire = cusp <= lon < fire_zone_end
|
|
92
|
+
dist_fire = lon - cusp if in_fire else None
|
|
93
|
+
|
|
94
|
+
if in_water:
|
|
95
|
+
dist = dist_water
|
|
96
|
+
return {
|
|
97
|
+
"planet": name,
|
|
98
|
+
"longitude": round(lon, 4),
|
|
99
|
+
"rashi": zone["from_sign"],
|
|
100
|
+
"degree_in_sign": round(deg_in_sign, 4),
|
|
101
|
+
"gandanta_zone": f"{zone['from_sign']}–{zone['to_sign']} junction",
|
|
102
|
+
"in_water_side": True,
|
|
103
|
+
"in_fire_side": False,
|
|
104
|
+
"intensity": _intensity_label(dist),
|
|
105
|
+
"nakshatras": zone["nakshatras"],
|
|
106
|
+
"significance": zone["description"],
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if in_fire:
|
|
110
|
+
dist = dist_fire
|
|
111
|
+
# For fire side, rashi is the fire sign
|
|
112
|
+
rashis = [
|
|
113
|
+
"Aries",
|
|
114
|
+
"Taurus",
|
|
115
|
+
"Gemini",
|
|
116
|
+
"Cancer",
|
|
117
|
+
"Leo",
|
|
118
|
+
"Virgo",
|
|
119
|
+
"Libra",
|
|
120
|
+
"Scorpio",
|
|
121
|
+
"Sagittarius",
|
|
122
|
+
"Capricorn",
|
|
123
|
+
"Aquarius",
|
|
124
|
+
"Pisces",
|
|
125
|
+
]
|
|
126
|
+
rashi = rashis[int(lon / 30) % 12]
|
|
127
|
+
return {
|
|
128
|
+
"planet": name,
|
|
129
|
+
"longitude": round(lon, 4),
|
|
130
|
+
"rashi": rashi,
|
|
131
|
+
"degree_in_sign": round(deg_in_sign, 4),
|
|
132
|
+
"gandanta_zone": f"{zone['from_sign']}–{zone['to_sign']} junction",
|
|
133
|
+
"in_water_side": False,
|
|
134
|
+
"in_fire_side": True,
|
|
135
|
+
"intensity": _intensity_label(dist),
|
|
136
|
+
"nakshatras": zone["nakshatras"],
|
|
137
|
+
"significance": zone["description"],
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
return None
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
# ---------------------------------------------------------------------------
|
|
144
|
+
# Public API
|
|
145
|
+
# ---------------------------------------------------------------------------
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def check_gandanta(planetary_positions: list, ascendant: dict = None) -> list:
|
|
149
|
+
"""Check which planets (and optionally the ascendant) fall in Gandanta zones.
|
|
150
|
+
|
|
151
|
+
Parameters
|
|
152
|
+
----------
|
|
153
|
+
planetary_positions : list
|
|
154
|
+
Each item is a planet dict with at least ``name`` and ``longitude``.
|
|
155
|
+
ascendant : dict, optional
|
|
156
|
+
Ascendant dict with at least ``longitude``. When supplied it is also
|
|
157
|
+
checked; it appears in the output as ``name="Ascendant"``.
|
|
158
|
+
|
|
159
|
+
Returns
|
|
160
|
+
-------
|
|
161
|
+
list of dict
|
|
162
|
+
One entry per planet/ascendant that lies in a Gandanta zone.
|
|
163
|
+
Empty list if none are in Gandanta.
|
|
164
|
+
"""
|
|
165
|
+
results = []
|
|
166
|
+
|
|
167
|
+
for planet in planetary_positions:
|
|
168
|
+
entry = _check_planet_in_gandanta(planet["name"], planet["longitude"])
|
|
169
|
+
if entry:
|
|
170
|
+
results.append(entry)
|
|
171
|
+
|
|
172
|
+
if ascendant is not None:
|
|
173
|
+
entry = _check_planet_in_gandanta("Ascendant", ascendant["longitude"])
|
|
174
|
+
if entry:
|
|
175
|
+
results.append(entry)
|
|
176
|
+
|
|
177
|
+
return results
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
def get_gandanta_info() -> dict:
|
|
181
|
+
"""Return educational information about all three Gandanta zones.
|
|
182
|
+
|
|
183
|
+
Returns
|
|
184
|
+
-------
|
|
185
|
+
dict
|
|
186
|
+
Keyed by ``"<WaterSign>-<FireSign>"``, each value holds zone metadata
|
|
187
|
+
including the boundary nakshatras, degrees affected, and significance.
|
|
188
|
+
"""
|
|
189
|
+
info = {}
|
|
190
|
+
for zone in _GANDANTA_ZONES:
|
|
191
|
+
key = f"{zone['from_sign']}-{zone['to_sign']}"
|
|
192
|
+
info[key] = {
|
|
193
|
+
"water_sign": zone["from_sign"],
|
|
194
|
+
"fire_sign": zone["to_sign"],
|
|
195
|
+
"junction_longitude": zone["water_end"] % 360,
|
|
196
|
+
"affected_range": f"Last {GANDANTA_DEGREES}° of {zone['from_sign']} "
|
|
197
|
+
f"and first {GANDANTA_DEGREES}° of {zone['to_sign']}",
|
|
198
|
+
"boundary_nakshatras": zone["nakshatras"],
|
|
199
|
+
"last_nakshatra_water": zone["nakshatras"][0],
|
|
200
|
+
"first_nakshatra_fire": zone["nakshatras"][1],
|
|
201
|
+
"significance": zone["description"],
|
|
202
|
+
"intensity_guide": {
|
|
203
|
+
"Severe": "< 1° from junction cusp — highly problematic; remedies essential",
|
|
204
|
+
"Moderate": "1°–2° from junction cusp — notable challenges",
|
|
205
|
+
"Mild": "2°–3°20' from junction cusp — some instability; monitor",
|
|
206
|
+
},
|
|
207
|
+
"general_note": (
|
|
208
|
+
"Planets in Gandanta operate in a zone of karmic dissolution. "
|
|
209
|
+
"The native may face deep-rooted instability in the significations of "
|
|
210
|
+
"that planet unless strong remedies are undertaken."
|
|
211
|
+
),
|
|
212
|
+
}
|
|
213
|
+
return info
|