Files
Jyotisha/scripts/collect_vedastro_official_parity_raw.py
T

207 lines
8.3 KiB
Python

#!/usr/bin/env python3
"""Collect bounded, secret-free VedAstro HTTP raw for divisional parity."""
from __future__ import annotations
import argparse
import hashlib
import json
import os
from pathlib import Path
from typing import Any
try:
from scripts import vedastro_service_adapter as adapter
except ModuleNotFoundError: # pragma: no cover - direct script execution
import vedastro_service_adapter as adapter
ROOT = Path(__file__).resolve().parents[1]
DEFAULT_PLANETS = ["Sun", "Moon", "Mars", "Mercury", "Jupiter", "Venus", "Saturn"]
VARGA_FIELDS = {
"D2": "PlanetHoraD2Signs",
"D4": "PlanetChaturthamshaD4Sign",
"D9": "PlanetNavamshaD9Sign",
"D10": "PlanetDashamamshaD10Sign",
}
SHADBALA_COMPONENT_FIELDS = [
"PlanetSthanaBala",
"PlanetDigBala",
"PlanetKalaBala",
"PlanetChestaBala",
"PlanetNaisargikaBala",
"PlanetDrikBala",
]
def _hash(payload: Any) -> str:
raw = json.dumps(payload, ensure_ascii=False, sort_keys=True, separators=(",", ":")).encode("utf-8")
return hashlib.sha256(raw).hexdigest()
def collect_chart_core(
case: dict[str, Any],
*,
case_id: str,
planets: list[str],
output_path: Path,
) -> dict[str, Any]:
endpoint = os.environ.get("VEDASTRO_API_ENDPOINT", "").strip()
if not endpoint:
raise RuntimeError("VEDASTRO_API_ENDPOINT is not configured")
manifest = adapter._official_full_snapshot_manifest(case, case_id)
chart_request = next(item for item in manifest["requests"] if item["section"] == "chart_core")
raw_responses: dict[str, Any] = {}
statuses: dict[str, str] = {}
scalar_responses: dict[str, Any] = {}
scalar_statuses: dict[str, str] = {}
component_responses: dict[str, Any] = {}
component_statuses: dict[str, str] = {}
retry_error_codes: list[int] = []
attempt_count = 0
for planet in planets:
request_item = {**chart_request, "fanout_value": planet}
try:
payload, attempts, retries = adapter._post_official_snapshot_section(endpoint, request_item)
raw_responses[planet] = payload
statuses[planet] = adapter._payload_status(payload)
attempt_count += attempts
retry_error_codes.extend(retries)
except Exception as exc: # Preserve partial raw instead of discarding the batch.
statuses[planet] = f"{type(exc).__name__}:{str(exc)[:200]}"
common_body = adapter._official_common_body(case)
scalar_requests = {
"shadbala": {
"section": "shadbala",
"endpoint_path": "/Calculate/AllPlanetStrength",
"method": "POST",
"headers": {"Content-Type": "application/json"},
"body": common_body,
"calculator_name": "AllPlanetStrength",
},
"ashtakavarga_sav": {
"section": "ashtakavarga_sav",
"endpoint_path": "/Calculate/AshtakvargaLifeMap",
"method": "POST",
"headers": {"Content-Type": "application/json"},
"body": {"birthTime": common_body["time"], "Ayanamsa": common_body["Ayanamsa"]},
"calculator_name": "AshtakvargaLifeMap",
},
"ashtakavarga_bav": {
"section": "ashtakavarga_bav",
"endpoint_path": "/Calculate/BhinnashtakavargaChart",
"method": "POST",
"headers": {"Content-Type": "application/json"},
"body": {"birthTime": common_body["time"], "Ayanamsa": common_body["Ayanamsa"]},
"calculator_name": "BhinnashtakavargaChart",
},
"ashtakavarga_sav_chart": {
"section": "ashtakavarga_sav_chart",
"endpoint_path": "/Calculate/SarvashtakavargaChart",
"method": "POST",
"headers": {"Content-Type": "application/json"},
"body": {"birthTime": common_body["time"], "Ayanamsa": common_body["Ayanamsa"]},
"calculator_name": "SarvashtakavargaChart",
},
}
for section, request_item in scalar_requests.items():
try:
payload, attempts, retries = adapter._post_official_snapshot_section(endpoint, request_item)
scalar_responses[section] = payload
scalar_statuses[section] = adapter._payload_status(payload)
attempt_count += attempts
retry_error_codes.extend(retries)
except Exception as exc:
scalar_statuses[section] = f"{type(exc).__name__}:{str(exc)[:200]}"
for planet in planets:
key = f"{planet}.chesta"
request_item = {
"section": "shadbala_chesta",
"endpoint_path": "/Calculate/PlanetChestaBala",
"method": "POST",
"headers": {"Content-Type": "application/json"},
"body": {
"planetName": {"Name": planet},
"time": common_body["time"],
"useSpecialSunMoon": False,
"Ayanamsa": common_body["Ayanamsa"],
},
"calculator_name": "PlanetChestaBala",
}
try:
payload, attempts, retries = adapter._post_official_snapshot_section(endpoint, request_item)
component_responses[key] = payload
component_statuses[key] = adapter._payload_status(payload)
attempt_count += attempts
retry_error_codes.extend(retries)
except Exception as exc:
component_statuses[key] = f"{type(exc).__name__}:{str(exc)[:200]}"
settings = {
"case_id": case_id,
"birth": {key: case.get(key) for key in ("year", "month", "day", "hour", "minute", "second", "lat", "lon", "tz")},
"ayanamsa_policy": case.get("ayanamsa_policy") or case.get("ayanamsa") or "lahiri",
"node_policy": case.get("node_policy") or case.get("node_mode") or "mean",
"endpoint_host": adapter._endpoint_host(endpoint),
"calculator": "AllPlanetData",
"planets": planets,
}
packet = {
"scope": "vedastro_official_http_divisional_parity_raw",
"status": "ok"
if statuses and scalar_statuses and component_statuses and all(value == "ok" for value in [*statuses.values(), *scalar_statuses.values(), *component_statuses.values()])
else "partial",
"source": "vedastro_official_http",
"settings": settings,
"coverage": {
"sections": [
*VARGA_FIELDS,
"shadbala_total",
"shadbala_components",
"ashtakavarga_bav",
"ashtakavarga_sav",
],
"field_map": VARGA_FIELDS,
"shadbala_component_fields": SHADBALA_COMPONENT_FIELDS,
"blocked_sections": [],
},
"fanout_statuses": dict(sorted(statuses.items())),
"scalar_statuses": dict(sorted(scalar_statuses.items())),
"component_statuses": dict(sorted(component_statuses.items())),
"attempt_count": attempt_count,
"retry_error_codes": retry_error_codes,
"raw_responses": raw_responses,
"scalar_responses": scalar_responses,
"component_responses": component_responses,
}
packet["response_hash"] = _hash({"chart_core": raw_responses, "scalar": scalar_responses, "components": component_responses})
output_path.parent.mkdir(parents=True, exist_ok=True)
output_path.write_text(json.dumps(packet, ensure_ascii=False, indent=2, sort_keys=True), encoding="utf-8")
return packet
def main() -> int:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("--case", choices=sorted(adapter.PARITY_CASES), default="beijing_first_use_demo")
parser.add_argument("--planets", default=",".join(DEFAULT_PLANETS))
parser.add_argument("--output", type=Path)
args = parser.parse_args()
planets = [item.strip() for item in args.planets.split(",") if item.strip()]
output = args.output or ROOT / "references" / "oracle" / "artifacts" / f"vedastro_{args.case}_divisional_raw.json"
report = collect_chart_core(
dict(adapter.PARITY_CASES[args.case]),
case_id=args.case,
planets=planets,
output_path=output,
)
print(json.dumps({"status": report["status"], "output": str(output), "response_hash": report["response_hash"], "fanout_statuses": report["fanout_statuses"]}, ensure_ascii=False, indent=2, sort_keys=True))
return 0 if report["status"] == "ok" else 1
if __name__ == "__main__":
raise SystemExit(main())