feat(reading): fill the five full-reading modules missing from upstream
The western comparison layer was invoked without its files, so it stayed blocked. Navamsha and Niryaana Shoola are filled from the native rashi dashas. 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
b3ef2d8158
commit
900581068e
@@ -153,6 +153,21 @@ def _birth_zone(value: str | float | int):
|
||||
return fixed_timezone(timedelta(hours=offset)), f"UTC{offset:+g}"
|
||||
|
||||
|
||||
def _birth_times(
|
||||
*,
|
||||
year: int,
|
||||
month: int,
|
||||
day: int,
|
||||
hour: int,
|
||||
minute: int,
|
||||
second: int,
|
||||
timezone: str | float | int,
|
||||
) -> tuple[datetime, datetime, str]:
|
||||
zone, timezone_label = _birth_zone(timezone)
|
||||
local = datetime(year, month, day, hour, minute, second, tzinfo=zone)
|
||||
utc = local.astimezone(ZoneInfo("UTC"))
|
||||
return local, utc, timezone_label
|
||||
|
||||
def build_tropical_natal_chart(
|
||||
*,
|
||||
year: int,
|
||||
@@ -217,6 +232,100 @@ def build_tropical_natal_chart(
|
||||
}
|
||||
|
||||
|
||||
def build_draconic_chart(**birth: Any) -> dict[str, Any]:
|
||||
"""Derive a draconic chart by rotating tropical longitudes to the true node Aries point."""
|
||||
natal_chart = build_tropical_natal_chart(**birth)
|
||||
natal = natal_chart["natal"]
|
||||
node_longitude = natal["planets"]["true_node"]["longitude"]
|
||||
|
||||
def _rotate_point(point: dict[str, Any]) -> dict[str, Any]:
|
||||
rotated = _point(point["longitude"] - node_longitude)
|
||||
if "house" in point:
|
||||
rotated["house"] = point["house"]
|
||||
if "speed_longitude" in point:
|
||||
rotated["speed_longitude"] = point["speed_longitude"]
|
||||
rotated["retrograde"] = point.get("retrograde", False)
|
||||
return rotated
|
||||
|
||||
rotated_planets = {name: _rotate_point(point) for name, point in natal["planets"].items()}
|
||||
rotated_angles = {name: _rotate_point(point) for name, point in natal["angles"].items()}
|
||||
rotated_houses = [
|
||||
{"house": row["house"], "cusp": _rotate_point(row["cusp"])}
|
||||
for row in natal["houses"]
|
||||
]
|
||||
aspect_points = {**rotated_planets, "ascendant": rotated_angles["ascendant"], "mc": rotated_angles["mc"]}
|
||||
return {
|
||||
"technique": "draconic_chart",
|
||||
"status": "used",
|
||||
"source_engine": natal_chart["source_engine"],
|
||||
"base_zodiac": natal_chart["zodiac"],
|
||||
"derived_zodiac": "draconic",
|
||||
"node_basis": "true_node",
|
||||
"node_longitude": node_longitude,
|
||||
"draconic": {
|
||||
"ascendant": rotated_angles["ascendant"],
|
||||
"mc": rotated_angles["mc"],
|
||||
"angles": rotated_angles,
|
||||
"planets": rotated_planets,
|
||||
"houses": rotated_houses,
|
||||
"aspects": _aspects(aspect_points),
|
||||
"distribution": _distribution(rotated_planets),
|
||||
"house_ruler_chains": _ruler_chains(
|
||||
[row["cusp"]["longitude"] for row in rotated_houses],
|
||||
rotated_planets,
|
||||
),
|
||||
},
|
||||
"boundary": "Longitude rotation to the true-node Aries point only; interpretation and timing remain separate audited layers.",
|
||||
}
|
||||
|
||||
def build_relocation_chart(
|
||||
*,
|
||||
relocation_latitude: float,
|
||||
relocation_longitude: float,
|
||||
relocation_timezone: str | float | int,
|
||||
house_system: str = "P",
|
||||
**birth: Any,
|
||||
) -> dict[str, Any]:
|
||||
"""Calculate a relocated chart for the same UTC birth instant at a new location."""
|
||||
local, utc, _ = _birth_times(
|
||||
year=int(birth["year"]),
|
||||
month=int(birth["month"]),
|
||||
day=int(birth["day"]),
|
||||
hour=int(birth["hour"]),
|
||||
minute=int(birth["minute"]),
|
||||
second=int(birth.get("second", 0)),
|
||||
timezone=birth["timezone"],
|
||||
)
|
||||
relocation_zone, relocation_label = _birth_zone(relocation_timezone)
|
||||
relocation_local = utc.astimezone(relocation_zone)
|
||||
relocated_chart = build_tropical_natal_chart(
|
||||
year=relocation_local.year,
|
||||
month=relocation_local.month,
|
||||
day=relocation_local.day,
|
||||
hour=relocation_local.hour,
|
||||
minute=relocation_local.minute,
|
||||
second=relocation_local.second,
|
||||
latitude=float(relocation_latitude),
|
||||
longitude=float(relocation_longitude),
|
||||
timezone=relocation_timezone,
|
||||
house_system=house_system,
|
||||
)
|
||||
return {
|
||||
"technique": "relocation_chart",
|
||||
"status": "used",
|
||||
"source_engine": relocated_chart["source_engine"],
|
||||
"same_birth_utc": utc.isoformat(),
|
||||
"source_local_time": local.isoformat(),
|
||||
"relocation_local_time": relocation_local.isoformat(),
|
||||
"relocation_timezone": relocation_label,
|
||||
"relocation_coordinates": {
|
||||
"latitude": float(relocation_latitude),
|
||||
"longitude": float(relocation_longitude),
|
||||
},
|
||||
"relocated_chart": relocated_chart,
|
||||
"boundary": "Same UTC birth instant with relocated coordinates and timezone; interpretation and relocation outcome judgment remain separate audited layers.",
|
||||
}
|
||||
|
||||
def _request_field(body: dict[str, Any], *keys: str) -> Any:
|
||||
for key in keys:
|
||||
if key in body and body[key] not in (None, ""):
|
||||
|
||||
Reference in New Issue
Block a user