f83db2fac1
- add external validation reports and open-source comparison references - integrate Jaimini arudha/graha pada, enhanced argala, and additional synastry kutas - update skill docs and capability matrices - add smoke tests for open-source integrations
509 lines
19 KiB
Python
509 lines
19 KiB
Python
from .constants import ZODIAC_SIGNS, SIGN_LORDS, NATURAL_FRIENDS, NATURAL_ENEMIES, EXALTATION, DEBILITATION, OWN_SIGNS
|
|
from .nakshatra import get_nakshatra
|
|
import math
|
|
|
|
# --- Data Tables ---
|
|
# 0-26 indexed Nakshatras
|
|
YONI_ANIMALS = [
|
|
"Horse", "Elephant", "Sheep", "Serpent", "Serpent", "Dog", "Cat", "Sheep", "Cat", # 0-8
|
|
"Rat", "Rat", "Cow", "Buffalo", "Tiger", "Buffalo", "Tiger", "Deer", "Deer", # 9-17
|
|
"Dog", "Monkey", "Mongoose", "Monkey", "Lion", "Horse", "Lion", "Cow", "Elephant" # 18-26
|
|
]
|
|
|
|
YONI_ENEMIES = {
|
|
"Horse": "Buffalo", "Buffalo": "Horse",
|
|
"Elephant": "Lion", "Lion": "Elephant",
|
|
"Sheep": "Monkey", "Monkey": "Sheep",
|
|
"Serpent": "Mongoose", "Mongoose": "Serpent",
|
|
"Dog": "Deer", "Deer": "Dog",
|
|
"Cat": "Rat", "Rat": "Cat",
|
|
"Cow": "Tiger", "Tiger": "Cow"
|
|
}
|
|
|
|
GANA = [
|
|
"Deva", "Manushya", "Rakshasa", "Manushya", "Deva", "Manushya", "Deva", "Deva", "Rakshasa",
|
|
"Rakshasa", "Manushya", "Manushya", "Deva", "Rakshasa", "Deva", "Rakshasa", "Deva", "Rakshasa",
|
|
"Rakshasa", "Manushya", "Manushya", "Deva", "Rakshasa", "Rakshasa", "Manushya", "Manushya", "Deva"
|
|
]
|
|
|
|
NADI = [
|
|
"Adi", "Madhya", "Antya", "Antya", "Madhya", "Adi", "Adi", "Madhya", "Antya",
|
|
"Antya", "Madhya", "Adi", "Adi", "Madhya", "Antya", "Antya", "Madhya", "Adi",
|
|
"Adi", "Madhya", "Antya", "Antya", "Madhya", "Adi", "Adi", "Madhya", "Antya"
|
|
]
|
|
|
|
VARNA = {
|
|
"Cancer": 1, "Scorpio": 1, "Pisces": 1, # Brahmin
|
|
"Aries": 2, "Leo": 2, "Sagittarius": 2, # Kshatriya
|
|
"Taurus": 3, "Virgo": 3, "Capricorn": 3, # Vaishya
|
|
"Gemini": 4, "Libra": 4, "Aquarius": 4 # Shudra
|
|
}
|
|
|
|
# 1. Varna (1 point)
|
|
def calc_varna(m_sign, f_sign):
|
|
m_varna = VARNA[m_sign]
|
|
f_varna = VARNA[f_sign]
|
|
return 1.0 if m_varna <= f_varna else 0.0
|
|
|
|
# 2. Vashya (2 points) — Full BPHS classification
|
|
# Sign categories: Chatushpada (quadruped), Manava (human), Jalachara (water),
|
|
# Vanachara (wild/forest), Keet (insect/reptile)
|
|
VASHYA_TYPE = {
|
|
"Aries": "Chatushpada", "Taurus": "Chatushpada",
|
|
"Leo": "Vanachara", "Sagittarius": "Chatushpada", # 2nd half is Manava
|
|
"Capricorn": "Chatushpada", # 1st half is Chatushpada
|
|
"Gemini": "Manava", "Virgo": "Manava", "Libra": "Manava",
|
|
"Aquarius": "Manava", # 1st half is Manava
|
|
"Cancer": "Jalachara", "Pisces": "Jalachara",
|
|
"Scorpio": "Keet",
|
|
}
|
|
|
|
# Vashya compatibility scoring matrix
|
|
VASHYA_MATRIX = {
|
|
("Chatushpada", "Chatushpada"): 2.0,
|
|
("Manava", "Manava"): 2.0,
|
|
("Jalachara", "Jalachara"): 2.0,
|
|
("Vanachara", "Vanachara"): 2.0,
|
|
("Keet", "Keet"): 2.0,
|
|
("Chatushpada", "Manava"): 0.5,
|
|
("Manava", "Chatushpada"): 0.5,
|
|
("Manava", "Jalachara"): 1.0,
|
|
("Jalachara", "Manava"): 1.0,
|
|
("Chatushpada", "Jalachara"): 0.5,
|
|
("Jalachara", "Chatushpada"): 0.5,
|
|
("Vanachara", "Chatushpada"): 0.0, # Wild eats quadruped
|
|
("Chatushpada", "Vanachara"): 0.0,
|
|
("Keet", "Chatushpada"): 0.0,
|
|
("Chatushpada", "Keet"): 0.0,
|
|
("Vanachara", "Manava"): 0.5,
|
|
("Manava", "Vanachara"): 0.5,
|
|
("Keet", "Manava"): 0.5,
|
|
("Manava", "Keet"): 0.5,
|
|
("Vanachara", "Jalachara"): 0.5,
|
|
("Jalachara", "Vanachara"): 0.5,
|
|
("Keet", "Jalachara"): 1.0,
|
|
("Jalachara", "Keet"): 1.0,
|
|
("Vanachara", "Keet"): 1.0,
|
|
("Keet", "Vanachara"): 1.0,
|
|
}
|
|
|
|
def calc_vashya(m_sign, f_sign):
|
|
if m_sign == f_sign:
|
|
return 2.0
|
|
m_type = VASHYA_TYPE.get(m_sign, "Manava")
|
|
f_type = VASHYA_TYPE.get(f_sign, "Manava")
|
|
return VASHYA_MATRIX.get((m_type, f_type), 1.0)
|
|
|
|
# 3. Tara (3 points)
|
|
def calc_tara(m_nak_idx, f_nak_idx):
|
|
m_to_f = (f_nak_idx - m_nak_idx) % 9
|
|
f_to_m = (m_nak_idx - f_nak_idx) % 9
|
|
pts = 0.0
|
|
if m_to_f not in (2, 4, 6): pts += 1.5
|
|
if f_to_m not in (2, 4, 6): pts += 1.5
|
|
return pts
|
|
|
|
# 4. Yoni (4 points)
|
|
def calc_yoni(m_nak_idx, f_nak_idx):
|
|
m_yoni = YONI_ANIMALS[m_nak_idx]
|
|
f_yoni = YONI_ANIMALS[f_nak_idx]
|
|
if m_yoni == f_yoni:
|
|
return 4.0
|
|
if YONI_ENEMIES.get(m_yoni) == f_yoni:
|
|
return 0.0
|
|
return 2.0 # Neutral
|
|
|
|
# 5. Graha Maitri (5 points)
|
|
def check_friendship(p1, p2):
|
|
if p1 == p2:
|
|
return 1.0 # Same lord
|
|
if p2 in NATURAL_FRIENDS.get(p1, []):
|
|
return 1.0
|
|
if p2 in NATURAL_ENEMIES.get(p1, []):
|
|
return 0.0
|
|
return 0.5 # Neutral
|
|
|
|
def calc_graha_maitri(m_sign, f_sign):
|
|
m_lord = SIGN_LORDS[m_sign]
|
|
f_lord = SIGN_LORDS[f_sign]
|
|
m_to_f = check_friendship(m_lord, f_lord)
|
|
f_to_m = check_friendship(f_lord, m_lord)
|
|
|
|
total = m_to_f + f_to_m
|
|
if total == 2.0: return 5.0
|
|
if total == 1.5: return 4.0
|
|
if total == 1.0: return 3.0
|
|
if total == 0.5: return 1.0
|
|
return 0.0
|
|
|
|
# 6. Gana (6 points)
|
|
def calc_gana(m_nak_idx, f_nak_idx):
|
|
m_gana = GANA[m_nak_idx]
|
|
f_gana = GANA[f_nak_idx]
|
|
if m_gana == f_gana: return 6.0
|
|
if m_gana == "Deva" and f_gana == "Manushya": return 6.0
|
|
if m_gana == "Manushya" and f_gana == "Deva": return 5.0
|
|
if m_gana == "Rakshasa" and f_gana == "Manushya": return 0.0
|
|
if f_gana == "Rakshasa" and m_gana == "Manushya": return 0.0
|
|
if m_gana == "Rakshasa" and f_gana == "Deva": return 1.0
|
|
if f_gana == "Rakshasa" and m_gana == "Deva": return 0.0
|
|
return 0.0
|
|
|
|
# 7. Bhakoot (7 points)
|
|
def calc_bhakoot(m_sign, f_sign):
|
|
m_idx = ZODIAC_SIGNS.index(m_sign)
|
|
f_idx = ZODIAC_SIGNS.index(f_sign)
|
|
diff = (f_idx - m_idx) % 12 + 1
|
|
if diff in (1, 7, 3, 11, 4, 10):
|
|
return 7.0
|
|
return 0.0
|
|
|
|
# 8. Nadi (8 points)
|
|
def calc_nadi(m_nak_idx, f_nak_idx):
|
|
m_nadi = NADI[m_nak_idx]
|
|
f_nadi = NADI[f_nak_idx]
|
|
if m_nadi != f_nadi:
|
|
return 8.0
|
|
return 0.0 # Nadi Dosha
|
|
|
|
|
|
# ==========================================
|
|
# ADDITIONAL KUTAS (beyond 36-point Ashtakoot)
|
|
# ==========================================
|
|
|
|
# 9. Mahendra Kuta — longevity and well-being
|
|
def calc_mahendra(m_nak_idx, f_nak_idx):
|
|
"""Male's nakshatra counted from female's. Auspicious if 4,7,10,13,16,19,22,25."""
|
|
count = ((m_nak_idx - f_nak_idx) % 27) + 1
|
|
return "good" if count in (4, 7, 10, 13, 16, 19, 22, 25) else "bad"
|
|
|
|
|
|
# 10. Stree Deergha — husband's longevity
|
|
def calc_stree_deergha(m_nak_idx, f_nak_idx):
|
|
"""Male's nakshatra must be >= 9 nakshatras from female's (counted f→m)."""
|
|
count = ((m_nak_idx - f_nak_idx) % 27) + 1
|
|
return "good" if count >= 9 else "bad"
|
|
|
|
|
|
# 11. Vedha Kuta — obstruction pairs
|
|
VEDHA_PAIRS = [
|
|
(0, 17), # Ashwini - Jyeshtha
|
|
(1, 16), # Bharani - Anuradha
|
|
(2, 15), # Krittika - Vishakha
|
|
(3, 14), # Rohini - Swati
|
|
(5, 21), # Ardra - Shravana
|
|
(6, 20), # Punarvasu - Uttara Ashadha
|
|
(7, 19), # Pushya - Purva Ashadha
|
|
(8, 18), # Ashlesha - Mula
|
|
(9, 26), # Magha - Revati
|
|
(10, 25), # Purva Phalguni - Uttara Bhadrapada
|
|
(11, 24), # Uttara Phalguni - Purva Bhadrapada
|
|
(12, 23), # Hasta - Shatabhisha
|
|
(4, 22), # Mrigashira - Dhanishta
|
|
]
|
|
|
|
def calc_vedha(m_nak_idx, f_nak_idx):
|
|
"""Check if male and female nakshatras form a hostile Vedha pair."""
|
|
for a, b in VEDHA_PAIRS:
|
|
if (m_nak_idx == a and f_nak_idx == b) or (m_nak_idx == b and f_nak_idx == a):
|
|
return "bad"
|
|
return "good"
|
|
|
|
|
|
# 12. Kuja Dosha (Manglik) — Mars affliction analysis
|
|
_DOSHA_HOUSES = {2, 4, 7, 8, 12}
|
|
_HIGH_SEVERITY_HOUSES = {7, 8}
|
|
|
|
_MARS_EXCEPTIONS = {
|
|
2: {"Gemini", "Virgo"},
|
|
12: {"Taurus", "Libra"},
|
|
4: {"Aries", "Scorpio"},
|
|
7: {"Capricorn", "Cancer"},
|
|
8: {"Sagittarius", "Pisces"},
|
|
}
|
|
_MARS_EXEMPT_SIGNS = {"Aquarius", "Leo"}
|
|
|
|
|
|
def _planet_dignity_level(planet_name, sign):
|
|
"""Return dignity level for Kuja Dosha scoring."""
|
|
if planet_name in EXALTATION and EXALTATION[planet_name][0] == sign:
|
|
return "exalted"
|
|
if planet_name in OWN_SIGNS and sign in OWN_SIGNS[planet_name]:
|
|
return "own"
|
|
lord = SIGN_LORDS.get(sign)
|
|
if lord and planet_name in NATURAL_FRIENDS.get(lord, []):
|
|
return "friendly"
|
|
if lord and planet_name in NATURAL_ENEMIES.get(lord, []):
|
|
return "enemy"
|
|
if planet_name in DEBILITATION and DEBILITATION[planet_name][0] == sign:
|
|
return "debilitated"
|
|
return "neutral"
|
|
|
|
|
|
_DOSHA_SCORES_HIGH = {
|
|
"Mars": {"debilitated": 100, "enemy": 90, "neutral": 80, "friendly": 70, "own": 60, "exalted": 50},
|
|
"Saturn": {"debilitated": 75, "enemy": 67.5, "neutral": 60, "friendly": 52.5, "own": 45, "exalted": 37.5},
|
|
"Sun": {"debilitated": 50, "enemy": 45, "neutral": 40, "friendly": 35, "own": 30, "exalted": 25},
|
|
}
|
|
_DOSHA_SCORES_LOW = {
|
|
"Mars": {"debilitated": 50, "enemy": 45, "neutral": 40, "friendly": 35, "own": 30, "exalted": 25},
|
|
"Saturn": {"debilitated": 37.5, "enemy": 33.75, "neutral": 30, "friendly": 26.25, "own": 22.5, "exalted": 18.75},
|
|
"Sun": {"debilitated": 25, "enemy": 22.5, "neutral": 20, "friendly": 17.5, "own": 15, "exalted": 12.5},
|
|
}
|
|
|
|
|
|
def _calc_dosha_score(planet_name, house, sign):
|
|
"""Calculate Kuja Dosha score for a single planet placement."""
|
|
if house not in _DOSHA_HOUSES:
|
|
return 0.0
|
|
# Mars-specific exceptions
|
|
if planet_name == "Mars":
|
|
if sign in _MARS_EXEMPT_SIGNS:
|
|
return 0.0
|
|
if house in _MARS_EXCEPTIONS and sign in _MARS_EXCEPTIONS[house]:
|
|
return 0.0
|
|
|
|
dig = _planet_dignity_level(planet_name, sign)
|
|
score_planet = planet_name if planet_name in _DOSHA_SCORES_HIGH else "Saturn" # Rahu/Ketu use Saturn table
|
|
|
|
if house in _HIGH_SEVERITY_HOUSES:
|
|
return _DOSHA_SCORES_HIGH.get(score_planet, _DOSHA_SCORES_HIGH["Saturn"]).get(dig, 60)
|
|
else:
|
|
return _DOSHA_SCORES_LOW.get(score_planet, _DOSHA_SCORES_LOW["Saturn"]).get(dig, 30)
|
|
|
|
|
|
def calc_kuja_dosha(chart):
|
|
"""
|
|
Calculate total Kuja Dosha score for a chart.
|
|
Checks Mars, Saturn, Rahu, Ketu, Sun in houses 2,4,7,8,12.
|
|
chart: output from calculate_vedic_chart (needs planets with house and sign).
|
|
Returns dict with total score and per-planet breakdown.
|
|
"""
|
|
planets = chart.get("planets", {})
|
|
total = 0.0
|
|
breakdown = {}
|
|
for p_name in ("Mars", "Saturn", "Rahu", "Ketu", "Sun"):
|
|
pd = planets.get(p_name)
|
|
if not pd:
|
|
continue
|
|
score = _calc_dosha_score(p_name, pd["house"], pd["sign"])
|
|
if score > 0:
|
|
breakdown[p_name] = {"house": pd["house"], "sign": pd["sign"], "score": score}
|
|
total += score
|
|
return {"total_score": round(total, 2), "breakdown": breakdown, "is_manglik": total > 0}
|
|
|
|
|
|
def match_kuja_dosha(male_score, female_score):
|
|
"""
|
|
Compare Kuja Dosha between male and female.
|
|
|diff| <= 5: good. Female > male by > 5: bad. Male > female by > 5: check 25% threshold.
|
|
"""
|
|
diff = male_score - female_score
|
|
if abs(diff) <= 5:
|
|
return {"result": "good", "description": "Kuja Dosha balanced between partners."}
|
|
if diff < -5:
|
|
return {"result": "bad", "description": "Female has significantly more Kuja Dosha."}
|
|
# male > female by > 5
|
|
if female_score > 0 and diff < female_score * 0.25:
|
|
return {"result": "acceptable", "description": "Male has more Kuja Dosha but within tolerance."}
|
|
return {"result": "bad", "description": "Male has significantly more Kuja Dosha."}
|
|
|
|
|
|
# 13. Rajju Kuta — marital longevity based on nakshatra body-part group
|
|
RAJJU_GROUPS = {
|
|
"Pada": {0, 8, 9, 17, 18, 26}, # Ashwini, Ashlesha, Magha, Jyeshtha, Mula, Revati
|
|
"Kati": {1, 7, 10, 16, 25, 19}, # Bharani, Pushya, P.Phalguni, Anuradha, U.Bhadra, P.Ashadha
|
|
"Udara": {2, 6, 11, 15, 20, 24}, # Krittika, Punarvasu, U.Phalguni, Vishakha, U.Ashadha, P.Bhadra
|
|
"Kanta": {3, 5, 12, 14, 21, 23}, # Rohini, Ardra, Hasta, Swati, Shravana, Shatabhisha
|
|
"Sira": {4, 13, 22}, # Mrigashira, Chitra, Dhanishta
|
|
}
|
|
|
|
RAJJU_EFFECTS = {
|
|
"Sira": "head — risk to husband's longevity",
|
|
"Kanta": "neck — risk to wife's longevity",
|
|
"Udara": "stomach — risk to children",
|
|
"Kati": "waist — poverty may ensue",
|
|
"Pada": "foot — couple may be always wandering",
|
|
}
|
|
|
|
def _get_rajju_group(nak_idx):
|
|
for group, indices in RAJJU_GROUPS.items():
|
|
if nak_idx in indices:
|
|
return group
|
|
return None
|
|
|
|
def calc_rajju(m_nak_idx, f_nak_idx):
|
|
"""Same Rajju group = bad; different = good."""
|
|
m_group = _get_rajju_group(m_nak_idx)
|
|
f_group = _get_rajju_group(f_nak_idx)
|
|
if m_group and f_group and m_group == f_group:
|
|
return {"result": "bad", "group": m_group, "effect": RAJJU_EFFECTS.get(m_group, "")}
|
|
return {"result": "good", "group": None, "effect": ""}
|
|
|
|
|
|
# 14. Bad Constellations — specific nakshatra quarters considered destructive
|
|
def calc_bad_constellations(m_nak_idx, m_pada, f_nak_idx, f_pada):
|
|
"""
|
|
Only first pada of Moola/Ashlesha/Jyeshtha and 4th pada of Vishakha are bad.
|
|
Ashlesha/Jyeshtha/Vishakha destructive only for females.
|
|
"""
|
|
issues = []
|
|
if m_nak_idx == 18 and m_pada == 1:
|
|
issues.append("Male born in Moola 1st pada — risk to father-in-law.")
|
|
if f_nak_idx == 18 and f_pada == 1:
|
|
issues.append("Female born in Moola 1st pada — risk to father-in-law.")
|
|
if f_nak_idx == 8 and f_pada == 1:
|
|
issues.append("Female born in Ashlesha 1st pada — risk to husband's mother.")
|
|
if f_nak_idx == 17 and f_pada == 1:
|
|
issues.append("Female born in Jyeshtha 1st pada — risk to husband's elder brother.")
|
|
if f_nak_idx == 15 and f_pada == 4:
|
|
issues.append("Female born in Vishakha 4th pada — risk to husband's younger brother.")
|
|
return {"result": "bad" if issues else "good", "issues": issues}
|
|
|
|
|
|
# 15. Lagna and House 7 — cross-Lagna compatibility
|
|
def calc_lagna_house7(chart1, chart2):
|
|
"""
|
|
Good if female's Moon sign = male's Lagna OR male's Moon sign = female's Lagna,
|
|
OR if 7th house lords are exchanged.
|
|
"""
|
|
m_lagna = chart1.get("lagna", {}).get("sign")
|
|
f_lagna = chart2.get("lagna", {}).get("sign")
|
|
m_moon = chart1.get("planets", {}).get("Moon", {}).get("sign")
|
|
f_moon = chart2.get("planets", {}).get("Moon", {}).get("sign")
|
|
|
|
if (f_moon and m_lagna and f_moon == m_lagna) or (m_moon and f_lagna and m_moon == f_lagna):
|
|
return {"result": "good", "description": "Moon-Lagna cross match — mutual understanding and affection."}
|
|
|
|
m_lagna_idx = ZODIAC_SIGNS.index(m_lagna) if m_lagna else None
|
|
f_lagna_idx = ZODIAC_SIGNS.index(f_lagna) if f_lagna else None
|
|
if m_lagna_idx is not None and f_lagna_idx is not None:
|
|
m_7th_sign = ZODIAC_SIGNS[(m_lagna_idx + 6) % 12]
|
|
f_7th_sign = ZODIAC_SIGNS[(f_lagna_idx + 6) % 12]
|
|
m_7th_lord = SIGN_LORDS[m_7th_sign]
|
|
f_7th_lord = SIGN_LORDS[f_7th_sign]
|
|
m_7lord_sign = chart1.get("planets", {}).get(m_7th_lord, {}).get("sign")
|
|
f_7lord_sign = chart2.get("planets", {}).get(f_7th_lord, {}).get("sign")
|
|
if m_7lord_sign == f_lagna or f_7lord_sign == m_lagna:
|
|
return {"result": "good", "description": "7th house lord cross-placement — marriage stability."}
|
|
|
|
return {"result": "neutral", "description": "No special Lagna-7th house connection found."}
|
|
|
|
|
|
# 16. Sex Energy — based on planets in 7th house
|
|
def calc_sex_energy(chart1, chart2):
|
|
"""
|
|
Mars/Venus in 7th = strong sex drive. Mercury/Jupiter in 7th = moderate.
|
|
Mismatch between partners = potential incompatibility.
|
|
"""
|
|
def _classify(chart):
|
|
planets = chart.get("planets", {})
|
|
strong = any(planets.get(p, {}).get("house") == 7 for p in ("Mars", "Venus"))
|
|
moderate = any(planets.get(p, {}).get("house") == 7 for p in ("Mercury", "Jupiter"))
|
|
if strong and not moderate:
|
|
return "strong"
|
|
if moderate and not strong:
|
|
return "moderate"
|
|
if strong and moderate:
|
|
return "mixed"
|
|
return "unknown"
|
|
|
|
m_type = _classify(chart1)
|
|
f_type = _classify(chart2)
|
|
if m_type in ("unknown", "mixed") or f_type in ("unknown", "mixed"):
|
|
return {"result": "neutral", "male": m_type, "female": f_type,
|
|
"description": "Insufficient data for sex energy assessment."}
|
|
if m_type == f_type:
|
|
return {"result": "good", "male": m_type, "female": f_type,
|
|
"description": f"Both partners have {m_type} sex energy — compatible."}
|
|
return {"result": "bad", "male": m_type, "female": f_type,
|
|
"description": f"Male has {m_type} and female has {f_type} sex energy — potential mismatch."}
|
|
|
|
|
|
def calculate_ashtakoot(male_moon_lon: float, female_moon_lon: float,
|
|
male_chart=None, female_chart=None):
|
|
"""
|
|
Calculates the 36-point Ashtakoot compatibility matching
|
|
plus additional kutas (Mahendra, Stree Deergha, Vedha, Rajju, etc.).
|
|
"""
|
|
m_nak = get_nakshatra(male_moon_lon)
|
|
f_nak = get_nakshatra(female_moon_lon)
|
|
m_nak_idx = m_nak["index"]
|
|
f_nak_idx = f_nak["index"]
|
|
|
|
m_sign_idx = int((male_moon_lon % 360) / 30)
|
|
f_sign_idx = int((female_moon_lon % 360) / 30)
|
|
m_sign = ZODIAC_SIGNS[m_sign_idx]
|
|
f_sign = ZODIAC_SIGNS[f_sign_idx]
|
|
|
|
scores = {
|
|
"Varna": calc_varna(m_sign, f_sign),
|
|
"Vashya": calc_vashya(m_sign, f_sign),
|
|
"Tara": calc_tara(m_nak_idx, f_nak_idx),
|
|
"Yoni": calc_yoni(m_nak_idx, f_nak_idx),
|
|
"GrahaMaitri": calc_graha_maitri(m_sign, f_sign),
|
|
"Gana": calc_gana(m_nak_idx, f_nak_idx),
|
|
"Bhakoot": calc_bhakoot(m_sign, f_sign),
|
|
"Nadi": calc_nadi(m_nak_idx, f_nak_idx),
|
|
}
|
|
|
|
total_score = sum(scores.values())
|
|
|
|
# Additional kutas
|
|
rajju_result = calc_rajju(m_nak_idx, f_nak_idx)
|
|
additional_kutas = {
|
|
"Mahendra": calc_mahendra(m_nak_idx, f_nak_idx),
|
|
"StreeDeergha": calc_stree_deergha(m_nak_idx, f_nak_idx),
|
|
"Vedha": calc_vedha(m_nak_idx, f_nak_idx),
|
|
"Rajju": rajju_result,
|
|
"BadConstellations": calc_bad_constellations(
|
|
m_nak_idx, m_nak.get("pada", 0), f_nak_idx, f_nak.get("pada", 0)),
|
|
}
|
|
|
|
# Chart-dependent kutas (need full chart data)
|
|
if male_chart and female_chart:
|
|
additional_kutas["LagnaHouse7"] = calc_lagna_house7(male_chart, female_chart)
|
|
additional_kutas["SexEnergy"] = calc_sex_energy(male_chart, female_chart)
|
|
|
|
# Exception logic (per VedAstro/BPHS):
|
|
exceptions = []
|
|
|
|
# 1. Bad Nadi neutralized if Bhakoot + Rajju both good
|
|
if scores["Nadi"] == 0:
|
|
if scores["Bhakoot"] > 0 and rajju_result["result"] == "good":
|
|
exceptions.append("Nadi Dosha mitigated by good Bhakoot and Rajju.")
|
|
|
|
# 2. Bad Rajju neutralized if GrahaMaitri + Bhakoot + Tara + Mahendra all good
|
|
if rajju_result["result"] == "bad":
|
|
if (scores["GrahaMaitri"] >= 4.0 and scores["Bhakoot"] > 0 and
|
|
scores["Tara"] >= 1.5 and additional_kutas["Mahendra"] == "good"):
|
|
exceptions.append("Rajju Dosha mitigated by good Graha Maitri, Bhakoot, Tara, and Mahendra.")
|
|
|
|
# 3. Bad Stree Deergha neutralized if Bhakoot + GrahaMaitri both good
|
|
if additional_kutas["StreeDeergha"] == "bad":
|
|
if scores["Bhakoot"] > 0 and scores["GrahaMaitri"] >= 4.0:
|
|
exceptions.append("Stree Deergha Dosha mitigated by good Bhakoot and Graha Maitri.")
|
|
|
|
return {
|
|
"male_details": {
|
|
"moon_sign": m_sign,
|
|
"nakshatra": m_nak["name"],
|
|
"gana": GANA[m_nak_idx],
|
|
"nadi": NADI[m_nak_idx],
|
|
"yoni": YONI_ANIMALS[m_nak_idx]
|
|
},
|
|
"female_details": {
|
|
"moon_sign": f_sign,
|
|
"nakshatra": f_nak["name"],
|
|
"gana": GANA[f_nak_idx],
|
|
"nadi": NADI[f_nak_idx],
|
|
"yoni": YONI_ANIMALS[f_nak_idx]
|
|
},
|
|
"scores": scores,
|
|
"total_score": total_score,
|
|
"max_score": 36.0,
|
|
"additional_kutas": additional_kutas,
|
|
"exceptions": exceptions,
|
|
"is_match_approved": total_score >= 18.0 and (scores["Nadi"] > 0 or len(exceptions) > 0)
|
|
}
|