Skip to content

Commit cc5e5a8

Browse files
authored
Merge pull request #57 from bidyashish/work1
feat: add birth chart data and implement UI updates for kundali chart…
2 parents a8eb3d5 + aa659d9 commit cc5e5a8

25 files changed

Lines changed: 1005 additions & 8 deletions

File tree

CLAUDE.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ Browser → `http://localhost:3121` (Vite dev server / Nginx-served static build
6565
- `panchang_extras.py` — verified extra-yoga detectors layered on top of `advanced_panchang`: Ganda Mūla window + Ravi Yoga window. (Other classical sections like Mantri Mandala / Agnivāsa / Śivavāsa are intentionally omitted — see file docstring.)
6666
- `gowri_panchang.py` — Tamil/Telugu Gowri Panchangam (Nalla Neram). Splits sunrise→sunset and sunset→next-sunrise into 8 segments each, labels them via the 8-name cycle (Soram, Uthi, Visham, Amridha, Rogam, Labam, Dhanam, Sugam) plus an auspicious tag. Per-weekday starting Gowri lives in `GOWRI_DAY_START` / `GOWRI_NIGHT_START` — change those tables if your regional source disagrees. Exposed via `panchang["gowri_panchang"] = {day, night}` and rendered behind the **Telugu** tab on `/panchang`.
6767
- `hora.py` — Planetary Hora hours. 12 day-horas (sunrise→sunset / 12) + 12 night-horas (sunset→next-sunrise / 12). Cycles forward through `HORA_CYCLE = [Sun, Venus, Mercury, Moon, Saturn, Jupiter, Mars]` starting from the day-lord; the night seamlessly continues the same cycle (12 mod 7 = 5 positions later). Auspicious = {Jupiter, Venus, Mercury, Moon}. Exposed via `panchang["hora"] = {day, night}` and rendered on `/panchang` for **both** style tabs (Hora is universal across traditions).
68+
- `tyajyam.py` — Tyajyam (inauspicious time periods). Four calculation types: Nakshatra Tyajyam (ratio-based offset within nakshatra span, 96 min fixed), Tithi Tyajyam (ratio-based offset within tithi span, 96 min fixed), Vara Tyajyam (nazhigai offset from sunrise, 90 min fixed), and Amritadi Yogam (weekday + nakshatra lookup yielding Siddha/Amrita/Marana/Prabalarishta). Exposed via `panchang["tyajyam"]`. Lagna Tyajyam is defined but not wired in yet (needs lagna start/end JDs from udaya_lagna).
6869
- `vargas.py` — 16 divisional charts (D1–D60). D30 uses special uneven-segment rules; touch with care.
6970
- `ayanamsa.py` — ayanamsa selection (`AYANAMSA_OPTIONS`). Default `lahiri`.
7071
- `muhurta.py` — auspicious-window scanner with purpose-based scoring (0–100 with explainable reasons).

backend/advanced_panchang.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
from hora import compute_hora
2828
from nalla_neram import compute_nalla_neram
2929
from tamil_calendar import compute_tamil_calendar
30+
from tyajyam import compute_tyajyam
3031
from panchang_constants import (
3132
CHANDRA_MASA,
3233
CHANDRA_VASA,
@@ -982,6 +983,17 @@ def _attach_iso(items, jd_key="ends_at_jd"):
982983
va = _compute_varjyam_amrit(naks_with_bounds, tz)
983984
siddhi = _compute_siddhi_yogas(naks_with_bounds, vara_iso, tz)
984985

986+
# Tyajyam (nakshatra, tithi, vara, amritadi yogam)
987+
tyajyam = compute_tyajyam(
988+
nakshatras_with_bounds=naks_with_bounds,
989+
tithis_in_window=tithis,
990+
sunrise_jd=sunrise_jd,
991+
next_sunrise_jd=next_sunrise_jd,
992+
weekday_iso=vara_iso,
993+
iso_fn=_iso,
994+
tz=tz,
995+
)
996+
985997
# Bhadra (any Vishti karana period in window) - compute start/end
986998
bhadra = []
987999
prev_end_jd = ref_jd
@@ -1207,6 +1219,7 @@ def _attach_iso(items, jd_key="ends_at_jd"):
12071219
},
12081220
"gowri_panchang": gowri,
12091221
"hora": hora,
1222+
"tyajyam": tyajyam,
12101223
"nalla_neram": nalla,
12111224
"tamil_calendar": tamil_cal,
12121225
"calendars": {

backend/tests/test_tyajyam.py

Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
"""Tests for Tyajyam calculations."""
2+
3+
import pytz
4+
5+
from tyajyam import (
6+
NAKSHATRA_TYAJYAM_RATIO,
7+
VARA_TYAJYAM_NAZHIGAI,
8+
_AMRITADI_TABLE,
9+
_tithi_ratio,
10+
compute_nakshatra_tyajyam,
11+
compute_vara_tyajyam,
12+
compute_amritadi_yogam,
13+
compute_tyajyam,
14+
)
15+
16+
TZ = pytz.timezone("Asia/Kolkata")
17+
18+
19+
def _iso(jd, tz):
20+
"""Simplified JD->ISO for testing (minutes-level precision)."""
21+
from advanced_panchang import _iso as real_iso
22+
23+
return real_iso(jd, tz)
24+
25+
26+
# ---- Table dimension tests ----
27+
28+
29+
def test_nakshatra_ratio_table_has_27_entries():
30+
assert len(NAKSHATRA_TYAJYAM_RATIO) == 27
31+
32+
33+
def test_amritadi_table_has_27_rows_of_7_columns():
34+
assert len(_AMRITADI_TABLE) == 27
35+
for i, row in enumerate(_AMRITADI_TABLE):
36+
assert len(row) == 7, f"Row {i} has {len(row)} cols"
37+
38+
39+
def test_amritadi_table_valid_codes():
40+
valid = {"A", "S", "M", "P"}
41+
for i, row in enumerate(_AMRITADI_TABLE):
42+
for j, ch in enumerate(row):
43+
assert ch in valid, f"Row {i} col {j}: invalid code '{ch}'"
44+
45+
46+
def test_vara_nazhigai_covers_all_weekdays():
47+
for d in range(1, 8):
48+
assert d in VARA_TYAJYAM_NAZHIGAI
49+
50+
51+
# ---- Tithi ratio tests ----
52+
53+
54+
def test_tithi_ratio_pratipada():
55+
assert _tithi_ratio(1) == (2, 5)
56+
assert _tithi_ratio(16) == (2, 5) # Krishna Pratipada same base
57+
58+
59+
def test_tithi_ratio_dashami():
60+
assert _tithi_ratio(10) == (11, 20)
61+
assert _tithi_ratio(25) == (11, 20) # Krishna Dashami
62+
63+
64+
def test_tithi_ratio_purnima():
65+
assert _tithi_ratio(15) == (29, 60)
66+
67+
68+
def test_tithi_ratio_amavasya():
69+
assert _tithi_ratio(30) == (1, 10)
70+
71+
72+
# ---- Spec example: Ashwini Nakshatra Tyajyam ----
73+
74+
75+
def test_ashwini_tyajyam_matches_spec():
76+
"""Ashwini ratio 5/6, 12h span -> start at 10h, duration 96 min."""
77+
base_jd = 2460000.0
78+
span_jd = 12.0 / 24.0 # 12 hours
79+
naks = [{"nak_idx": 0, "start_jd": base_jd, "end_jd": base_jd + span_jd}]
80+
results = compute_nakshatra_tyajyam(naks, _iso, TZ)
81+
assert len(results) == 1
82+
assert results[0]["nakshatra"] == "Ashwini"
83+
84+
85+
def test_nakshatra_tyajyam_skips_if_offset_past_end():
86+
"""If the ratio puts the start beyond the nakshatra end, skip it."""
87+
base_jd = 2460000.0
88+
# Give Chitra (ratio 14/15) only a 1-hour window - tyajyam starts at 56 min
89+
span_jd = 1.0 / 24.0
90+
naks = [{"nak_idx": 13, "start_jd": base_jd, "end_jd": base_jd + span_jd}]
91+
results = compute_nakshatra_tyajyam(naks, _iso, TZ)
92+
# Chitra 14/15 of 60 min = 56 min offset, so start is at 56 min — within window
93+
assert len(results) == 1
94+
95+
96+
# ---- Vara Tyajyam ----
97+
98+
99+
def test_vara_tyajyam_friday():
100+
"""Friday: 21 nazhigai * 24 min = 504 min from sunrise, 90 min duration."""
101+
base_jd = 2460000.0
102+
result = compute_vara_tyajyam(base_jd, 5, _iso, TZ)
103+
assert result is not None
104+
assert "start" in result
105+
assert "end" in result
106+
107+
108+
def test_vara_tyajyam_none_without_sunrise():
109+
assert compute_vara_tyajyam(None, 5, _iso, TZ) is None
110+
111+
112+
# ---- Amritadi Yogam ----
113+
114+
115+
def test_amritadi_safe_nakshatras_never_marana():
116+
"""Punarvasu, Purva Phalguni, Swati, Uttara Bhadrapada never produce M or P."""
117+
safe_indices = [6, 10, 14, 25]
118+
for idx in safe_indices:
119+
row = _AMRITADI_TABLE[idx]
120+
assert "M" not in row and "P" not in row, (
121+
f"Nakshatra {idx} should never have Marana or Prabalarishta"
122+
)
123+
124+
125+
def test_amritadi_yogam_returns_correct_yogam():
126+
"""Thursday (iso=4) + Ashwini -> column index 3 -> 'A' -> Amrita."""
127+
base_jd = 2460000.0
128+
naks = [{"nak_idx": 0, "start_jd": base_jd, "end_jd": base_jd + 0.5}]
129+
results = compute_amritadi_yogam(naks, 4, base_jd, base_jd + 1.0, _iso, TZ)
130+
assert len(results) == 1
131+
assert results[0]["yogam"] == "Amrita"
132+
assert results[0]["nakshatra"] == "Ashwini"
133+
134+
135+
def test_amritadi_bharani_sunday_is_prabalarishta():
136+
"""Sunday (iso=7) + Bharani -> column index 6 -> 'P' -> Prabalarishta."""
137+
base_jd = 2460000.0
138+
naks = [{"nak_idx": 1, "start_jd": base_jd, "end_jd": base_jd + 0.5}]
139+
results = compute_amritadi_yogam(naks, 7, base_jd, base_jd + 1.0, _iso, TZ)
140+
assert len(results) == 1
141+
assert results[0]["yogam"] == "Prabalarishta"
142+
143+
144+
# ---- Integration test ----
145+
146+
147+
def test_compute_tyajyam_returns_all_keys():
148+
base_jd = 2460000.0
149+
naks = [{"nak_idx": 0, "start_jd": base_jd, "end_jd": base_jd + 0.5}]
150+
tithis = [
151+
{
152+
"index": 1,
153+
"name": "Shukla Pratipada",
154+
"start_jd": base_jd,
155+
"ends_at_jd": base_jd + 0.5,
156+
}
157+
]
158+
result = compute_tyajyam(
159+
nakshatras_with_bounds=naks,
160+
tithis_in_window=tithis,
161+
sunrise_jd=base_jd,
162+
next_sunrise_jd=base_jd + 1.0,
163+
weekday_iso=4,
164+
iso_fn=_iso,
165+
tz=TZ,
166+
)
167+
assert "nakshatra_tyajyam" in result
168+
assert "tithi_tyajyam" in result
169+
assert "vara_tyajyam" in result
170+
assert "amritadi_yogam" in result
171+
172+
173+
def test_full_panchang_includes_tyajyam():
174+
"""End-to-end: compute_detailed_panchang returns tyajyam section."""
175+
from advanced_panchang import compute_detailed_panchang
176+
177+
result = compute_detailed_panchang(
178+
"2026-05-16", 49.888, -119.496, "America/Vancouver"
179+
)
180+
assert "tyajyam" in result
181+
tyajyam = result["tyajyam"]
182+
assert "nakshatra_tyajyam" in tyajyam
183+
assert "tithi_tyajyam" in tyajyam
184+
assert "vara_tyajyam" in tyajyam
185+
assert "amritadi_yogam" in tyajyam
186+
# Should have at least one entry in most categories
187+
assert len(tyajyam["nakshatra_tyajyam"]) >= 1
188+
assert len(tyajyam["amritadi_yogam"]) >= 1

0 commit comments

Comments
 (0)