|
@@ -876,7 +876,7 @@ async def person_manage(
|
|
|
person_id: Required for get, update, delete.
|
|
person_id: Required for get, update, delete.
|
|
|
name: Person's full name (required for add).
|
|
name: Person's full name (required for add).
|
|
|
nickname: Optional short name for quick lookup (used with _byId tools).
|
|
nickname: Optional short name for quick lookup (used with _byId tools).
|
|
|
- birth_datetime: ISO 8601 UTC datetime (required for add).
|
|
|
|
|
|
|
+ birth_datetime: ISO 8601 naive local time, no offset (e.g. "1990-05-15T10:30:00").
|
|
|
birthplace: Optional birth place name (e.g., "Zurich, Switzerland").
|
|
birthplace: Optional birth place name (e.g., "Zurich, Switzerland").
|
|
|
latitude: Birth latitude (required for add).
|
|
latitude: Birth latitude (required for add).
|
|
|
longitude: Birth longitude (required for add).
|
|
longitude: Birth longitude (required for add).
|
|
@@ -1586,8 +1586,8 @@ async def _get_person_birth_data(person_id: str) -> dict[str, Any]:
|
|
|
produce the correct UTC datetime, using zoneinfo for modern dates and
|
|
produce the correct UTC datetime, using zoneinfo for modern dates and
|
|
|
LMT (longitude/15) as fallback when no timezone is set.
|
|
LMT (longitude/15) as fallback when no timezone is set.
|
|
|
|
|
|
|
|
- Rule: birth_datetime in the returned dict is ALWAYS UTC. Callers must not
|
|
|
|
|
- re-normalize it.
|
|
|
|
|
|
|
+ Rule: birth_datetime in the returned dict is ALWAYS naive local time.
|
|
|
|
|
+ birth_datetime_utc is for internal ephemeris calls only.
|
|
|
"""
|
|
"""
|
|
|
from . import storage
|
|
from . import storage
|
|
|
from .ephemeris_client import _normalize_datetime
|
|
from .ephemeris_client import _normalize_datetime
|
|
@@ -1602,7 +1602,9 @@ async def _get_person_birth_data(person_id: str) -> dict[str, Any]:
|
|
|
lon=person["longitude"],
|
|
lon=person["longitude"],
|
|
|
)
|
|
)
|
|
|
return {
|
|
return {
|
|
|
- "birth_datetime": utc_dt,
|
|
|
|
|
|
|
+ "birth_datetime": person["birth_datetime"], # naive local time
|
|
|
|
|
+ "timezone": person.get("timezone"),
|
|
|
|
|
+ "birth_datetime_utc": utc_dt, # for ephemeris calls
|
|
|
"birthplace": person.get("birthplace"),
|
|
"birthplace": person.get("birthplace"),
|
|
|
"latitude": person["latitude"],
|
|
"latitude": person["latitude"],
|
|
|
"longitude": person["longitude"],
|
|
"longitude": person["longitude"],
|
|
@@ -1641,8 +1643,8 @@ async def calculate_natal_chart_by_id(
|
|
|
birth = await _get_person_birth_data(person_id)
|
|
birth = await _get_person_birth_data(person_id)
|
|
|
if "error" in birth:
|
|
if "error" in birth:
|
|
|
return birth
|
|
return birth
|
|
|
- return await calculate_natal_chart(
|
|
|
|
|
- birth_datetime=birth["birth_datetime"],
|
|
|
|
|
|
|
+ result = await calculate_natal_chart(
|
|
|
|
|
+ birth_datetime=birth["birth_datetime_utc"],
|
|
|
latitude=birth["latitude"],
|
|
latitude=birth["latitude"],
|
|
|
longitude=birth["longitude"],
|
|
longitude=birth["longitude"],
|
|
|
elevation=birth.get("elevation", 0.0),
|
|
elevation=birth.get("elevation", 0.0),
|
|
@@ -1653,6 +1655,10 @@ async def calculate_natal_chart_by_id(
|
|
|
include_karmic=include_karmic,
|
|
include_karmic=include_karmic,
|
|
|
top_n_aspects=top_n_aspects,
|
|
top_n_aspects=top_n_aspects,
|
|
|
)
|
|
)
|
|
|
|
|
+ if "error" not in result:
|
|
|
|
|
+ result["input"]["birth_datetime"] = birth["birth_datetime"]
|
|
|
|
|
+ result["input"]["timezone"] = birth.get("timezone")
|
|
|
|
|
+ return result
|
|
|
|
|
|
|
|
|
|
|
|
|
@mcp.tool()
|
|
@mcp.tool()
|
|
@@ -1683,8 +1689,8 @@ Returns:
|
|
|
birth = await _get_person_birth_data(person_id)
|
|
birth = await _get_person_birth_data(person_id)
|
|
|
if "error" in birth:
|
|
if "error" in birth:
|
|
|
return birth
|
|
return birth
|
|
|
- return await calculate_transit_chart(
|
|
|
|
|
- birth_datetime=birth["birth_datetime"],
|
|
|
|
|
|
|
+ result = await calculate_transit_chart(
|
|
|
|
|
+ birth_datetime=birth["birth_datetime_utc"],
|
|
|
transit_datetime=transit_datetime,
|
|
transit_datetime=transit_datetime,
|
|
|
latitude=birth["latitude"],
|
|
latitude=birth["latitude"],
|
|
|
longitude=birth["longitude"],
|
|
longitude=birth["longitude"],
|
|
@@ -1694,6 +1700,10 @@ Returns:
|
|
|
house_system=house_system,
|
|
house_system=house_system,
|
|
|
orb_limits=orb_limits,
|
|
orb_limits=orb_limits,
|
|
|
)
|
|
)
|
|
|
|
|
+ if "error" not in result:
|
|
|
|
|
+ result["input"]["birth_datetime"] = birth["birth_datetime"]
|
|
|
|
|
+ result["input"]["timezone"] = birth.get("timezone")
|
|
|
|
|
+ return result
|
|
|
|
|
|
|
|
|
|
|
|
|
@mcp.tool()
|
|
@mcp.tool()
|
|
@@ -1732,11 +1742,11 @@ async def calculate_synastry_chart_by_id(
|
|
|
p2 = await _get_person_birth_data(person2_id)
|
|
p2 = await _get_person_birth_data(person2_id)
|
|
|
if "error" in p2:
|
|
if "error" in p2:
|
|
|
return p2
|
|
return p2
|
|
|
- return await calculate_synastry_chart(
|
|
|
|
|
- person1_datetime=p1["birth_datetime"],
|
|
|
|
|
|
|
+ result = await calculate_synastry_chart(
|
|
|
|
|
+ person1_datetime=p1["birth_datetime_utc"],
|
|
|
person1_latitude=p1["latitude"],
|
|
person1_latitude=p1["latitude"],
|
|
|
person1_longitude=p1["longitude"],
|
|
person1_longitude=p1["longitude"],
|
|
|
- person2_datetime=p2["birth_datetime"],
|
|
|
|
|
|
|
+ person2_datetime=p2["birth_datetime_utc"],
|
|
|
person2_latitude=p2["latitude"],
|
|
person2_latitude=p2["latitude"],
|
|
|
person2_longitude=p2["longitude"],
|
|
person2_longitude=p2["longitude"],
|
|
|
elevation=p1.get("elevation", 0.0),
|
|
elevation=p1.get("elevation", 0.0),
|
|
@@ -1747,6 +1757,12 @@ async def calculate_synastry_chart_by_id(
|
|
|
significator_filter=significator_filter,
|
|
significator_filter=significator_filter,
|
|
|
include_davison_full=include_davison_full,
|
|
include_davison_full=include_davison_full,
|
|
|
)
|
|
)
|
|
|
|
|
+ if "error" not in result:
|
|
|
|
|
+ result["input"]["person1_birth_datetime"] = p1["birth_datetime"]
|
|
|
|
|
+ result["input"]["person1_timezone"] = p1.get("timezone")
|
|
|
|
|
+ result["input"]["person2_birth_datetime"] = p2["birth_datetime"]
|
|
|
|
|
+ result["input"]["person2_timezone"] = p2.get("timezone")
|
|
|
|
|
+ return result
|
|
|
|
|
|
|
|
|
|
|
|
|
@mcp.tool()
|
|
@mcp.tool()
|
|
@@ -1776,17 +1792,23 @@ Returns:
|
|
|
p2 = await _get_person_birth_data(person2_id)
|
|
p2 = await _get_person_birth_data(person2_id)
|
|
|
if "error" in p2:
|
|
if "error" in p2:
|
|
|
return p2
|
|
return p2
|
|
|
- return await calculate_composite_chart(
|
|
|
|
|
- person1_datetime=p1["birth_datetime"],
|
|
|
|
|
|
|
+ result = await calculate_composite_chart(
|
|
|
|
|
+ person1_datetime=p1["birth_datetime_utc"],
|
|
|
person1_latitude=p1["latitude"],
|
|
person1_latitude=p1["latitude"],
|
|
|
person1_longitude=p1["longitude"],
|
|
person1_longitude=p1["longitude"],
|
|
|
- person2_datetime=p2["birth_datetime"],
|
|
|
|
|
|
|
+ person2_datetime=p2["birth_datetime_utc"],
|
|
|
person2_latitude=p2["latitude"],
|
|
person2_latitude=p2["latitude"],
|
|
|
person2_longitude=p2["longitude"],
|
|
person2_longitude=p2["longitude"],
|
|
|
elevation=p1.get("elevation", 0.0),
|
|
elevation=p1.get("elevation", 0.0),
|
|
|
house_system=house_system,
|
|
house_system=house_system,
|
|
|
orb_limits=orb_limits,
|
|
orb_limits=orb_limits,
|
|
|
)
|
|
)
|
|
|
|
|
+ if "error" not in result:
|
|
|
|
|
+ result["input"]["person1_birth_datetime"] = p1["birth_datetime"]
|
|
|
|
|
+ result["input"]["person1_timezone"] = p1.get("timezone")
|
|
|
|
|
+ result["input"]["person2_birth_datetime"] = p2["birth_datetime"]
|
|
|
|
|
+ result["input"]["person2_timezone"] = p2.get("timezone")
|
|
|
|
|
+ return result
|
|
|
|
|
|
|
|
|
|
|
|
|
@mcp.tool()
|
|
@mcp.tool()
|
|
@@ -1816,17 +1838,23 @@ Returns:
|
|
|
p2 = await _get_person_birth_data(person2_id)
|
|
p2 = await _get_person_birth_data(person2_id)
|
|
|
if "error" in p2:
|
|
if "error" in p2:
|
|
|
return p2
|
|
return p2
|
|
|
- return await calculate_davison_chart(
|
|
|
|
|
- person1_datetime=p1["birth_datetime"],
|
|
|
|
|
|
|
+ result = await calculate_davison_chart(
|
|
|
|
|
+ person1_datetime=p1["birth_datetime_utc"],
|
|
|
person1_latitude=p1["latitude"],
|
|
person1_latitude=p1["latitude"],
|
|
|
person1_longitude=p1["longitude"],
|
|
person1_longitude=p1["longitude"],
|
|
|
- person2_datetime=p2["birth_datetime"],
|
|
|
|
|
|
|
+ person2_datetime=p2["birth_datetime_utc"],
|
|
|
person2_latitude=p2["latitude"],
|
|
person2_latitude=p2["latitude"],
|
|
|
person2_longitude=p2["longitude"],
|
|
person2_longitude=p2["longitude"],
|
|
|
elevation=p1.get("elevation", 0.0),
|
|
elevation=p1.get("elevation", 0.0),
|
|
|
house_system=house_system,
|
|
house_system=house_system,
|
|
|
orb_limits=orb_limits,
|
|
orb_limits=orb_limits,
|
|
|
)
|
|
)
|
|
|
|
|
+ if "error" not in result:
|
|
|
|
|
+ result["input"]["person1_birth_datetime"] = p1["birth_datetime"]
|
|
|
|
|
+ result["input"]["person1_timezone"] = p1.get("timezone")
|
|
|
|
|
+ result["input"]["person2_birth_datetime"] = p2["birth_datetime"]
|
|
|
|
|
+ result["input"]["person2_timezone"] = p2.get("timezone")
|
|
|
|
|
+ return result
|
|
|
|
|
|
|
|
|
|
|
|
|
@mcp.tool()
|
|
@mcp.tool()
|
|
@@ -1857,8 +1885,8 @@ Returns:
|
|
|
birth = await _get_person_birth_data(person_id)
|
|
birth = await _get_person_birth_data(person_id)
|
|
|
if "error" in birth:
|
|
if "error" in birth:
|
|
|
return birth
|
|
return birth
|
|
|
- return await get_transit_preview(
|
|
|
|
|
- birth_datetime=birth["birth_datetime"],
|
|
|
|
|
|
|
+ result = await get_transit_preview(
|
|
|
|
|
+ birth_datetime=birth["birth_datetime_utc"],
|
|
|
latitude=birth["latitude"],
|
|
latitude=birth["latitude"],
|
|
|
longitude=birth["longitude"],
|
|
longitude=birth["longitude"],
|
|
|
start_date=start_date,
|
|
start_date=start_date,
|
|
@@ -1867,6 +1895,10 @@ Returns:
|
|
|
transit_longitude=transit_longitude,
|
|
transit_longitude=transit_longitude,
|
|
|
min_significance=min_significance,
|
|
min_significance=min_significance,
|
|
|
)
|
|
)
|
|
|
|
|
+ if "error" not in result:
|
|
|
|
|
+ result["input"]["birth_datetime"] = birth["birth_datetime"]
|
|
|
|
|
+ result["input"]["timezone"] = birth.get("timezone")
|
|
|
|
|
+ return result
|
|
|
|
|
|
|
|
|
|
|
|
|
# ═══════════════════════════════════════════════════════════════════════
|
|
# ═══════════════════════════════════════════════════════════════════════
|
|
@@ -1916,12 +1948,12 @@ async def render_natal_chart(
|
|
|
include_houses: bool = False,
|
|
include_houses: bool = False,
|
|
|
title: str | None = None,
|
|
title: str | None = None,
|
|
|
subtitle: str | None = None,
|
|
subtitle: str | None = None,
|
|
|
|
|
+ format: str = "svg",
|
|
|
) -> dict[str, Any]:
|
|
) -> dict[str, Any]:
|
|
|
- """Render a natal chart wheel as SVG.
|
|
|
|
|
|
|
+ """Render a natal chart wheel.
|
|
|
|
|
|
|
|
Calculates planetary positions and renders a visual zodiac wheel with
|
|
Calculates planetary positions and renders a visual zodiac wheel with
|
|
|
- planets, houses, and aspect lines. The SVG can be displayed in a browser,
|
|
|
|
|
- converted to PDF/PNG, or attached to a chat.
|
|
|
|
|
|
|
+ planets, houses, and aspect lines. Output as SVG or raster image (PNG/JPG).
|
|
|
|
|
|
|
|
BIRTH DATA (required):
|
|
BIRTH DATA (required):
|
|
|
birth_datetime: ISO 8601 datetime with timezone (e.g. "1990-05-15T10:30:00+01:00").
|
|
birth_datetime: ISO 8601 datetime with timezone (e.g. "1990-05-15T10:30:00+01:00").
|
|
@@ -1944,9 +1976,10 @@ async def render_natal_chart(
|
|
|
include_houses: Include a house cusp table (requires table_position != "none").
|
|
include_houses: Include a house cusp table (requires table_position != "none").
|
|
|
title: Custom chart title. Auto-generated if not provided.
|
|
title: Custom chart title. Auto-generated if not provided.
|
|
|
subtitle: Custom subtitle. Auto-generated from birth data if not provided.
|
|
subtitle: Custom subtitle. Auto-generated from birth data if not provided.
|
|
|
|
|
+ format: Output format — "svg" (default), "png", or "jpg".
|
|
|
|
|
|
|
|
Returns:
|
|
Returns:
|
|
|
- Dict with "svg" (SVG string), "format" ("svg"), "width", "height",
|
|
|
|
|
|
|
+ Dict with "content", "format", "content_type", "width", "height",
|
|
|
and "included" (list of what's in the chart).
|
|
and "included" (list of what's in the chart).
|
|
|
"""
|
|
"""
|
|
|
from .chart_renderer import render_natal_wheel
|
|
from .chart_renderer import render_natal_wheel
|
|
@@ -1963,7 +1996,7 @@ async def render_natal_chart(
|
|
|
if "error" in chart_data:
|
|
if "error" in chart_data:
|
|
|
return chart_data
|
|
return chart_data
|
|
|
|
|
|
|
|
- svg = render_natal_wheel(
|
|
|
|
|
|
|
+ result = render_natal_wheel(
|
|
|
chart_data,
|
|
chart_data,
|
|
|
style=style,
|
|
style=style,
|
|
|
color_mode=color_mode,
|
|
color_mode=color_mode,
|
|
@@ -1973,14 +2006,10 @@ async def render_natal_chart(
|
|
|
include_houses=include_houses,
|
|
include_houses=include_houses,
|
|
|
title=title,
|
|
title=title,
|
|
|
subtitle=subtitle,
|
|
subtitle=subtitle,
|
|
|
|
|
+ format=format,
|
|
|
)
|
|
)
|
|
|
- return {
|
|
|
|
|
- "svg": svg,
|
|
|
|
|
- "format": "svg",
|
|
|
|
|
- "width": size,
|
|
|
|
|
- "height": size,
|
|
|
|
|
- "included": _included_list(table_position, include_planets, include_houses),
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ result["included"] = _included_list(table_position, include_planets, include_houses)
|
|
|
|
|
+ return result
|
|
|
|
|
|
|
|
|
|
|
|
|
# ── render_natal_chart_by_id ──────────────────────────────────────────
|
|
# ── render_natal_chart_by_id ──────────────────────────────────────────
|
|
@@ -2000,11 +2029,12 @@ async def render_natal_chart_by_id(
|
|
|
include_planets: bool = False,
|
|
include_planets: bool = False,
|
|
|
include_houses: bool = False,
|
|
include_houses: bool = False,
|
|
|
title: str | None = None,
|
|
title: str | None = None,
|
|
|
|
|
+ format: str = "svg",
|
|
|
) -> dict[str, Any]:
|
|
) -> dict[str, Any]:
|
|
|
"""Render a natal chart wheel for a person from the database.
|
|
"""Render a natal chart wheel for a person from the database.
|
|
|
|
|
|
|
|
Looks up birth data by person_id or nickname, calculates the chart,
|
|
Looks up birth data by person_id or nickname, calculates the chart,
|
|
|
- and renders it as an SVG wheel. Same rendering options as render_natal_chart.
|
|
|
|
|
|
|
+ and renders it as a wheel. Output as SVG or raster image (PNG/JPG).
|
|
|
|
|
|
|
|
PERSON LOOKUP (required):
|
|
PERSON LOOKUP (required):
|
|
|
person_id: ID or nickname of a person in the persons database.
|
|
person_id: ID or nickname of a person in the persons database.
|
|
@@ -2037,7 +2067,7 @@ async def render_natal_chart_by_id(
|
|
|
if "error" in chart_data:
|
|
if "error" in chart_data:
|
|
|
return chart_data
|
|
return chart_data
|
|
|
|
|
|
|
|
- svg = render_natal_wheel(
|
|
|
|
|
|
|
+ result = render_natal_wheel(
|
|
|
chart_data,
|
|
chart_data,
|
|
|
style=style,
|
|
style=style,
|
|
|
color_mode=color_mode,
|
|
color_mode=color_mode,
|
|
@@ -2046,14 +2076,10 @@ async def render_natal_chart_by_id(
|
|
|
include_planets=include_planets,
|
|
include_planets=include_planets,
|
|
|
include_houses=include_houses,
|
|
include_houses=include_houses,
|
|
|
title=title,
|
|
title=title,
|
|
|
|
|
+ format=format,
|
|
|
)
|
|
)
|
|
|
- return {
|
|
|
|
|
- "svg": svg,
|
|
|
|
|
- "format": "svg",
|
|
|
|
|
- "width": size,
|
|
|
|
|
- "height": size,
|
|
|
|
|
- "included": _included_list(table_position, include_planets, include_houses),
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ result["included"] = _included_list(table_position, include_planets, include_houses)
|
|
|
|
|
+ return result
|
|
|
|
|
|
|
|
|
|
|
|
|
# ── render_transit_chart ──────────────────────────────────────────────
|
|
# ── render_transit_chart ──────────────────────────────────────────────
|
|
@@ -2075,12 +2101,13 @@ async def render_transit_chart(
|
|
|
color_mode: str = "color",
|
|
color_mode: str = "color",
|
|
|
size: int = 600,
|
|
size: int = 600,
|
|
|
title: str | None = None,
|
|
title: str | None = None,
|
|
|
|
|
+ format: str = "svg",
|
|
|
) -> dict[str, Any]:
|
|
) -> dict[str, Any]:
|
|
|
"""Render a transit chart as a bi-wheel (natal inner, transit outer).
|
|
"""Render a transit chart as a bi-wheel (natal inner, transit outer).
|
|
|
|
|
|
|
|
Calculates natal and transiting planet positions, then renders a bi-wheel
|
|
Calculates natal and transiting planet positions, then renders a bi-wheel
|
|
|
showing natal planets inside and transiting planets outside, with
|
|
showing natal planets inside and transiting planets outside, with
|
|
|
- transit-to-natal aspect lines.
|
|
|
|
|
|
|
+ transit-to-natal aspect lines. Output as SVG or raster image (PNG/JPG).
|
|
|
|
|
|
|
|
BIRTH DATA (required):
|
|
BIRTH DATA (required):
|
|
|
birth_datetime: ISO 8601 birth datetime with timezone.
|
|
birth_datetime: ISO 8601 birth datetime with timezone.
|
|
@@ -2102,9 +2129,10 @@ async def render_transit_chart(
|
|
|
color_mode: {_RENDER_COLOR_HELP}
|
|
color_mode: {_RENDER_COLOR_HELP}
|
|
|
size: {_RENDER_SIZE_HELP}
|
|
size: {_RENDER_SIZE_HELP}
|
|
|
title: {_RENDER_TITLE_HELP}
|
|
title: {_RENDER_TITLE_HELP}
|
|
|
|
|
+ format: Output format — "svg" (default), "png", or "jpg".
|
|
|
|
|
|
|
|
Returns:
|
|
Returns:
|
|
|
- Dict with "svg" (SVG string), "format", "width", "height", "included".
|
|
|
|
|
|
|
+ Dict with "content", "format", "content_type", "width", "height", "included".
|
|
|
"""
|
|
"""
|
|
|
from .chart_renderer import render_transit_wheel
|
|
from .chart_renderer import render_transit_wheel
|
|
|
|
|
|
|
@@ -2122,20 +2150,16 @@ async def render_transit_chart(
|
|
|
if "error" in chart_data:
|
|
if "error" in chart_data:
|
|
|
return chart_data
|
|
return chart_data
|
|
|
|
|
|
|
|
- svg = render_transit_wheel(
|
|
|
|
|
|
|
+ result = render_transit_wheel(
|
|
|
chart_data,
|
|
chart_data,
|
|
|
style=style,
|
|
style=style,
|
|
|
color_mode=color_mode,
|
|
color_mode=color_mode,
|
|
|
size=size,
|
|
size=size,
|
|
|
title=title,
|
|
title=title,
|
|
|
|
|
+ format=format,
|
|
|
)
|
|
)
|
|
|
- return {
|
|
|
|
|
- "svg": svg,
|
|
|
|
|
- "format": "svg",
|
|
|
|
|
- "width": size,
|
|
|
|
|
- "height": size,
|
|
|
|
|
- "included": ["wheel"],
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ result["included"] = ["wheel"]
|
|
|
|
|
+ return result
|
|
|
|
|
|
|
|
|
|
|
|
|
# ── render_transit_chart_by_id ────────────────────────────────────────
|
|
# ── render_transit_chart_by_id ────────────────────────────────────────
|
|
@@ -2154,11 +2178,12 @@ async def render_transit_chart_by_id(
|
|
|
color_mode: str = "color",
|
|
color_mode: str = "color",
|
|
|
size: int = 600,
|
|
size: int = 600,
|
|
|
title: str | None = None,
|
|
title: str | None = None,
|
|
|
|
|
+ format: str = "svg",
|
|
|
) -> dict[str, Any]:
|
|
) -> dict[str, Any]:
|
|
|
"""Render a transit bi-wheel for a person from the database.
|
|
"""Render a transit bi-wheel for a person from the database.
|
|
|
|
|
|
|
|
Looks up birth data by person_id, calculates transits for the given
|
|
Looks up birth data by person_id, calculates transits for the given
|
|
|
- date, and renders a bi-wheel chart.
|
|
|
|
|
|
|
+ date, and renders a bi-wheel chart. Output as SVG or raster image (PNG/JPG).
|
|
|
|
|
|
|
|
PERSON LOOKUP (required):
|
|
PERSON LOOKUP (required):
|
|
|
person_id: ID or nickname of a person in the persons database.
|
|
person_id: ID or nickname of a person in the persons database.
|
|
@@ -2194,20 +2219,16 @@ async def render_transit_chart_by_id(
|
|
|
if "error" in chart_data:
|
|
if "error" in chart_data:
|
|
|
return chart_data
|
|
return chart_data
|
|
|
|
|
|
|
|
- svg = render_transit_wheel(
|
|
|
|
|
|
|
+ result = render_transit_wheel(
|
|
|
chart_data,
|
|
chart_data,
|
|
|
style=style,
|
|
style=style,
|
|
|
color_mode=color_mode,
|
|
color_mode=color_mode,
|
|
|
size=size,
|
|
size=size,
|
|
|
title=title,
|
|
title=title,
|
|
|
|
|
+ format=format,
|
|
|
)
|
|
)
|
|
|
- return {
|
|
|
|
|
- "svg": svg,
|
|
|
|
|
- "format": "svg",
|
|
|
|
|
- "width": size,
|
|
|
|
|
- "height": size,
|
|
|
|
|
- "included": ["wheel"],
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ result["included"] = ["wheel"]
|
|
|
|
|
+ return result
|
|
|
|
|
|
|
|
|
|
|
|
|
# ── render_synastry_chart ─────────────────────────────────────────────
|
|
# ── render_synastry_chart ─────────────────────────────────────────────
|
|
@@ -2230,11 +2251,12 @@ async def render_synastry_chart(
|
|
|
color_mode: str = "color",
|
|
color_mode: str = "color",
|
|
|
size: int = 800,
|
|
size: int = 800,
|
|
|
title: str | None = None,
|
|
title: str | None = None,
|
|
|
|
|
+ format: str = "svg",
|
|
|
) -> dict[str, Any]:
|
|
) -> dict[str, Any]:
|
|
|
"""Render a synastry (relationship) chart with two side-by-side wheels.
|
|
"""Render a synastry (relationship) chart with two side-by-side wheels.
|
|
|
|
|
|
|
|
Calculates both natal charts and renders them side by side with
|
|
Calculates both natal charts and renders them side by side with
|
|
|
- interaspect lines between the two charts.
|
|
|
|
|
|
|
+ interaspect lines between the two charts. Output as SVG or raster image (PNG/JPG).
|
|
|
|
|
|
|
|
PERSON 1 (required):
|
|
PERSON 1 (required):
|
|
|
person1_datetime: ISO 8601 birth datetime with timezone.
|
|
person1_datetime: ISO 8601 birth datetime with timezone.
|
|
@@ -2278,20 +2300,16 @@ async def render_synastry_chart(
|
|
|
if "error" in chart_data:
|
|
if "error" in chart_data:
|
|
|
return chart_data
|
|
return chart_data
|
|
|
|
|
|
|
|
- svg = render_synastry_wheel(
|
|
|
|
|
|
|
+ result = render_synastry_wheel(
|
|
|
chart_data,
|
|
chart_data,
|
|
|
style=style,
|
|
style=style,
|
|
|
color_mode=color_mode,
|
|
color_mode=color_mode,
|
|
|
size=size,
|
|
size=size,
|
|
|
title=title,
|
|
title=title,
|
|
|
|
|
+ format=format,
|
|
|
)
|
|
)
|
|
|
- return {
|
|
|
|
|
- "svg": svg,
|
|
|
|
|
- "format": "svg",
|
|
|
|
|
- "width": size,
|
|
|
|
|
- "height": size,
|
|
|
|
|
- "included": ["wheel"],
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ result["included"] = ["wheel"]
|
|
|
|
|
+ return result
|
|
|
|
|
|
|
|
|
|
|
|
|
# ── render_synastry_chart_by_id ───────────────────────────────────────
|
|
# ── render_synastry_chart_by_id ───────────────────────────────────────
|
|
@@ -2309,11 +2327,13 @@ async def render_synastry_chart_by_id(
|
|
|
color_mode: str = "color",
|
|
color_mode: str = "color",
|
|
|
size: int = 800,
|
|
size: int = 800,
|
|
|
title: str | None = None,
|
|
title: str | None = None,
|
|
|
|
|
+ format: str = "svg",
|
|
|
) -> dict[str, Any]:
|
|
) -> dict[str, Any]:
|
|
|
"""Render a synastry chart for two people from the database.
|
|
"""Render a synastry chart for two people from the database.
|
|
|
|
|
|
|
|
Looks up both persons by ID or nickname, calculates their synastry,
|
|
Looks up both persons by ID or nickname, calculates their synastry,
|
|
|
and renders side-by-side natal wheels with interaspect lines.
|
|
and renders side-by-side natal wheels with interaspect lines.
|
|
|
|
|
+ Output as SVG or raster image (PNG/JPG).
|
|
|
|
|
|
|
|
PERSON LOOKUP (required):
|
|
PERSON LOOKUP (required):
|
|
|
person1_id: ID or nickname of person 1 in the persons database.
|
|
person1_id: ID or nickname of person 1 in the persons database.
|
|
@@ -2345,20 +2365,16 @@ async def render_synastry_chart_by_id(
|
|
|
if "error" in chart_data:
|
|
if "error" in chart_data:
|
|
|
return chart_data
|
|
return chart_data
|
|
|
|
|
|
|
|
- svg = render_synastry_wheel(
|
|
|
|
|
|
|
+ result = render_synastry_wheel(
|
|
|
chart_data,
|
|
chart_data,
|
|
|
style=style,
|
|
style=style,
|
|
|
color_mode=color_mode,
|
|
color_mode=color_mode,
|
|
|
size=size,
|
|
size=size,
|
|
|
title=title,
|
|
title=title,
|
|
|
|
|
+ format=format,
|
|
|
)
|
|
)
|
|
|
- return {
|
|
|
|
|
- "svg": svg,
|
|
|
|
|
- "format": "svg",
|
|
|
|
|
- "width": size,
|
|
|
|
|
- "height": size,
|
|
|
|
|
- "included": ["wheel"],
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ result["included"] = ["wheel"]
|
|
|
|
|
+ return result
|
|
|
|
|
|
|
|
|
|
|
|
|
# ── render_composite_chart ────────────────────────────────────────────
|
|
# ── render_composite_chart ────────────────────────────────────────────
|
|
@@ -2383,11 +2399,13 @@ async def render_composite_chart(
|
|
|
include_planets: bool = False,
|
|
include_planets: bool = False,
|
|
|
include_houses: bool = False,
|
|
include_houses: bool = False,
|
|
|
title: str | None = None,
|
|
title: str | None = None,
|
|
|
|
|
+ format: str = "svg",
|
|
|
) -> dict[str, Any]:
|
|
) -> dict[str, Any]:
|
|
|
"""Render a composite chart (midpoint method) as a single wheel.
|
|
"""Render a composite chart (midpoint method) as a single wheel.
|
|
|
|
|
|
|
|
Calculates the composite chart from two people's birth data and renders
|
|
Calculates the composite chart from two people's birth data and renders
|
|
|
it as a standard natal-style wheel representing the relationship.
|
|
it as a standard natal-style wheel representing the relationship.
|
|
|
|
|
+ Output as SVG or raster image (PNG/JPG).
|
|
|
|
|
|
|
|
PERSON 1 (required):
|
|
PERSON 1 (required):
|
|
|
person1_datetime: ISO 8601 birth datetime with timezone.
|
|
person1_datetime: ISO 8601 birth datetime with timezone.
|
|
@@ -2432,7 +2450,7 @@ async def render_composite_chart(
|
|
|
if "error" in chart_data:
|
|
if "error" in chart_data:
|
|
|
return chart_data
|
|
return chart_data
|
|
|
|
|
|
|
|
- svg = render_natal_wheel(
|
|
|
|
|
|
|
+ result = render_natal_wheel(
|
|
|
chart_data,
|
|
chart_data,
|
|
|
style=style,
|
|
style=style,
|
|
|
color_mode=color_mode,
|
|
color_mode=color_mode,
|
|
@@ -2441,14 +2459,10 @@ async def render_composite_chart(
|
|
|
include_planets=include_planets,
|
|
include_planets=include_planets,
|
|
|
include_houses=include_houses,
|
|
include_houses=include_houses,
|
|
|
title=title,
|
|
title=title,
|
|
|
|
|
+ format=format,
|
|
|
)
|
|
)
|
|
|
- return {
|
|
|
|
|
- "svg": svg,
|
|
|
|
|
- "format": "svg",
|
|
|
|
|
- "width": size,
|
|
|
|
|
- "height": size,
|
|
|
|
|
- "included": _included_list(table_position, include_planets, include_houses),
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ result["included"] = _included_list(table_position, include_planets, include_houses)
|
|
|
|
|
+ return result
|
|
|
|
|
|
|
|
|
|
|
|
|
# ── render_composite_chart_by_id ──────────────────────────────────────
|
|
# ── render_composite_chart_by_id ──────────────────────────────────────
|
|
@@ -2468,11 +2482,12 @@ async def render_composite_chart_by_id(
|
|
|
include_planets: bool = False,
|
|
include_planets: bool = False,
|
|
|
include_houses: bool = False,
|
|
include_houses: bool = False,
|
|
|
title: str | None = None,
|
|
title: str | None = None,
|
|
|
|
|
+ format: str = "svg",
|
|
|
) -> dict[str, Any]:
|
|
) -> dict[str, Any]:
|
|
|
"""Render a composite chart for two people from the database.
|
|
"""Render a composite chart for two people from the database.
|
|
|
|
|
|
|
|
Looks up both persons by ID, calculates the composite chart, and
|
|
Looks up both persons by ID, calculates the composite chart, and
|
|
|
- renders it as a single natal-style wheel.
|
|
|
|
|
|
|
+ renders it as a single natal-style wheel. Output as SVG or raster image (PNG/JPG).
|
|
|
|
|
|
|
|
PERSON LOOKUP (required):
|
|
PERSON LOOKUP (required):
|
|
|
person1_id: ID or nickname of person 1 in the persons database.
|
|
person1_id: ID or nickname of person 1 in the persons database.
|
|
@@ -2490,9 +2505,10 @@ async def render_composite_chart_by_id(
|
|
|
include_planets: {_RENDER_PLANETS_HELP}
|
|
include_planets: {_RENDER_PLANETS_HELP}
|
|
|
include_houses: {_RENDER_HOUSES_HELP}
|
|
include_houses: {_RENDER_HOUSES_HELP}
|
|
|
title: {_RENDER_TITLE_HELP}
|
|
title: {_RENDER_TITLE_HELP}
|
|
|
|
|
+ format: Output format — "svg" (default), "png", or "jpg".
|
|
|
|
|
|
|
|
Returns:
|
|
Returns:
|
|
|
- Dict with "svg", "format", "width", "height", "included".
|
|
|
|
|
|
|
+ Dict with "content", "format", "content_type", "width", "height", "included".
|
|
|
"""
|
|
"""
|
|
|
from .chart_renderer import render_natal_wheel
|
|
from .chart_renderer import render_natal_wheel
|
|
|
|
|
|
|
@@ -2505,7 +2521,7 @@ async def render_composite_chart_by_id(
|
|
|
if "error" in chart_data:
|
|
if "error" in chart_data:
|
|
|
return chart_data
|
|
return chart_data
|
|
|
|
|
|
|
|
- svg = render_natal_wheel(
|
|
|
|
|
|
|
+ result = render_natal_wheel(
|
|
|
chart_data,
|
|
chart_data,
|
|
|
style=style,
|
|
style=style,
|
|
|
color_mode=color_mode,
|
|
color_mode=color_mode,
|
|
@@ -2514,14 +2530,10 @@ async def render_composite_chart_by_id(
|
|
|
include_planets=include_planets,
|
|
include_planets=include_planets,
|
|
|
include_houses=include_houses,
|
|
include_houses=include_houses,
|
|
|
title=title,
|
|
title=title,
|
|
|
|
|
+ format=format,
|
|
|
)
|
|
)
|
|
|
- return {
|
|
|
|
|
- "svg": svg,
|
|
|
|
|
- "format": "svg",
|
|
|
|
|
- "width": size,
|
|
|
|
|
- "height": size,
|
|
|
|
|
- "included": _included_list(table_position, include_planets, include_houses),
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ result["included"] = _included_list(table_position, include_planets, include_houses)
|
|
|
|
|
+ return result
|
|
|
|
|
|
|
|
|
|
|
|
|
# ── render_davison_chart ──────────────────────────────────────────────
|
|
# ── render_davison_chart ──────────────────────────────────────────────
|
|
@@ -2546,11 +2558,12 @@ async def render_davison_chart(
|
|
|
include_planets: bool = False,
|
|
include_planets: bool = False,
|
|
|
include_houses: bool = False,
|
|
include_houses: bool = False,
|
|
|
title: str | None = None,
|
|
title: str | None = None,
|
|
|
|
|
+ format: str = "svg",
|
|
|
) -> dict[str, Any]:
|
|
) -> dict[str, Any]:
|
|
|
"""Render a Davison chart (midpoint in time and space) as a single wheel.
|
|
"""Render a Davison chart (midpoint in time and space) as a single wheel.
|
|
|
|
|
|
|
|
Calculates the Davison chart from two people's birth data and renders
|
|
Calculates the Davison chart from two people's birth data and renders
|
|
|
- it as a standard natal-style wheel.
|
|
|
|
|
|
|
+ it as a standard natal-style wheel. Output as SVG or raster image (PNG/JPG).
|
|
|
|
|
|
|
|
PERSON 1 (required):
|
|
PERSON 1 (required):
|
|
|
person1_datetime: ISO 8601 birth datetime with timezone.
|
|
person1_datetime: ISO 8601 birth datetime with timezone.
|
|
@@ -2595,7 +2608,7 @@ async def render_davison_chart(
|
|
|
if "error" in chart_data:
|
|
if "error" in chart_data:
|
|
|
return chart_data
|
|
return chart_data
|
|
|
|
|
|
|
|
- svg = render_natal_wheel(
|
|
|
|
|
|
|
+ result = render_natal_wheel(
|
|
|
chart_data,
|
|
chart_data,
|
|
|
style=style,
|
|
style=style,
|
|
|
color_mode=color_mode,
|
|
color_mode=color_mode,
|
|
@@ -2604,14 +2617,10 @@ async def render_davison_chart(
|
|
|
include_planets=include_planets,
|
|
include_planets=include_planets,
|
|
|
include_houses=include_houses,
|
|
include_houses=include_houses,
|
|
|
title=title,
|
|
title=title,
|
|
|
|
|
+ format=format,
|
|
|
)
|
|
)
|
|
|
- return {
|
|
|
|
|
- "svg": svg,
|
|
|
|
|
- "format": "svg",
|
|
|
|
|
- "width": size,
|
|
|
|
|
- "height": size,
|
|
|
|
|
- "included": _included_list(table_position, include_planets, include_houses),
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ result["included"] = _included_list(table_position, include_planets, include_houses)
|
|
|
|
|
+ return result
|
|
|
|
|
|
|
|
|
|
|
|
|
# ── render_davison_chart_by_id ────────────────────────────────────────
|
|
# ── render_davison_chart_by_id ────────────────────────────────────────
|
|
@@ -2631,11 +2640,12 @@ async def render_davison_chart_by_id(
|
|
|
include_planets: bool = False,
|
|
include_planets: bool = False,
|
|
|
include_houses: bool = False,
|
|
include_houses: bool = False,
|
|
|
title: str | None = None,
|
|
title: str | None = None,
|
|
|
|
|
+ format: str = "svg",
|
|
|
) -> dict[str, Any]:
|
|
) -> dict[str, Any]:
|
|
|
"""Render a Davison chart for two people from the database.
|
|
"""Render a Davison chart for two people from the database.
|
|
|
|
|
|
|
|
Looks up both persons by ID, calculates the Davison chart, and
|
|
Looks up both persons by ID, calculates the Davison chart, and
|
|
|
- renders it as a single natal-style wheel.
|
|
|
|
|
|
|
+ renders it as a single natal-style wheel. Output as SVG or raster image.
|
|
|
|
|
|
|
|
PERSON LOOKUP (required):
|
|
PERSON LOOKUP (required):
|
|
|
person1_id: ID or nickname of person 1 in the persons database.
|
|
person1_id: ID or nickname of person 1 in the persons database.
|
|
@@ -2668,7 +2678,7 @@ async def render_davison_chart_by_id(
|
|
|
if "error" in chart_data:
|
|
if "error" in chart_data:
|
|
|
return chart_data
|
|
return chart_data
|
|
|
|
|
|
|
|
- svg = render_natal_wheel(
|
|
|
|
|
|
|
+ result = render_natal_wheel(
|
|
|
chart_data,
|
|
chart_data,
|
|
|
style=style,
|
|
style=style,
|
|
|
color_mode=color_mode,
|
|
color_mode=color_mode,
|
|
@@ -2677,14 +2687,10 @@ async def render_davison_chart_by_id(
|
|
|
include_planets=include_planets,
|
|
include_planets=include_planets,
|
|
|
include_houses=include_houses,
|
|
include_houses=include_houses,
|
|
|
title=title,
|
|
title=title,
|
|
|
|
|
+ format=format,
|
|
|
)
|
|
)
|
|
|
- return {
|
|
|
|
|
- "svg": svg,
|
|
|
|
|
- "format": "svg",
|
|
|
|
|
- "width": size,
|
|
|
|
|
- "height": size,
|
|
|
|
|
- "included": _included_list(table_position, include_planets, include_houses),
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ result["included"] = _included_list(table_position, include_planets, include_houses)
|
|
|
|
|
+ return result
|
|
|
|
|
|
|
|
|
|
|
|
|
# ── Helper ────────────────────────────────────────────────────────────
|
|
# ── Helper ────────────────────────────────────────────────────────────
|