Enhance Jyotish validation and Jaimini modules
- 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
This commit is contained in:
@@ -0,0 +1,185 @@
|
||||
import datetime
|
||||
from .constants import VIMSHOTTARI_YEARS, DASHA_SEQUENCE, NAK_SPAN
|
||||
from .nakshatra import get_nakshatra
|
||||
|
||||
|
||||
def _add_years_days(dt, years, days):
|
||||
"""Add fractional years (as whole years + remaining days) to a datetime."""
|
||||
total_days = years * 365.2425 + days
|
||||
return dt + datetime.timedelta(days=total_days)
|
||||
|
||||
|
||||
def _build_sub_periods(start_dt, total_days, starting_lord):
|
||||
"""
|
||||
Build sub-periods (Antardasha or Pratyantardasha) within a parent period.
|
||||
The sub-period sequence starts from the parent lord and cycles through
|
||||
the Dasha sequence.
|
||||
"""
|
||||
seq_start = DASHA_SEQUENCE.index(starting_lord)
|
||||
periods = []
|
||||
cursor = start_dt
|
||||
|
||||
for i in range(9):
|
||||
lord = DASHA_SEQUENCE[(seq_start + i) % 9]
|
||||
proportion = VIMSHOTTARI_YEARS[lord] / 120.0
|
||||
sub_days = total_days * proportion
|
||||
end = cursor + datetime.timedelta(days=sub_days)
|
||||
periods.append({
|
||||
"planet": lord,
|
||||
"start": cursor.strftime("%Y-%m-%d"),
|
||||
"end": end.strftime("%Y-%m-%d"),
|
||||
"days": round(sub_days, 2),
|
||||
})
|
||||
cursor = end
|
||||
|
||||
return periods
|
||||
|
||||
|
||||
def calculate_dashas(moon_longitude, birth_dt, query_dt=None):
|
||||
"""
|
||||
Compute Vimshottari Dasha timeline from Moon's sidereal longitude at birth.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
moon_longitude : float
|
||||
Sidereal longitude of the Moon at birth (0-360).
|
||||
birth_dt : datetime.datetime
|
||||
Birth datetime (timezone-aware or naive).
|
||||
query_dt : datetime.datetime, optional
|
||||
Date to find active Maha/Antar/Pratyantar for. Defaults to today.
|
||||
|
||||
Returns
|
||||
-------
|
||||
dict with keys: maha, antar, pratyantar, timeline
|
||||
"""
|
||||
if query_dt is None:
|
||||
query_dt = datetime.datetime.now()
|
||||
if hasattr(birth_dt, 'tzinfo') and birth_dt.tzinfo:
|
||||
birth_dt = birth_dt.replace(tzinfo=None)
|
||||
if hasattr(query_dt, 'tzinfo') and query_dt.tzinfo:
|
||||
query_dt = query_dt.replace(tzinfo=None)
|
||||
|
||||
nak_info = get_nakshatra(moon_longitude)
|
||||
nak_lord = nak_info["lord"]
|
||||
|
||||
elapsed_fraction = nak_info["degree_in_nakshatra"] / NAK_SPAN
|
||||
remaining_fraction = 1.0 - elapsed_fraction
|
||||
|
||||
seq_start = DASHA_SEQUENCE.index(nak_lord)
|
||||
|
||||
# Build Mahadasha timeline starting from birth
|
||||
timeline = []
|
||||
cursor = birth_dt
|
||||
|
||||
first_maha_years = VIMSHOTTARI_YEARS[nak_lord] * remaining_fraction
|
||||
first_maha_days = first_maha_years * 365.2425
|
||||
first_end = cursor + datetime.timedelta(days=first_maha_days)
|
||||
timeline.append({
|
||||
"planet": nak_lord,
|
||||
"start": cursor.strftime("%Y-%m-%d"),
|
||||
"end": first_end.strftime("%Y-%m-%d"),
|
||||
"years": round(first_maha_years, 4),
|
||||
"days": round(first_maha_days, 2),
|
||||
})
|
||||
cursor = first_end
|
||||
|
||||
# Remaining 8 full cycles, then repeat to cover 120+ years
|
||||
for cycle in range(2):
|
||||
start_offset = 1 if cycle == 0 else 0
|
||||
for i in range(start_offset, 9):
|
||||
lord = DASHA_SEQUENCE[(seq_start + i) % 9]
|
||||
years = VIMSHOTTARI_YEARS[lord]
|
||||
days = years * 365.2425
|
||||
end = cursor + datetime.timedelta(days=days)
|
||||
timeline.append({
|
||||
"planet": lord,
|
||||
"start": cursor.strftime("%Y-%m-%d"),
|
||||
"end": end.strftime("%Y-%m-%d"),
|
||||
"years": float(years),
|
||||
"days": days,
|
||||
})
|
||||
cursor = end
|
||||
|
||||
active_maha = None
|
||||
active_antar = None
|
||||
active_pratyantar = None
|
||||
active_sukshma = None
|
||||
active_prana = None
|
||||
|
||||
for period in timeline:
|
||||
p_start = datetime.datetime.strptime(period["start"], "%Y-%m-%d")
|
||||
p_end = datetime.datetime.strptime(period["end"], "%Y-%m-%d")
|
||||
if p_start <= query_dt < p_end:
|
||||
active_maha = period
|
||||
break
|
||||
|
||||
if active_maha:
|
||||
maha_start = datetime.datetime.strptime(active_maha["start"], "%Y-%m-%d")
|
||||
maha_days = active_maha["days"]
|
||||
antars = _build_sub_periods(maha_start, maha_days, active_maha["planet"])
|
||||
|
||||
for antar in antars:
|
||||
a_start = datetime.datetime.strptime(antar["start"], "%Y-%m-%d")
|
||||
a_end = datetime.datetime.strptime(antar["end"], "%Y-%m-%d")
|
||||
if a_start <= query_dt < a_end:
|
||||
active_antar = antar
|
||||
break
|
||||
|
||||
if active_antar:
|
||||
antar_start = datetime.datetime.strptime(active_antar["start"], "%Y-%m-%d")
|
||||
antar_days = active_antar["days"]
|
||||
pratyantars = _build_sub_periods(antar_start, antar_days, active_antar["planet"])
|
||||
|
||||
for prat in pratyantars:
|
||||
pr_start = datetime.datetime.strptime(prat["start"], "%Y-%m-%d")
|
||||
pr_end = datetime.datetime.strptime(prat["end"], "%Y-%m-%d")
|
||||
if pr_start <= query_dt < pr_end:
|
||||
active_pratyantar = prat
|
||||
break
|
||||
|
||||
# Level 4: Sukshma Dasha
|
||||
if active_pratyantar:
|
||||
prat_start = datetime.datetime.strptime(active_pratyantar["start"], "%Y-%m-%d")
|
||||
prat_days = active_pratyantar["days"]
|
||||
sukshmas = _build_sub_periods(prat_start, prat_days, active_pratyantar["planet"])
|
||||
|
||||
for suk in sukshmas:
|
||||
s_start = datetime.datetime.strptime(suk["start"], "%Y-%m-%d")
|
||||
s_end = datetime.datetime.strptime(suk["end"], "%Y-%m-%d")
|
||||
if s_start <= query_dt < s_end:
|
||||
active_sukshma = suk
|
||||
break
|
||||
|
||||
# Level 5: Prana Dasha
|
||||
if active_sukshma:
|
||||
suk_start = datetime.datetime.strptime(active_sukshma["start"], "%Y-%m-%d")
|
||||
suk_days = active_sukshma["days"]
|
||||
pranas = _build_sub_periods(suk_start, suk_days, active_sukshma["planet"])
|
||||
|
||||
for pra in pranas:
|
||||
pra_start = datetime.datetime.strptime(pra["start"], "%Y-%m-%d")
|
||||
pra_end = datetime.datetime.strptime(pra["end"], "%Y-%m-%d")
|
||||
if pra_start <= query_dt < pra_end:
|
||||
active_prana = pra
|
||||
break
|
||||
|
||||
# Trim timeline to a reasonable window (birth to ~120 years)
|
||||
compact_timeline = []
|
||||
for t in timeline:
|
||||
compact_timeline.append({
|
||||
"planet": t["planet"],
|
||||
"start": t["start"],
|
||||
"end": t["end"],
|
||||
})
|
||||
t_end = datetime.datetime.strptime(t["end"], "%Y-%m-%d")
|
||||
if t_end > birth_dt + datetime.timedelta(days=120 * 365.2425):
|
||||
break
|
||||
|
||||
return {
|
||||
"maha": active_maha,
|
||||
"antar": active_antar,
|
||||
"pratyantar": active_pratyantar,
|
||||
"sukshma": active_sukshma,
|
||||
"prana": active_prana,
|
||||
"timeline": compact_timeline,
|
||||
}
|
||||
Reference in New Issue
Block a user