fix(avastha): keep simultaneous conditions and use the PVR activity formula
Upstream e628118d, 7d545039, f75a6e77, and ac565836. Dignity follows the sign, Deeptadi keeps every condition that applies, and Shayanadi hours come from the previous Hindu sunrise. BUG-1389. Co-Authored-By: Grok (agent loop) Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
co-authored by
Grok (agent loop)
Cursor
parent
0bd254cf18
commit
83c97a8976
@@ -0,0 +1,64 @@
|
||||
"""Classical age/parity and dignity rules, independent of house placement."""
|
||||
import sys
|
||||
from pathlib import Path
|
||||
import pytest
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parents[1] / 'scripts'))
|
||||
from avastha_calculator import AvasthaCalculator
|
||||
|
||||
|
||||
def test_deeptadi_keeps_exaltation_and_combustion_together():
|
||||
row = AvasthaCalculator().calculate_all_avasthas(
|
||||
'Venus', 'Pisces', 27, 1, is_combust=True)['avasthas']['Deeptadi']
|
||||
assert row['state'] == 'Deepta'
|
||||
assert 'Deepta' in row['applicable_states']
|
||||
assert 'Kopa' in row['applicable_states']
|
||||
assert row['conditions']['combust'] is True
|
||||
assert row['legacy_single_label_note'] == '无经典出处'
|
||||
assert row['uncomputed_label'] == '未计算'
|
||||
|
||||
|
||||
@pytest.mark.parametrize('sign,degree,state,strength', [
|
||||
('Aries', 0, 'Infant', .25), ('Aries', 6, 'Young', .5),
|
||||
('Aries', 12, 'Youth', 1), ('Aries', 18, 'Old', None),
|
||||
('Aries', 24, 'Dead', 0), ('Taurus', 0, 'Dead', 0),
|
||||
('Taurus', 6, 'Old', None), ('Taurus', 12, 'Youth', 1),
|
||||
('Taurus', 18, 'Young', .5), ('Taurus', 24, 'Infant', .25),
|
||||
])
|
||||
def test_age_sign_parity_and_exact_boundaries(sign, degree, state, strength):
|
||||
row = AvasthaCalculator().calculate_all_avasthas('Sun', sign, degree, 9)['avasthas']['Bala']
|
||||
assert row['state'] == state and row['strength'] == strength
|
||||
|
||||
|
||||
@pytest.mark.parametrize('planet,sign,expected', [
|
||||
('Sun', 'Aries', 'Awake'), ('Venus', 'Pisces', 'Awake'),
|
||||
('Mars', 'Cancer', 'Deep Sleep'), ('Saturn', 'Aquarius', 'Awake'),
|
||||
('Sun', 'Capricorn', 'Deep Sleep'), ('Sun', 'Sagittarius', 'Dreaming'),
|
||||
('Sun', 'Gemini', 'Dreaming'), ('Rahu', 'Aries', 'Not classified'),
|
||||
])
|
||||
def test_jagrat_follows_dignity_not_house(planet, sign, expected):
|
||||
calculator = AvasthaCalculator()
|
||||
for house in (1, 2, 9):
|
||||
row = calculator.calculate_all_avasthas(planet, sign, 4.95, house)['avasthas']['Jagrat']
|
||||
assert row['state'] == expected
|
||||
|
||||
|
||||
def test_simplified_states_cannot_masquerade_as_full_classical_methods():
|
||||
states = AvasthaCalculator().calculate_all_avasthas('Sun', 'Aries', 4.95, 9)['avasthas']
|
||||
for name in ('Deeptadi', 'Lajjitadi', 'Shayanadi'):
|
||||
assert states[name]['method_status'] == 'simplified_heuristic'
|
||||
assert states['Deeptadi']['state'] == 'Deepta'
|
||||
|
||||
|
||||
@pytest.mark.parametrize('planet,sign,combust,expected', [
|
||||
('Sun', 'Sagittarius', False, 'Shanta'),
|
||||
('Sun', 'Gemini', False, 'Dina'),
|
||||
('Sun', 'Capricorn', False, 'Duhkhita'),
|
||||
('Mars', 'Cancer', False, 'Khala'),
|
||||
('Venus', 'Gemini', True, 'Kopa'),
|
||||
])
|
||||
def test_deeptadi_names_follow_declared_source_not_mislabelled_conditions(planet, sign, combust, expected):
|
||||
row = AvasthaCalculator().calculate_all_avasthas(
|
||||
planet, sign, 4.0, 1, is_combust=combust)['avasthas']['Deeptadi']
|
||||
assert row['state'] == expected
|
||||
assert row['label_profile'] == 'science_of_light_deeptadi_natural_relationship_subset'
|
||||
assert row['method_status'] == 'simplified_heuristic'
|
||||
@@ -0,0 +1,39 @@
|
||||
"""Integration contract for the full-reading Avastha producer."""
|
||||
import ast
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
from types import SimpleNamespace
|
||||
|
||||
|
||||
def test_full_reading_wires_hindu_sunrise_into_activity_formula(monkeypatch):
|
||||
repo = Path(__file__).resolve().parents[1]
|
||||
monkeypatch.syspath_prepend(str(repo / 'scripts'))
|
||||
import saham_daynight
|
||||
seen = {}
|
||||
|
||||
def fake_daytime(*args, **kwargs):
|
||||
seen['rising'] = kwargs.get('rising')
|
||||
return {'julian_day_ut': 2451545.0, 'sunrise_jd_ut': 2451545.0 - 17 / 24}
|
||||
|
||||
monkeypatch.setattr(saham_daynight, 'determine_daytime', fake_daytime)
|
||||
tree = ast.parse((repo / 'scripts/jyotish_engine.py').read_text(encoding='utf-8'))
|
||||
blocks = [node for node in ast.walk(tree) if isinstance(node, ast.Try)
|
||||
and any(isinstance(item, ast.ImportFrom) and item.module == 'avastha_calculator'
|
||||
for item in node.body)]
|
||||
assert len(blocks) == 1
|
||||
report = {'modules': {}, 'errors': []}
|
||||
env = dict(
|
||||
report=report,
|
||||
args=SimpleNamespace(lat=0, lon=0, tz=0),
|
||||
asc_deg=0,
|
||||
planet_lons={'Sun': 0, 'Moon': 0},
|
||||
planets={'Sun': {'degree': 0, 'degree_raw': 0, 'sign': 'Aries', 'house': 1}},
|
||||
_birth_datetime_from_args=lambda args: datetime(2000, 1, 1),
|
||||
)
|
||||
exec(compile(ast.Module(body=blocks, type_ignores=[]), '<avastha-main>', 'exec'), env)
|
||||
assert report['errors'] == []
|
||||
assert seen['rising'] == 'hindu'
|
||||
actual = report['modules']['avasthas']['Sun']['avasthas']['Shayanadi']
|
||||
assert actual['method_status'] == 'pvr_15_4_4_running_ghati'
|
||||
assert actual['components']['running_ghati'] == 43
|
||||
assert actual['state_index'] == 10
|
||||
@@ -0,0 +1,43 @@
|
||||
import math
|
||||
import pytest
|
||||
from scripts.avastha_calculator import calculate_pvr_shayanadi, AvasthaCalculator
|
||||
|
||||
|
||||
def test_pvr_footnotes_navamsa_part_and_running_ghati():
|
||||
# Printed Mercury 22Ge14 is the seventh *part*, not its D9 sign.
|
||||
# Sunrise 06:00, birth 23:00: 42.5 elapsed ghatis, 43rd running.
|
||||
row = calculate_pvr_shayanadi('Mercury', 82 + 14 / 60, 0, 0, 17)
|
||||
assert row['components'] == dict(
|
||||
planet_nakshatra=7, planet_index=4,
|
||||
navamsa_part=7, moon_nakshatra=1, running_ghati=43,
|
||||
lagna_sign_number=1, formula_sum=241,
|
||||
)
|
||||
assert row['state'] == 'Shayana'
|
||||
|
||||
|
||||
def test_zero_remainder_is_twelfth_state():
|
||||
# 1*1*1 + 1 + 9 + 1 = 12.
|
||||
assert calculate_pvr_shayanadi('Sun', 0, 0, 0, 3.2)['state'] == 'Nidra'
|
||||
|
||||
|
||||
def test_exact_navamsa_and_ghati_boundaries():
|
||||
edge = 10 / 3
|
||||
assert calculate_pvr_shayanadi('Sun', math.nextafter(edge, -math.inf), 0, 0, 0)['components']['navamsa_part'] == 1
|
||||
assert calculate_pvr_shayanadi('Sun', edge, 0, 0, 0)['components']['navamsa_part'] == 2
|
||||
assert calculate_pvr_shayanadi('Sun', 0, 0, 0, .4)['components']['running_ghati'] == 2
|
||||
|
||||
|
||||
def test_actual_activity_context_replaces_element_heuristic():
|
||||
row = AvasthaCalculator().calculate_all_avasthas(
|
||||
'Sun', 'Aries', 0, 1,
|
||||
activity_context=dict(moon_longitude=0, asc_longitude=0, hours_since_sunrise=3.2),
|
||||
)
|
||||
assert row['avasthas']['Shayanadi']['state'] == 'Nidra'
|
||||
assert row['avasthas']['Shayanadi']['method_status'] == 'pvr_15_4_4_running_ghati'
|
||||
assert row['avasthas']['Shayanadi']['activity_intensity_status'] == 'requires_name_first_sound_index'
|
||||
|
||||
|
||||
@pytest.mark.parametrize('elapsed', [-1, float('nan'), float('inf'), True])
|
||||
def test_invalid_sunrise_elapsed_is_not_silently_defaulted(elapsed):
|
||||
with pytest.raises(ValueError):
|
||||
calculate_pvr_shayanadi('Sun', 0, 0, 0, elapsed)
|
||||
Reference in New Issue
Block a user