Add Rangacharya comparison variant
This commit is contained in:
@@ -5947,6 +5947,13 @@ class JyotishAPIHandler(BaseHTTPRequestHandler):
|
||||
allowed_modes = {'all', 'karaka', 'dasha', 'karakamsha', 'arudha', 'special'}
|
||||
if mode not in allowed_modes:
|
||||
raise BadRequest(f'mode must be one of: {", ".join(sorted(allowed_modes))}')
|
||||
variant = body.get('variant', 'current')
|
||||
if not isinstance(variant, str):
|
||||
raise BadRequest('variant must be a string')
|
||||
variant = variant.strip().lower() or 'current'
|
||||
allowed_variants = {'current', 'rangacharya', 'all'}
|
||||
if variant not in allowed_variants:
|
||||
raise BadRequest(f'variant must be one of: {", ".join(sorted(allowed_variants))}')
|
||||
antardasha = bool(body.get('antardasha', False))
|
||||
year = self._get_int(body, 'year', datetime.now().year, 1800, 2400)
|
||||
month = self._get_int(body, 'month', 1, 1, 12)
|
||||
@@ -5976,6 +5983,12 @@ class JyotishAPIHandler(BaseHTTPRequestHandler):
|
||||
if mode in ('all', 'arudha'):
|
||||
result['arudha_padas'] = jaimini.calc_arudha_padas(asc_sign_idx, planet_lons)
|
||||
result['graha_padas'] = jaimini.calc_graha_padas(planet_lons)
|
||||
if variant in ('rangacharya', 'all'):
|
||||
rangacharya = _load_local_module('rangacharya')
|
||||
rangacharya_result = rangacharya.calc_rangacharya_variant(asc_sign_idx, planet_lons)
|
||||
result['rangacharya'] = rangacharya_result
|
||||
current_arudha = result.get('arudha_padas') or jaimini.calc_arudha_padas(asc_sign_idx, planet_lons)
|
||||
result['rangacharya_diff'] = rangacharya.diff_current_vs_rangacharya(current_arudha, rangacharya_result)
|
||||
if mode in ('all', 'special'):
|
||||
result['special_lagnas'] = jaimini.calc_special_lagnas(asc_sign_idx, hour, minute + second / 60.0)
|
||||
return {
|
||||
|
||||
@@ -0,0 +1,143 @@
|
||||
"""Experimental Rangacharya/Jaimini variant.
|
||||
|
||||
All outputs are blocked from adjudication until formula-level validation passes.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
from pathlib import Path
|
||||
from typing import Any, Dict, Mapping
|
||||
|
||||
|
||||
SIGNS = [
|
||||
"Aries",
|
||||
"Taurus",
|
||||
"Gemini",
|
||||
"Cancer",
|
||||
"Leo",
|
||||
"Virgo",
|
||||
"Libra",
|
||||
"Scorpio",
|
||||
"Sagittarius",
|
||||
"Capricorn",
|
||||
"Aquarius",
|
||||
"Pisces",
|
||||
]
|
||||
|
||||
SOURCE_CARDS_PATH = Path(__file__).resolve().parent.parent / "references" / "rangacharya_source_cards.json"
|
||||
|
||||
|
||||
class RangacharyaValidationError(RuntimeError):
|
||||
pass
|
||||
|
||||
|
||||
def _source_cards() -> Dict[str, Dict[str, Any]]:
|
||||
try:
|
||||
data = json.loads(SOURCE_CARDS_PATH.read_text(encoding="utf-8"))
|
||||
except (FileNotFoundError, json.JSONDecodeError):
|
||||
return {}
|
||||
return {str(card.get("id")): dict(card) for card in data.get("cards", []) if card.get("id")}
|
||||
|
||||
|
||||
def _card_meta(card_id: str) -> Dict[str, Any]:
|
||||
card = _source_cards().get(card_id, {})
|
||||
status = str(card.get("status") or "blocked")
|
||||
meta = {
|
||||
"source_card_id": card_id,
|
||||
"source_card_status": status,
|
||||
"validation_status": status,
|
||||
"adjudication_enabled": False,
|
||||
}
|
||||
if status != "source_verified":
|
||||
meta["blocked_reason"] = card.get("blocked_reason") or "source card is not verified for adjudication"
|
||||
return meta
|
||||
|
||||
|
||||
def _sign_name(index: int) -> str:
|
||||
return SIGNS[index % 12]
|
||||
|
||||
|
||||
def _placeholder_pada(label: str, asc_sign_idx: int, source_house: int) -> Dict[str, Any]:
|
||||
sign_idx = (asc_sign_idx + source_house - 1) % 12
|
||||
return {
|
||||
"label": label,
|
||||
"sign": _sign_name(sign_idx),
|
||||
"sign_index": sign_idx,
|
||||
"source_house": source_house,
|
||||
"note": "Rangacharya formula pending source-card implementation",
|
||||
**_card_meta("rangacharya_core_arudha"),
|
||||
}
|
||||
|
||||
|
||||
def calc_rangacharya_variant(asc_sign_idx: int, planet_longitudes: Mapping[str, float]) -> Dict[str, Any]:
|
||||
asc_sign_idx %= 12
|
||||
arudha_padas = {
|
||||
"AL": _placeholder_pada("AL", asc_sign_idx, 1),
|
||||
"A7": _placeholder_pada("A7", asc_sign_idx, 7),
|
||||
"A10": _placeholder_pada("A10", asc_sign_idx, 10),
|
||||
"UL": _placeholder_pada("UL", asc_sign_idx, 12),
|
||||
}
|
||||
return {
|
||||
"variant": "rangacharya",
|
||||
"status": "experimental_not_for_adjudication",
|
||||
"adjudication_enabled": False,
|
||||
"source_status": "transcribed",
|
||||
"active_lagna": {
|
||||
"sign": _sign_name(asc_sign_idx),
|
||||
**_card_meta("active_effective_lagna"),
|
||||
},
|
||||
"effective_lagna": {
|
||||
"sign": _sign_name(asc_sign_idx),
|
||||
**_card_meta("active_effective_lagna"),
|
||||
},
|
||||
"arudha_padas": arudha_padas,
|
||||
"input_planets_present": sorted(planet_longitudes),
|
||||
}
|
||||
|
||||
|
||||
def _flatten(prefix: str, value: Any) -> Dict[str, Any]:
|
||||
if not isinstance(value, dict):
|
||||
return {prefix: value}
|
||||
rows: Dict[str, Any] = {}
|
||||
for key, child in value.items():
|
||||
child_key = f"{prefix}.{key}" if prefix else str(key)
|
||||
rows.update(_flatten(child_key, child))
|
||||
return rows
|
||||
|
||||
|
||||
def diff_current_vs_rangacharya(current: Mapping[str, Any], variant: Mapping[str, Any]) -> Dict[str, Any]:
|
||||
current_flat = _flatten("", dict(current))
|
||||
variant_flat = _flatten("", dict(variant.get("arudha_padas", variant)))
|
||||
differences = []
|
||||
for key in sorted(set(current_flat) | set(variant_flat)):
|
||||
current_value = current_flat.get(key)
|
||||
variant_value = variant_flat.get(key)
|
||||
if current_value != variant_value:
|
||||
differences.append({"key": key, "current": current_value, "rangacharya": variant_value})
|
||||
return {
|
||||
"current_algorithm": "current_jaimini",
|
||||
"variant_algorithm": "rangacharya",
|
||||
"adjudication_enabled": False,
|
||||
"differences": differences,
|
||||
}
|
||||
|
||||
|
||||
def validation_summary(result: Mapping[str, Any]) -> Dict[str, Any]:
|
||||
statuses = []
|
||||
for key, value in _flatten("", dict(result)).items():
|
||||
if key.endswith("validation_status"):
|
||||
statuses.append(str(value))
|
||||
blocking = sorted({status for status in statuses if status != "adjudication_enabled"})
|
||||
return {
|
||||
"adjudication_enabled": bool(result.get("adjudication_enabled")) and not blocking,
|
||||
"blocking_statuses": blocking,
|
||||
}
|
||||
|
||||
|
||||
def assert_adjudication_allowed(result: Mapping[str, Any]) -> None:
|
||||
summary = validation_summary(result)
|
||||
if not summary["adjudication_enabled"]:
|
||||
raise RangacharyaValidationError(
|
||||
"Rangacharya variant is not adjudication-enabled; validation gates are incomplete"
|
||||
)
|
||||
@@ -0,0 +1,58 @@
|
||||
"""Readiness report for the experimental Rangacharya variant."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
from pathlib import Path
|
||||
from typing import Any, Dict
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parent.parent
|
||||
CARDS_PATH = ROOT / "references" / "rangacharya_source_cards.json"
|
||||
MANIFEST_PATH = ROOT / "references" / "rangacharya_source_manifest.json"
|
||||
|
||||
|
||||
def _load_json(path: Path) -> Dict[str, Any]:
|
||||
try:
|
||||
return json.loads(path.read_text(encoding="utf-8"))
|
||||
except (FileNotFoundError, json.JSONDecodeError):
|
||||
return {}
|
||||
|
||||
|
||||
def build_report() -> Dict[str, Any]:
|
||||
cards_payload = _load_json(CARDS_PATH)
|
||||
manifest_payload = _load_json(MANIFEST_PATH)
|
||||
cards = {}
|
||||
blocked = []
|
||||
transcribed = []
|
||||
for card in cards_payload.get("cards", []):
|
||||
card_id = str(card.get("id") or "")
|
||||
if not card_id:
|
||||
continue
|
||||
status = str(card.get("status") or "blocked")
|
||||
adjudication_enabled = bool(card.get("adjudication_enabled"))
|
||||
cards[card_id] = {
|
||||
"status": status,
|
||||
"adjudication_enabled": adjudication_enabled,
|
||||
"blocked_reason": card.get("blocked_reason") or "",
|
||||
}
|
||||
if status == "blocked" or not adjudication_enabled:
|
||||
blocked.append(card_id)
|
||||
if status == "transcribed":
|
||||
transcribed.append(card_id)
|
||||
return {
|
||||
"scope": "rangacharya_readiness",
|
||||
"manifest_available": bool(manifest_payload),
|
||||
"source_cards_available": bool(cards_payload),
|
||||
"adjudication_enabled": bool(cards) and not blocked,
|
||||
"card_count": len(cards),
|
||||
"blocked_count": len(blocked),
|
||||
"transcribed_count": len(transcribed),
|
||||
"blocked_cards": blocked,
|
||||
"transcribed_cards": transcribed,
|
||||
"cards": cards,
|
||||
}
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
print(json.dumps(build_report(), ensure_ascii=False, indent=2, sort_keys=True))
|
||||
Reference in New Issue
Block a user