#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ yogini_dasha.py — Yogini Dasha System (36-Year Cycle) The Yogini Dasha is a Nakshatra-based dasha system with a total cycle of 36 years. It consists of 8 Yoginis (female planetary deities), each ruling a specific number of years. The sequence is fixed and cycles continuously. The 8 Yoginis, their year allotments, and associated planets: Order | Yogini | Years | Planet ------|-----------|-------|-------- 1 | Maya | 1 | Sun 2 | Bhadrika | 2 | Moon 3 | Jaya | 3 | Mars 4 | Rati | 4 | Mercury 5 | Mangala | 5 | Jupiter 6 | Pingala | 6 | Saturn 7 | Dhanya | 7 | Venus 8 | Bhramari | 8 | Rahu Total: 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 = 36 years. Starting Yogini: The starting Yogini is determined by the Ascendant (Lagna) Rashi. Per standard texts: - Aries / Leo / Sagittarius / Gemini / Libra / Aquarius → start from Maya (1) - Taurus / Virgo / Capricorn / Cancer / Scorpio / Pisces → start from Bhadrika (2) Alternative (more common) rule based on Lagna degree modulo 8: Lagna Nakshatra Pada determines the starting Yogini. For simplicity, this implementation uses the Ascendant Rashi rule above. Reference: Various classical and modern Jyotish texts on Yogini Dasha. """ from datetime import datetime, timedelta # --------------------------------------------------------------------------- # Constants # --------------------------------------------------------------------------- # 12 Rashis (Zodiac signs) RASHIS = [ "Aries", "Taurus", "Gemini", "Cancer", "Leo", "Virgo", "Libra", "Scorpio", "Sagittarius", "Capricorn", "Aquarius", "Pisces" ] # Yogini sequence: fixed order, never changes YOGINI_SEQUENCE = [ {"name": "Maya", "years": 1, "planet": "Sun"}, {"name": "Bhadrika", "years": 2, "planet": "Moon"}, {"name": "Jaya", "years": 3, "planet": "Mars"}, {"name": "Rati", "years": 4, "planet": "Mercury"}, {"name": "Mangala", "years": 5, "planet": "Jupiter"}, {"name": "Pingala", "years": 6, "planet": "Saturn"}, {"name": "Dhanya", "years": 7, "planet": "Venus"}, {"name": "Bhramari", "years": 8, "planet": "Rahu"}, ] TOTAL_CYCLE = 36 # years # Lagna Rashi to starting Yogini index mapping # Odd Rashis (0,4,8,2,6,10) → start from Maya (0) # Even Rashis (1,5,9,3,7,11) → start from Bhadrika (1) def _get_start_yogini_index(lagna_rashi_index: int) -> int: """Return starting Yogini index based on Ascendant Rashi.""" if lagna_rashi_index % 2 == 0: return 0 # Maya else: return 1 # Bhadrika # --------------------------------------------------------------------------- # Core Functions # --------------------------------------------------------------------------- def _build_major_periods(start_idx: int, birth_date: datetime) -> list: """ Build the full list of major Yogini periods. Returns a list of dicts with keys: yogini, planet, years, start_date, end_date. """ periods = [] seq_len = len(YOGINI_SEQUENCE) current_date = birth_date for i in range(seq_len): idx = (start_idx + i) % seq_len yogini = YOGINI_SEQUENCE[idx] end_date = current_date + timedelta(days=yogini["years"] * 365.25) periods.append({ "yogini": yogini["name"], "planet": yogini["planet"], "years": yogini["years"], "start_date": current_date.isoformat(), "end_date": end_date.isoformat(), }) current_date = end_date return periods def calculate_yogini_dasha(birth_info: dict) -> dict: """ Calculate Yogini Dasha for a native. Args: birth_info: dict containing: - "lagna_rashi_index": int (0 = Aries, ..., 11 = Pisces) - "birth_datetime": datetime object or ISO-format string (optional) Returns: dict with keys: - "major": list of major period dicts - "current": dict — the currently running major period - "total_cycle": int — 36 - "starting_yogini": str — first Yogini name - "starting_planet": str — planet of first Yogini """ lagna_idx = birth_info.get("lagna_rashi_index", 0) birth_dt = birth_info.get("birth_datetime") if isinstance(birth_dt, str): birth_dt = datetime.fromisoformat(birth_dt) if birth_dt is None: birth_dt = datetime.now() start_idx = _get_start_yogini_index(lagna_idx) starting_yogini = YOGINI_SEQUENCE[start_idx]["name"] starting_planet = YOGINI_SEQUENCE[start_idx]["planet"] major_periods = _build_major_periods(start_idx, birth_dt) # Determine current period. Yogini repeats every 36 years; older natives # should still return a current period instead of None after the first cycle. now = datetime.now() current_period = None age_years = max((now - birth_dt).days / 365.25, 0) current_in_cycle = age_years % TOTAL_CYCLE cumulative = 0.0 for p in major_periods: years = p["years"] if cumulative <= current_in_cycle < cumulative + years: cycle_start = birth_dt + timedelta(days=(age_years - current_in_cycle + cumulative) * 365.25) cycle_end = cycle_start + timedelta(days=years * 365.25) current_period = p.copy() current_period["start_date"] = cycle_start.isoformat() current_period["end_date"] = cycle_end.isoformat() current_period["elapsed_years"] = (now - cycle_start).days / 365.25 current_period["remaining_years"] = (cycle_end - now).days / 365.25 current_period["cycle_number"] = int(age_years // TOTAL_CYCLE) + 1 break cumulative += years return { "major": major_periods, "current": current_period, "total_cycle": TOTAL_CYCLE, "starting_yogini": starting_yogini, "starting_planet": starting_planet, } # --------------------------------------------------------------------------- # Self-test # --------------------------------------------------------------------------- if __name__ == "__main__": # Example 1: Ascendant in Aries (odd sign) → starts from Maya test_birth_1 = { "lagna_rashi_index": 0, # Aries "birth_datetime": datetime(1990, 6, 15, 10, 30), } result_1 = calculate_yogini_dasha(test_birth_1) print("=" * 60) print("Test 1: Ascendant in Aries (odd sign)") print("=" * 60) print(f"Starting Yogini : {result_1['starting_yogini']} ({result_1['starting_planet']})") print(f"Total Cycle : {result_1['total_cycle']} years") print("Major Periods:") for p in result_1["major"]: print(f" {p['yogini']:10s} | {p['planet']:8s} | {p['years']} years | {p['start_date'][:10]} → {p['end_date'][:10]}") print(f"Current Period : {result_1['current']}") print() # Example 2: Ascendant in Taurus (even sign) → starts from Bhadrika test_birth_2 = { "lagna_rashi_index": 1, # Taurus "birth_datetime": datetime(1985, 3, 20, 8, 0), } result_2 = calculate_yogini_dasha(test_birth_2) print("=" * 60) print("Test 2: Ascendant in Taurus (even sign)") print("=" * 60) print(f"Starting Yogini : {result_2['starting_yogini']} ({result_2['starting_planet']})") print(f"Total Cycle : {result_2['total_cycle']} years") for p in result_2["major"]: print(f" {p['yogini']:10s} | {p['planet']:8s} | {p['years']} years | {p['start_date'][:10]} → {p['end_date'][:10]}") print(f"Current Period : {result_2['current']}") print() # Example 3: Ascendant in Scorpio (even sign) test_birth_3 = { "lagna_rashi_index": 7, # Scorpio "birth_datetime": datetime(2000, 1, 1, 0, 0), } result_3 = calculate_yogini_dasha(test_birth_3) print("=" * 60) print("Test 3: Ascendant in Scorpio (even sign)") print("=" * 60) print(f"Starting Yogini : {result_3['starting_yogini']} ({result_3['starting_planet']})") for p in result_3["major"]: print(f" {p['yogini']:10s} | {p['planet']:8s} | {p['years']} years | {p['start_date'][:10]} → {p['end_date'][:10]}")