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,465 @@
|
|
|
1
|
+
"""Arishta Yogas: Longevity assessment and affliction indicators."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from kundali_lib.vedic.constants import RASHI_LORDS, RASHIS
|
|
6
|
+
|
|
7
|
+
# ---------------------------------------------------------------------------
|
|
8
|
+
# Reference data
|
|
9
|
+
# ---------------------------------------------------------------------------
|
|
10
|
+
|
|
11
|
+
RASHI_LORDS_MAP: dict[str, str] = {r: RASHI_LORDS[i] for i, r in enumerate(RASHIS)}
|
|
12
|
+
|
|
13
|
+
EXALTATION: dict[str, str] = {
|
|
14
|
+
"Sun": "Aries",
|
|
15
|
+
"Moon": "Taurus",
|
|
16
|
+
"Mars": "Capricorn",
|
|
17
|
+
"Mercury": "Virgo",
|
|
18
|
+
"Jupiter": "Cancer",
|
|
19
|
+
"Venus": "Pisces",
|
|
20
|
+
"Saturn": "Libra",
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
DEBILITATION: dict[str, str] = {
|
|
24
|
+
"Sun": "Libra",
|
|
25
|
+
"Moon": "Scorpio",
|
|
26
|
+
"Mars": "Cancer",
|
|
27
|
+
"Mercury": "Pisces",
|
|
28
|
+
"Jupiter": "Capricorn",
|
|
29
|
+
"Venus": "Virgo",
|
|
30
|
+
"Saturn": "Aries",
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
OWN_SIGNS: dict[str, list[str]] = {
|
|
34
|
+
"Sun": ["Leo"],
|
|
35
|
+
"Moon": ["Cancer"],
|
|
36
|
+
"Mars": ["Aries", "Scorpio"],
|
|
37
|
+
"Mercury": ["Gemini", "Virgo"],
|
|
38
|
+
"Jupiter": ["Sagittarius", "Pisces"],
|
|
39
|
+
"Venus": ["Taurus", "Libra"],
|
|
40
|
+
"Saturn": ["Capricorn", "Aquarius"],
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
FRIENDLY: dict[str, set[str]] = {
|
|
44
|
+
"Sun": {"Moon", "Mars", "Jupiter"},
|
|
45
|
+
"Moon": {"Sun", "Mercury"},
|
|
46
|
+
"Mars": {"Sun", "Moon", "Jupiter"},
|
|
47
|
+
"Mercury": {"Sun", "Venus"},
|
|
48
|
+
"Jupiter": {"Sun", "Moon", "Mars"},
|
|
49
|
+
"Venus": {"Mercury", "Saturn"},
|
|
50
|
+
"Saturn": {"Mercury", "Venus"},
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
ENEMY: dict[str, set[str]] = {
|
|
54
|
+
"Sun": {"Venus", "Saturn"},
|
|
55
|
+
"Moon": {"Rahu", "Ketu"},
|
|
56
|
+
"Mars": {"Mercury"},
|
|
57
|
+
"Mercury": {"Moon"},
|
|
58
|
+
"Jupiter": {"Mercury", "Venus"},
|
|
59
|
+
"Venus": {"Sun", "Moon"},
|
|
60
|
+
"Saturn": {"Sun", "Moon", "Mars"},
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
MALEFIC_PLANETS = {"Sun", "Mars", "Saturn", "Rahu", "Ketu"}
|
|
64
|
+
BENEFIC_PLANETS = {"Moon", "Mercury", "Jupiter", "Venus"}
|
|
65
|
+
DUSTHANA_HOUSES = {6, 8, 12}
|
|
66
|
+
|
|
67
|
+
# ---------------------------------------------------------------------------
|
|
68
|
+
# Pindayu years contribution
|
|
69
|
+
# ---------------------------------------------------------------------------
|
|
70
|
+
|
|
71
|
+
PINDAYU_YEARS: dict[str, dict[str, float]] = {
|
|
72
|
+
"Sun": {
|
|
73
|
+
"exalt": 19,
|
|
74
|
+
"own": 19,
|
|
75
|
+
"friendly": 15,
|
|
76
|
+
"neutral": 12,
|
|
77
|
+
"enemy": 9,
|
|
78
|
+
"debil": 6,
|
|
79
|
+
},
|
|
80
|
+
"Moon": {
|
|
81
|
+
"exalt": 25,
|
|
82
|
+
"own": 25,
|
|
83
|
+
"friendly": 21,
|
|
84
|
+
"neutral": 16,
|
|
85
|
+
"enemy": 12,
|
|
86
|
+
"debil": 8,
|
|
87
|
+
},
|
|
88
|
+
"Mars": {
|
|
89
|
+
"exalt": 15,
|
|
90
|
+
"own": 15,
|
|
91
|
+
"friendly": 12,
|
|
92
|
+
"neutral": 9,
|
|
93
|
+
"enemy": 7,
|
|
94
|
+
"debil": 5,
|
|
95
|
+
},
|
|
96
|
+
"Mercury": {
|
|
97
|
+
"exalt": 20,
|
|
98
|
+
"own": 20,
|
|
99
|
+
"friendly": 16,
|
|
100
|
+
"neutral": 12,
|
|
101
|
+
"enemy": 9,
|
|
102
|
+
"debil": 7,
|
|
103
|
+
},
|
|
104
|
+
"Jupiter": {
|
|
105
|
+
"exalt": 18,
|
|
106
|
+
"own": 18,
|
|
107
|
+
"friendly": 14,
|
|
108
|
+
"neutral": 11,
|
|
109
|
+
"enemy": 8,
|
|
110
|
+
"debil": 6,
|
|
111
|
+
},
|
|
112
|
+
"Venus": {
|
|
113
|
+
"exalt": 20,
|
|
114
|
+
"own": 20,
|
|
115
|
+
"friendly": 16,
|
|
116
|
+
"neutral": 12,
|
|
117
|
+
"enemy": 9,
|
|
118
|
+
"debil": 7,
|
|
119
|
+
},
|
|
120
|
+
"Saturn": {
|
|
121
|
+
"exalt": 17,
|
|
122
|
+
"own": 17,
|
|
123
|
+
"friendly": 14,
|
|
124
|
+
"neutral": 11,
|
|
125
|
+
"enemy": 8,
|
|
126
|
+
"debil": 5,
|
|
127
|
+
},
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
# ---------------------------------------------------------------------------
|
|
131
|
+
# Balarishta conditions
|
|
132
|
+
# ---------------------------------------------------------------------------
|
|
133
|
+
|
|
134
|
+
BALARISHTA_CONDITIONS: list[dict] = [
|
|
135
|
+
{
|
|
136
|
+
"name": "Moon in Papakartari",
|
|
137
|
+
"check": "Moon hemmed between malefics in 6th/8th from Moon",
|
|
138
|
+
"severity": "High",
|
|
139
|
+
},
|
|
140
|
+
{
|
|
141
|
+
"name": "Moon with Rahu or Ketu at birth",
|
|
142
|
+
"check": "Moon conjunct Rahu or Ketu (same house)",
|
|
143
|
+
"severity": "High",
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
"name": "Malefics in 1, 8, 12 without benefic aspect",
|
|
147
|
+
"check": "Sun/Mars/Saturn/Rahu/Ketu in 1st/8th/12th houses",
|
|
148
|
+
"severity": "Medium",
|
|
149
|
+
},
|
|
150
|
+
{
|
|
151
|
+
"name": "Moon in dusthana in weak state",
|
|
152
|
+
"check": "Moon in 6th, 8th, or 12th in debilitation or enemy's sign",
|
|
153
|
+
"severity": "Medium",
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
"name": "Exchange between 1st and 8th lords",
|
|
157
|
+
"check": "Lord of 8th in 1st or Lord of 1st in 8th",
|
|
158
|
+
"severity": "High",
|
|
159
|
+
},
|
|
160
|
+
]
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
# ---------------------------------------------------------------------------
|
|
164
|
+
# Internal helpers
|
|
165
|
+
# ---------------------------------------------------------------------------
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def _get_planet(positions: list[dict], name: str) -> dict | None:
|
|
169
|
+
for p in positions:
|
|
170
|
+
if p["name"] == name:
|
|
171
|
+
return p
|
|
172
|
+
return None
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def _planet_sign_relationship(planet_name: str, rashi: str) -> str:
|
|
176
|
+
"""Return 'exalt', 'debil', 'own', 'friendly', 'enemy', or 'neutral'."""
|
|
177
|
+
if EXALTATION.get(planet_name) == rashi:
|
|
178
|
+
return "exalt"
|
|
179
|
+
if DEBILITATION.get(planet_name) == rashi:
|
|
180
|
+
return "debil"
|
|
181
|
+
if rashi in OWN_SIGNS.get(planet_name, []):
|
|
182
|
+
return "own"
|
|
183
|
+
lord = RASHI_LORDS_MAP.get(rashi)
|
|
184
|
+
if lord in FRIENDLY.get(planet_name, set()):
|
|
185
|
+
return "friendly"
|
|
186
|
+
if lord in ENEMY.get(planet_name, set()):
|
|
187
|
+
return "enemy"
|
|
188
|
+
return "neutral"
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
def _pindayu_for(planet: dict) -> float:
|
|
192
|
+
name = planet["name"]
|
|
193
|
+
if name not in PINDAYU_YEARS:
|
|
194
|
+
return 0.0
|
|
195
|
+
rel = _planet_sign_relationship(name, planet.get("rashi", ""))
|
|
196
|
+
return float(PINDAYU_YEARS[name].get(rel, PINDAYU_YEARS[name]["neutral"]))
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
def _planets_in_house(positions: list[dict], house: int) -> list[str]:
|
|
200
|
+
return [p["name"] for p in positions if p.get("house", 0) == house]
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
def _has_benefic_aspect(positions: list[dict], house: int, asc_sign: str) -> bool:
|
|
204
|
+
"""Simplified: check if any benefic planet aspects the given house (7th aspect)."""
|
|
205
|
+
for p in positions:
|
|
206
|
+
if p["name"] in BENEFIC_PLANETS:
|
|
207
|
+
planet_house = p.get("house", 0)
|
|
208
|
+
if (planet_house - 1) % 12 == (house - 7) % 12:
|
|
209
|
+
return True
|
|
210
|
+
return False
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
# ---------------------------------------------------------------------------
|
|
214
|
+
# Balarishta condition evaluators
|
|
215
|
+
# ---------------------------------------------------------------------------
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
def _eval_moon_papakartari(positions: list[dict]) -> bool:
|
|
219
|
+
"""Moon hemmed between malefics (malefic in 6th and 8th from Moon)."""
|
|
220
|
+
moon = _get_planet(positions, "Moon")
|
|
221
|
+
if not moon:
|
|
222
|
+
return False
|
|
223
|
+
moon_house = moon.get("house", 0)
|
|
224
|
+
house_6_from_moon = (moon_house + 4) % 12 + 1 # 6th from Moon
|
|
225
|
+
house_8_from_moon = (moon_house + 6) % 12 + 1 # 8th from Moon
|
|
226
|
+
planets_6 = _planets_in_house(positions, house_6_from_moon)
|
|
227
|
+
planets_8 = _planets_in_house(positions, house_8_from_moon)
|
|
228
|
+
has_malefic_6 = any(n in MALEFIC_PLANETS for n in planets_6)
|
|
229
|
+
has_malefic_8 = any(n in MALEFIC_PLANETS for n in planets_8)
|
|
230
|
+
return has_malefic_6 and has_malefic_8
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
def _eval_moon_with_nodes(positions: list[dict]) -> bool:
|
|
234
|
+
moon = _get_planet(positions, "Moon")
|
|
235
|
+
if not moon:
|
|
236
|
+
return False
|
|
237
|
+
moon_house = moon.get("house", 0)
|
|
238
|
+
return any(
|
|
239
|
+
_get_planet(positions, n)
|
|
240
|
+
and _get_planet(positions, n).get("house", 0) == moon_house
|
|
241
|
+
for n in ("Rahu", "Ketu")
|
|
242
|
+
)
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
def _eval_malefics_in_1_8_12(positions: list[dict], asc_sign: str) -> bool:
|
|
246
|
+
afflicted_houses = set()
|
|
247
|
+
for p in positions:
|
|
248
|
+
if p["name"] in MALEFIC_PLANETS and p.get("house", 0) in {1, 8, 12}:
|
|
249
|
+
if not _has_benefic_aspect(positions, p.get("house", 0), asc_sign):
|
|
250
|
+
afflicted_houses.add(p.get("house", 0))
|
|
251
|
+
return len(afflicted_houses) >= 2
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
def _eval_moon_in_dusthana_weak(positions: list[dict]) -> bool:
|
|
255
|
+
moon = _get_planet(positions, "Moon")
|
|
256
|
+
if not moon:
|
|
257
|
+
return False
|
|
258
|
+
house = moon.get("house", 0)
|
|
259
|
+
if house not in DUSTHANA_HOUSES:
|
|
260
|
+
return False
|
|
261
|
+
rel = _planet_sign_relationship("Moon", moon.get("rashi", ""))
|
|
262
|
+
return rel in ("debil", "enemy")
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
def _eval_1st_8th_exchange(positions: list[dict], asc_sign: str) -> bool:
|
|
266
|
+
asc_idx = RASHIS.index(asc_sign)
|
|
267
|
+
eighth_sign = RASHIS[(asc_idx + 7) % 12]
|
|
268
|
+
first_lord = RASHI_LORDS_MAP.get(asc_sign)
|
|
269
|
+
eighth_lord = RASHI_LORDS_MAP.get(eighth_sign)
|
|
270
|
+
if not first_lord or not eighth_lord:
|
|
271
|
+
return False
|
|
272
|
+
first_lord_planet = _get_planet(positions, first_lord)
|
|
273
|
+
eighth_lord_planet = _get_planet(positions, eighth_lord)
|
|
274
|
+
if not first_lord_planet or not eighth_lord_planet:
|
|
275
|
+
return False
|
|
276
|
+
# Exchange: 1st lord in 8th house or 8th lord in 1st house
|
|
277
|
+
return (
|
|
278
|
+
first_lord_planet.get("house", 0) == 8
|
|
279
|
+
or eighth_lord_planet.get("house", 0) == 1
|
|
280
|
+
)
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
# ---------------------------------------------------------------------------
|
|
284
|
+
# Public API
|
|
285
|
+
# ---------------------------------------------------------------------------
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
def get_arishta_yogas(base_chart: dict) -> dict:
|
|
289
|
+
"""Assess longevity and Arishta yogas from a birth chart.
|
|
290
|
+
|
|
291
|
+
Args:
|
|
292
|
+
base_chart: Output of ``build_chart()``.
|
|
293
|
+
|
|
294
|
+
Returns:
|
|
295
|
+
dict with Balarishta conditions, longevity category, Pindayu years
|
|
296
|
+
estimate, 8th house analysis, and overall Arishta assessment.
|
|
297
|
+
"""
|
|
298
|
+
positions: list[dict] = base_chart.get("planetary_positions", [])
|
|
299
|
+
asc_info: dict = base_chart.get("ascendant", {})
|
|
300
|
+
asc_sign: str = asc_info.get("rashi", RASHIS[0])
|
|
301
|
+
asc_idx = RASHIS.index(asc_sign)
|
|
302
|
+
|
|
303
|
+
# ── Balarishta evaluation ─────────────────────────────────────────────────
|
|
304
|
+
evaluators = [
|
|
305
|
+
_eval_moon_papakartari,
|
|
306
|
+
_eval_moon_with_nodes,
|
|
307
|
+
lambda pos: _eval_malefics_in_1_8_12(pos, asc_sign),
|
|
308
|
+
_eval_moon_in_dusthana_weak,
|
|
309
|
+
lambda pos: _eval_1st_8th_exchange(pos, asc_sign),
|
|
310
|
+
]
|
|
311
|
+
|
|
312
|
+
balarishta_results: list[dict] = []
|
|
313
|
+
for condition, evaluator in zip(BALARISHTA_CONDITIONS, evaluators):
|
|
314
|
+
present = evaluator(positions)
|
|
315
|
+
balarishta_results.append(
|
|
316
|
+
{
|
|
317
|
+
"name": condition["name"],
|
|
318
|
+
"present": present,
|
|
319
|
+
"description": condition["check"],
|
|
320
|
+
"severity": condition["severity"],
|
|
321
|
+
}
|
|
322
|
+
)
|
|
323
|
+
|
|
324
|
+
high_count = sum(
|
|
325
|
+
1 for b in balarishta_results if b["present"] and b["severity"] == "High"
|
|
326
|
+
)
|
|
327
|
+
med_count = sum(
|
|
328
|
+
1 for b in balarishta_results if b["present"] and b["severity"] == "Medium"
|
|
329
|
+
)
|
|
330
|
+
arishta_present = high_count >= 1 or med_count >= 2
|
|
331
|
+
|
|
332
|
+
# ── Arishta type ──────────────────────────────────────────────────────────
|
|
333
|
+
alparishta = False
|
|
334
|
+
# Alparishta: malefics simultaneously in 1, 8, 12
|
|
335
|
+
houses_with_malefics = {
|
|
336
|
+
p.get("house", 0) for p in positions if p["name"] in MALEFIC_PLANETS
|
|
337
|
+
}
|
|
338
|
+
if houses_with_malefics.issuperset({1, 8, 12}):
|
|
339
|
+
alparishta = True
|
|
340
|
+
# Classic Alparishta: Saturn in Lagna, Moon in 8th, Sun in 7th
|
|
341
|
+
saturn = _get_planet(positions, "Saturn")
|
|
342
|
+
moon = _get_planet(positions, "Moon")
|
|
343
|
+
sun = _get_planet(positions, "Sun")
|
|
344
|
+
if (
|
|
345
|
+
saturn
|
|
346
|
+
and saturn.get("house", 0) == 1
|
|
347
|
+
and moon
|
|
348
|
+
and moon.get("house", 0) == 8
|
|
349
|
+
and sun
|
|
350
|
+
and sun.get("house", 0) == 7
|
|
351
|
+
):
|
|
352
|
+
alparishta = True
|
|
353
|
+
|
|
354
|
+
if arishta_present and alparishta:
|
|
355
|
+
arishta_type: str | None = "Alparishta (short life threat, 0-32 years)"
|
|
356
|
+
elif arishta_present and high_count >= 1:
|
|
357
|
+
arishta_type = "Balarishta (early life affliction)"
|
|
358
|
+
elif arishta_present:
|
|
359
|
+
arishta_type = "Madhyarishta (medium life affliction)"
|
|
360
|
+
else:
|
|
361
|
+
arishta_type = None
|
|
362
|
+
|
|
363
|
+
# ── Pindayu calculation (Sun + Moon + Lagna lord) ─────────────────────────
|
|
364
|
+
lagna_lord_name = RASHI_LORDS_MAP.get(asc_sign, "")
|
|
365
|
+
sun_planet = _get_planet(positions, "Sun")
|
|
366
|
+
moon_planet = _get_planet(positions, "Moon")
|
|
367
|
+
lagna_lord_planet = _get_planet(positions, lagna_lord_name)
|
|
368
|
+
|
|
369
|
+
pindayu_sun = _pindayu_for(sun_planet) if sun_planet else 0.0
|
|
370
|
+
pindayu_moon = _pindayu_for(moon_planet) if moon_planet else 0.0
|
|
371
|
+
pindayu_ll = _pindayu_for(lagna_lord_planet) if lagna_lord_planet else 0.0
|
|
372
|
+
pindayu_total = pindayu_sun + pindayu_moon + pindayu_ll
|
|
373
|
+
|
|
374
|
+
# ── Longevity category ────────────────────────────────────────────────────
|
|
375
|
+
if pindayu_total >= 66 and not arishta_present:
|
|
376
|
+
longevity_category = "Long (75+ years)"
|
|
377
|
+
elif pindayu_total >= 36:
|
|
378
|
+
longevity_category = "Medium (36-75 years)"
|
|
379
|
+
else:
|
|
380
|
+
longevity_category = "Short (<36 years)"
|
|
381
|
+
|
|
382
|
+
# ── Eighth house analysis ─────────────────────────────────────────────────
|
|
383
|
+
eighth_sign = RASHIS[(asc_idx + 7) % 12]
|
|
384
|
+
eighth_lord_name = RASHI_LORDS_MAP[eighth_sign]
|
|
385
|
+
eighth_lord_planet = _get_planet(positions, eighth_lord_name)
|
|
386
|
+
eighth_lord_position = (
|
|
387
|
+
f"House {eighth_lord_planet.get('house', '?')} in {eighth_lord_planet.get('rashi', '?')}"
|
|
388
|
+
if eighth_lord_planet
|
|
389
|
+
else "Unknown"
|
|
390
|
+
)
|
|
391
|
+
planets_in_8 = _planets_in_house(positions, 8)
|
|
392
|
+
|
|
393
|
+
eighth_rel = _planet_sign_relationship(
|
|
394
|
+
eighth_lord_name,
|
|
395
|
+
eighth_lord_planet.get("rashi", "") if eighth_lord_planet else "",
|
|
396
|
+
)
|
|
397
|
+
eighth_strength = (
|
|
398
|
+
"Strong"
|
|
399
|
+
if eighth_rel in ("exalt", "own")
|
|
400
|
+
else "Weak"
|
|
401
|
+
if eighth_rel in ("debil", "enemy")
|
|
402
|
+
else "Moderate"
|
|
403
|
+
)
|
|
404
|
+
|
|
405
|
+
# ── Cancellation factors ──────────────────────────────────────────────────
|
|
406
|
+
cancellation: list[str] = []
|
|
407
|
+
jupiter = _get_planet(positions, "Jupiter")
|
|
408
|
+
if jupiter and jupiter.get("house", 0) in {1, 5, 9}:
|
|
409
|
+
cancellation.append("Jupiter in trikona mitigates Arishta")
|
|
410
|
+
if (
|
|
411
|
+
moon_planet
|
|
412
|
+
and _planet_sign_relationship("Moon", moon_planet.get("rashi", "")) == "exalt"
|
|
413
|
+
):
|
|
414
|
+
cancellation.append("Exalted Moon reduces early affliction severity")
|
|
415
|
+
if lagna_lord_planet and lagna_lord_planet.get("house", 0) in {1, 4, 7, 10}:
|
|
416
|
+
cancellation.append("Lagna lord in kendra provides resilience")
|
|
417
|
+
if not cancellation and arishta_present:
|
|
418
|
+
cancellation.append("No strong cancellation factors found")
|
|
419
|
+
elif not cancellation:
|
|
420
|
+
cancellation.append("Chart has no significant Arishta; no cancellation needed")
|
|
421
|
+
|
|
422
|
+
# ── Summary ───────────────────────────────────────────────────────────────
|
|
423
|
+
active_balarishta = [b["name"] for b in balarishta_results if b["present"]]
|
|
424
|
+
summary_parts = [
|
|
425
|
+
f"Longevity category: {longevity_category}.",
|
|
426
|
+
f"Pindayu indicator: Sun={pindayu_sun}, Moon={pindayu_moon}, "
|
|
427
|
+
f"Lagna lord={pindayu_ll} → Total={pindayu_total} years (indicative).",
|
|
428
|
+
]
|
|
429
|
+
if arishta_present:
|
|
430
|
+
summary_parts.append(
|
|
431
|
+
f"Arishta type: {arishta_type}. Active conditions: {', '.join(active_balarishta)}."
|
|
432
|
+
)
|
|
433
|
+
else:
|
|
434
|
+
summary_parts.append("No significant Arishta yoga detected.")
|
|
435
|
+
if cancellation:
|
|
436
|
+
summary_parts.append(f"Mitigating factors: {'; '.join(cancellation)}.")
|
|
437
|
+
|
|
438
|
+
return {
|
|
439
|
+
"balarishta_conditions": balarishta_results,
|
|
440
|
+
"longevity_category": longevity_category,
|
|
441
|
+
"pindayu_years": {
|
|
442
|
+
"Sun": pindayu_sun,
|
|
443
|
+
"Moon": pindayu_moon,
|
|
444
|
+
"Lagna_lord": pindayu_ll,
|
|
445
|
+
"total_indicator": pindayu_total,
|
|
446
|
+
"note": (
|
|
447
|
+
"Pindayu sum for Sun + Moon + Lagna lord only. "
|
|
448
|
+
"Full Pindayu includes all seven planets with various divisors."
|
|
449
|
+
),
|
|
450
|
+
},
|
|
451
|
+
"eighth_house_analysis": {
|
|
452
|
+
"eighth_lord": eighth_lord_name,
|
|
453
|
+
"eighth_lord_position": eighth_lord_position,
|
|
454
|
+
"planets_in_eighth": planets_in_8,
|
|
455
|
+
"strength": eighth_strength,
|
|
456
|
+
},
|
|
457
|
+
"arishta_present": arishta_present,
|
|
458
|
+
"arishta_type": arishta_type,
|
|
459
|
+
"cancellation_factors": cancellation,
|
|
460
|
+
"summary": " ".join(summary_parts),
|
|
461
|
+
"disclaimer": (
|
|
462
|
+
"Longevity calculation is indicative only. "
|
|
463
|
+
"Modern Vedic astrologers use this as one of many tools."
|
|
464
|
+
),
|
|
465
|
+
}
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
"""Ashtakavarga: mathematical benefic point system for transit predictions.
|
|
2
|
+
|
|
3
|
+
Each of 7 classical planets (Sun through Saturn) receives benefic points (0 or 1)
|
|
4
|
+
contributed by every other planet plus the Lagna, based on traditional Parashari
|
|
5
|
+
tables. The resulting per-planet scores (Bhinnashtakavarga, BAV) are summed into
|
|
6
|
+
a 12-house total (Sarvashtakavarga, SAV) which guides transit interpretation.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
# ── Parashari contribution tables ────────────────────────────────────────────
|
|
10
|
+
# For each target planet the dict maps contributor → list of house offsets
|
|
11
|
+
# (1-based, measured FROM the contributor) that receive one benefic point.
|
|
12
|
+
|
|
13
|
+
SUN_CONTRIBUTIONS: dict[str, list[int]] = {
|
|
14
|
+
"Sun": [1, 2, 4, 7, 8, 9, 10, 11],
|
|
15
|
+
"Moon": [3, 6, 10, 11],
|
|
16
|
+
"Mars": [1, 2, 4, 7, 8, 9, 10, 11],
|
|
17
|
+
"Mercury": [3, 5, 6, 9, 12],
|
|
18
|
+
"Jupiter": [5, 6, 9, 11],
|
|
19
|
+
"Venus": [6, 7, 12],
|
|
20
|
+
"Saturn": [1, 2, 4, 7, 8, 9, 10, 11],
|
|
21
|
+
"Lagna": [3, 4, 6, 10, 11, 12],
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
MOON_CONTRIBUTIONS: dict[str, list[int]] = {
|
|
25
|
+
"Sun": [3, 6, 7, 8, 10, 11],
|
|
26
|
+
"Moon": [1, 3, 6, 7, 10, 11],
|
|
27
|
+
"Mars": [2, 3, 5, 6, 9, 10, 11],
|
|
28
|
+
"Mercury": [1, 3, 4, 5, 7, 8, 10, 11],
|
|
29
|
+
"Jupiter": [1, 4, 7, 8, 10, 11, 12],
|
|
30
|
+
"Venus": [3, 4, 5, 7, 9, 10, 11],
|
|
31
|
+
"Saturn": [3, 5, 6, 11],
|
|
32
|
+
"Lagna": [3, 6, 10, 11],
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
MARS_CONTRIBUTIONS: dict[str, list[int]] = {
|
|
36
|
+
"Sun": [3, 5, 6, 10, 11],
|
|
37
|
+
"Moon": [3, 6, 11],
|
|
38
|
+
"Mars": [1, 2, 4, 7, 8, 10, 11],
|
|
39
|
+
"Mercury": [3, 5, 6],
|
|
40
|
+
"Jupiter": [6, 10, 11, 12],
|
|
41
|
+
"Venus": [6, 8, 11, 12],
|
|
42
|
+
"Saturn": [1, 4, 7, 8, 9, 10, 11],
|
|
43
|
+
"Lagna": [1, 3, 6, 10, 11],
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
MERCURY_CONTRIBUTIONS: dict[str, list[int]] = {
|
|
47
|
+
"Sun": [5, 6, 9, 11, 12],
|
|
48
|
+
"Moon": [2, 4, 6, 8, 10, 11],
|
|
49
|
+
"Mars": [1, 2, 4, 7, 8, 9, 10, 11],
|
|
50
|
+
"Mercury": [1, 3, 5, 6, 9, 10, 11, 12],
|
|
51
|
+
"Jupiter": [6, 8, 11, 12],
|
|
52
|
+
"Venus": [1, 2, 3, 4, 5, 8, 9, 11],
|
|
53
|
+
"Saturn": [1, 2, 4, 7, 8, 9, 10, 11],
|
|
54
|
+
"Lagna": [1, 2, 4, 6, 8, 10, 11],
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
JUPITER_CONTRIBUTIONS: dict[str, list[int]] = {
|
|
58
|
+
"Sun": [1, 2, 3, 4, 7, 8, 9, 10, 11],
|
|
59
|
+
"Moon": [2, 5, 7, 9, 11],
|
|
60
|
+
"Mars": [1, 2, 4, 7, 8, 10, 11],
|
|
61
|
+
"Mercury": [1, 2, 4, 5, 6, 9, 10, 11],
|
|
62
|
+
"Jupiter": [1, 2, 3, 4, 7, 8, 10, 11],
|
|
63
|
+
"Venus": [2, 5, 6, 9, 10, 11],
|
|
64
|
+
"Saturn": [3, 5, 6, 12],
|
|
65
|
+
"Lagna": [1, 2, 4, 5, 6, 7, 9, 10, 11],
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
VENUS_CONTRIBUTIONS: dict[str, list[int]] = {
|
|
69
|
+
"Sun": [8, 11, 12],
|
|
70
|
+
"Moon": [1, 2, 3, 4, 5, 8, 9, 11, 12],
|
|
71
|
+
"Mars": [3, 4, 6, 9, 11, 12],
|
|
72
|
+
"Mercury": [3, 5, 6, 9, 11],
|
|
73
|
+
"Jupiter": [5, 8, 9, 10, 11],
|
|
74
|
+
"Venus": [1, 2, 3, 4, 5, 8, 9, 10, 11],
|
|
75
|
+
"Saturn": [3, 4, 5, 8, 9, 10, 11],
|
|
76
|
+
"Lagna": [1, 2, 3, 4, 5, 8, 9, 11],
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
SATURN_CONTRIBUTIONS: dict[str, list[int]] = {
|
|
80
|
+
"Sun": [1, 2, 4, 7, 8, 10, 11],
|
|
81
|
+
"Moon": [3, 6, 11],
|
|
82
|
+
"Mars": [3, 5, 6, 10, 11, 12],
|
|
83
|
+
"Mercury": [6, 8, 9, 10, 11, 12],
|
|
84
|
+
"Jupiter": [5, 6, 11, 12],
|
|
85
|
+
"Venus": [6, 11, 12],
|
|
86
|
+
"Saturn": [3, 5, 6, 11],
|
|
87
|
+
"Lagna": [1, 3, 4, 6, 10, 11],
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
# Ordered list of target planets and their contribution tables
|
|
91
|
+
_TARGET_PLANETS: list[str] = [
|
|
92
|
+
"Sun",
|
|
93
|
+
"Moon",
|
|
94
|
+
"Mars",
|
|
95
|
+
"Mercury",
|
|
96
|
+
"Jupiter",
|
|
97
|
+
"Venus",
|
|
98
|
+
"Saturn",
|
|
99
|
+
]
|
|
100
|
+
|
|
101
|
+
_CONTRIBUTIONS: dict[str, dict[str, list[int]]] = {
|
|
102
|
+
"Sun": SUN_CONTRIBUTIONS,
|
|
103
|
+
"Moon": MOON_CONTRIBUTIONS,
|
|
104
|
+
"Mars": MARS_CONTRIBUTIONS,
|
|
105
|
+
"Mercury": MERCURY_CONTRIBUTIONS,
|
|
106
|
+
"Jupiter": JUPITER_CONTRIBUTIONS,
|
|
107
|
+
"Venus": VENUS_CONTRIBUTIONS,
|
|
108
|
+
"Saturn": SATURN_CONTRIBUTIONS,
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
# BAV strength thresholds (points out of 8 per house)
|
|
112
|
+
_BAV_STRONG = 5
|
|
113
|
+
_BAV_MODERATE = 4
|
|
114
|
+
# SAV strength thresholds (sum of 7 planets = max ~337 per house)
|
|
115
|
+
_SAV_EXCELLENT = 28
|
|
116
|
+
_SAV_GOOD = 22
|
|
117
|
+
_SAV_AVERAGE = 16
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
# ── Private helpers ───────────────────────────────────────────────────────────
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def _empty_house_scores() -> dict[str, int]:
|
|
124
|
+
return {f"House_{h}": 0 for h in range(1, 13)}
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def _compute_bav(
|
|
128
|
+
target_planet: str,
|
|
129
|
+
planet_houses: dict[str, int],
|
|
130
|
+
lagna_house: int = 1,
|
|
131
|
+
) -> dict[str, int]:
|
|
132
|
+
"""Compute Bhinnashtakavarga (BAV) for one target planet.
|
|
133
|
+
|
|
134
|
+
For each contributing planet / Lagna, for each benefic offset in its table,
|
|
135
|
+
the house that lands at (contributor_house + offset - 1) receives +1 point.
|
|
136
|
+
"""
|
|
137
|
+
scores = _empty_house_scores()
|
|
138
|
+
table = _CONTRIBUTIONS[target_planet]
|
|
139
|
+
|
|
140
|
+
for contributor, offsets in table.items():
|
|
141
|
+
if contributor == "Lagna":
|
|
142
|
+
contrib_house = lagna_house
|
|
143
|
+
else:
|
|
144
|
+
contrib_house = planet_houses.get(contributor)
|
|
145
|
+
if contrib_house is None:
|
|
146
|
+
continue # planet absent from chart (e.g. outer planets)
|
|
147
|
+
|
|
148
|
+
for offset in offsets:
|
|
149
|
+
# offset=1 → same house as contributor; offset=2 → next house; etc.
|
|
150
|
+
target_house = (contrib_house - 1 + offset - 1) % 12 + 1
|
|
151
|
+
scores[f"House_{target_house}"] += 1
|
|
152
|
+
|
|
153
|
+
scores["total"] = sum(scores[f"House_{h}"] for h in range(1, 13))
|
|
154
|
+
return scores
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
def _sav_interpretation(score: int) -> str:
|
|
158
|
+
if score >= _SAV_EXCELLENT:
|
|
159
|
+
return "Excellent (>=28)"
|
|
160
|
+
if score >= _SAV_GOOD:
|
|
161
|
+
return "Good (22-27)"
|
|
162
|
+
if score >= _SAV_AVERAGE:
|
|
163
|
+
return "Average (16-21)"
|
|
164
|
+
return "Weak (<16)"
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
# ── Public API ────────────────────────────────────────────────────────────────
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
def get_ashtakavarga(base_chart: dict) -> dict:
|
|
171
|
+
"""Calculate Ashtakavarga from a built chart dict.
|
|
172
|
+
|
|
173
|
+
Args:
|
|
174
|
+
base_chart: Output of ``build_chart()`` — must contain
|
|
175
|
+
``planetary_positions`` (list with ``name`` and ``house`` keys).
|
|
176
|
+
|
|
177
|
+
Returns:
|
|
178
|
+
dict with three keys:
|
|
179
|
+
|
|
180
|
+
* ``bhinnashtakavarga``: per-planet 12-house benefic scores + total.
|
|
181
|
+
* ``sarvashtakavarga``: sum of all 7 planets per house + total.
|
|
182
|
+
* ``interpretation``: SAV strength label per house.
|
|
183
|
+
"""
|
|
184
|
+
# Extract house numbers for each planet
|
|
185
|
+
planet_houses: dict[str, int] = {
|
|
186
|
+
p["name"]: p["house"] for p in base_chart["planetary_positions"]
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
# Lagna always occupies house 1 by definition
|
|
190
|
+
lagna_house = 1
|
|
191
|
+
|
|
192
|
+
# ── Bhinnashtakavarga (BAV) ──────────────────────────────────────────────
|
|
193
|
+
bav: dict[str, dict[str, int]] = {}
|
|
194
|
+
for target_planet in _TARGET_PLANETS:
|
|
195
|
+
bav[target_planet] = _compute_bav(target_planet, planet_houses, lagna_house)
|
|
196
|
+
|
|
197
|
+
# ── Sarvashtakavarga (SAV) ───────────────────────────────────────────────
|
|
198
|
+
sav = _empty_house_scores()
|
|
199
|
+
for planet in _TARGET_PLANETS:
|
|
200
|
+
for h in range(1, 13):
|
|
201
|
+
sav[f"House_{h}"] += bav[planet][f"House_{h}"]
|
|
202
|
+
sav["total"] = sum(sav[f"House_{h}"] for h in range(1, 13))
|
|
203
|
+
|
|
204
|
+
# ── Interpretation ───────────────────────────────────────────────────────
|
|
205
|
+
interpretation: dict[str, str] = {
|
|
206
|
+
f"House_{h}": _sav_interpretation(sav[f"House_{h}"]) for h in range(1, 13)
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
return {
|
|
210
|
+
"bhinnashtakavarga": bav,
|
|
211
|
+
"sarvashtakavarga": sav,
|
|
212
|
+
"interpretation": interpretation,
|
|
213
|
+
}
|