|
@@ -1,836 +0,0 @@
|
|
|
-"""
|
|
|
|
|
-Chart rendering tools for astro-mcp.
|
|
|
|
|
-
|
|
|
|
|
-Render visual chart wheels (SVG/PNG/JPG) from calculated chart data.
|
|
|
|
|
-Combines calculation + rendering in one step.
|
|
|
|
|
-"""
|
|
|
|
|
-
|
|
|
|
|
-from __future__ import annotations
|
|
|
|
|
-
|
|
|
|
|
-from typing import Any
|
|
|
|
|
-
|
|
|
|
|
-from .server import mcp
|
|
|
|
|
-from .chart_renderer import (
|
|
|
|
|
- render_natal_wheel,
|
|
|
|
|
- render_transit_wheel,
|
|
|
|
|
- render_synastry_wheel,
|
|
|
|
|
-)
|
|
|
|
|
-from .chart_tools import (
|
|
|
|
|
- calculate_natal_chart,
|
|
|
|
|
- calculate_transit_chart,
|
|
|
|
|
- calculate_synastry_chart,
|
|
|
|
|
- calculate_composite_chart,
|
|
|
|
|
- calculate_davison_chart,
|
|
|
|
|
-)
|
|
|
|
|
-from .by_id_tools import (
|
|
|
|
|
- calculate_natal_chart_by_id,
|
|
|
|
|
- calculate_transit_chart_by_id,
|
|
|
|
|
- calculate_synastry_chart_by_id,
|
|
|
|
|
- calculate_composite_chart_by_id,
|
|
|
|
|
- calculate_davison_chart_by_id,
|
|
|
|
|
-)
|
|
|
|
|
-
|
|
|
|
|
-# ═══════════════════════════════════════════════════════════════════════
|
|
|
|
|
-# CHART RENDERING TOOLS
|
|
|
|
|
-# ═══════════════════════════════════════════════════════════════════════
|
|
|
|
|
-# These tools render visual chart wheels from birth data.
|
|
|
|
|
-# They combine calculation + rendering in one step.
|
|
|
|
|
-# For data-only output, use the calculate_* tools instead.
|
|
|
|
|
-# ═══════════════════════════════════════════════════════════════════════
|
|
|
|
|
-
|
|
|
|
|
-# ── Shared render options (used by all render_* tools) ────────────────
|
|
|
|
|
-
|
|
|
|
|
-_RENDER_STYLE_HELP = (
|
|
|
|
|
- "Chart visual style: 'modern' (clean, minimal), 'traditional' "
|
|
|
|
|
- "(ornate, classical), or 'minimal' (bare bones)."
|
|
|
|
|
-)
|
|
|
|
|
-_RENDER_COLOR_HELP = (
|
|
|
|
|
- "Color mode: 'color' (full color with element-themed zodiac ring), "
|
|
|
|
|
- "'bw' (black/white, aspect lines distinguished by style), or "
|
|
|
|
|
- "'dark' (dark background for web display)."
|
|
|
|
|
-)
|
|
|
|
|
-_RENDER_SIZE_HELP = "SVG width/height in pixels (default: 600)."
|
|
|
|
|
-_RENDER_TABLE_HELP = "Include an aspect table below the wheel."
|
|
|
|
|
-_RENDER_PLANETS_HELP = "Include a planet data table below the wheel."
|
|
|
|
|
-_RENDER_HOUSES_HELP = "Include a house cusp table below the wheel."
|
|
|
|
|
-_RENDER_TITLE_HELP = "Custom chart title. Auto-generated if not provided."
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
-# ── render_natal_chart ────────────────────────────────────────────────
|
|
|
|
|
-
|
|
|
|
|
-@mcp.tool()
|
|
|
|
|
-async def render_natal_chart(
|
|
|
|
|
- # ── Birth data (same as calculate_natal_chart) ──────────────────
|
|
|
|
|
- birth_datetime: str,
|
|
|
|
|
- latitude: float,
|
|
|
|
|
- longitude: float,
|
|
|
|
|
- elevation: float = 0.0,
|
|
|
|
|
- house_system: str = "placidus",
|
|
|
|
|
- orb_limits: dict[str, float] | None = None,
|
|
|
|
|
- top_n_aspects: int | None = None,
|
|
|
|
|
- # ── Rendering options ───────────────────────────────────────────
|
|
|
|
|
- style: str = "modern",
|
|
|
|
|
- color_mode: str = "color",
|
|
|
|
|
- size: int = 600,
|
|
|
|
|
- table_position: str = "none",
|
|
|
|
|
- include_planets: bool = False,
|
|
|
|
|
- include_houses: bool = False,
|
|
|
|
|
- title: str | None = None,
|
|
|
|
|
- subtitle: str | None = None,
|
|
|
|
|
- format: str = "svg",
|
|
|
|
|
-) -> dict[str, Any]:
|
|
|
|
|
- """Render a natal chart wheel.
|
|
|
|
|
-
|
|
|
|
|
- Calculates planetary positions and renders a visual zodiac wheel with
|
|
|
|
|
- planets, houses, and aspect lines. Output as SVG or raster image (PNG/JPG).
|
|
|
|
|
-
|
|
|
|
|
- BIRTH DATA (required):
|
|
|
|
|
- birth_datetime: ISO 8601 datetime with timezone (e.g. "1990-05-15T10:30:00+01:00").
|
|
|
|
|
- latitude: Birth latitude in decimal degrees (-90 to 90).
|
|
|
|
|
- longitude: Birth longitude in decimal degrees (-180 to 180).
|
|
|
|
|
-
|
|
|
|
|
- BIRTH DATA (optional):
|
|
|
|
|
- elevation: Birth elevation in meters (default: 0).
|
|
|
|
|
- house_system: "placidus" (default), "equal", or "whole_sign".
|
|
|
|
|
- orb_limits: Per-aspect orb overrides, e.g. {"conjunction": 10}.
|
|
|
|
|
- top_n_aspects: Limit aspects to the N tightest by orb.
|
|
|
|
|
-
|
|
|
|
|
- RENDERING OPTIONS:
|
|
|
|
|
- style: {_RENDER_STYLE_HELP}
|
|
|
|
|
- color_mode: {_RENDER_COLOR_HELP}
|
|
|
|
|
- size: {_RENDER_SIZE_HELP}
|
|
|
|
|
- table_position: "none" (wheel only), "below" (portrait layout with tables
|
|
|
|
|
- under the wheel), or "right" (landscape layout with tables to the right).
|
|
|
|
|
- include_planets: Include a planet data 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.
|
|
|
|
|
- subtitle: Custom subtitle. Auto-generated from birth data if not provided.
|
|
|
|
|
- format: Output format — "svg" (default), "png", or "jpg".
|
|
|
|
|
-
|
|
|
|
|
- Returns:
|
|
|
|
|
- Dict with "content", "format", "content_type", "width", "height",
|
|
|
|
|
- and "included" (list of what's in the chart).
|
|
|
|
|
- """
|
|
|
|
|
- from .chart_renderer import render_natal_wheel
|
|
|
|
|
-
|
|
|
|
|
- chart_data = await calculate_natal_chart(
|
|
|
|
|
- birth_datetime=birth_datetime,
|
|
|
|
|
- latitude=latitude,
|
|
|
|
|
- longitude=longitude,
|
|
|
|
|
- elevation=elevation,
|
|
|
|
|
- house_system=house_system,
|
|
|
|
|
- orb_limits=orb_limits,
|
|
|
|
|
- top_n_aspects=top_n_aspects,
|
|
|
|
|
- )
|
|
|
|
|
- if "error" in chart_data:
|
|
|
|
|
- return chart_data
|
|
|
|
|
-
|
|
|
|
|
- result = render_natal_wheel(
|
|
|
|
|
- chart_data,
|
|
|
|
|
- style=style,
|
|
|
|
|
- color_mode=color_mode,
|
|
|
|
|
- size=size,
|
|
|
|
|
- table_position=table_position,
|
|
|
|
|
- include_planets=include_planets,
|
|
|
|
|
- include_houses=include_houses,
|
|
|
|
|
- title=title,
|
|
|
|
|
- subtitle=subtitle,
|
|
|
|
|
- format=format,
|
|
|
|
|
- )
|
|
|
|
|
- result["included"] = _included_list(table_position, include_planets, include_houses)
|
|
|
|
|
- return result
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
-# ── render_natal_chart_by_id ──────────────────────────────────────────
|
|
|
|
|
-
|
|
|
|
|
-@mcp.tool()
|
|
|
|
|
-async def render_natal_chart_by_id(
|
|
|
|
|
- # ── Person lookup (same as calculate_natal_chart_by_id) ─────────
|
|
|
|
|
- person_id: str,
|
|
|
|
|
- house_system: str = "placidus",
|
|
|
|
|
- orb_limits: dict[str, float] | None = None,
|
|
|
|
|
- top_n_aspects: int | None = None,
|
|
|
|
|
- # ── Rendering options ───────────────────────────────────────────
|
|
|
|
|
- style: str = "modern",
|
|
|
|
|
- color_mode: str = "color",
|
|
|
|
|
- size: int = 600,
|
|
|
|
|
- table_position: str = "none",
|
|
|
|
|
- include_planets: bool = False,
|
|
|
|
|
- include_houses: bool = False,
|
|
|
|
|
- title: str | None = None,
|
|
|
|
|
- format: str = "svg",
|
|
|
|
|
-) -> dict[str, Any]:
|
|
|
|
|
- """Render a natal chart wheel for a person from the database.
|
|
|
|
|
-
|
|
|
|
|
- Looks up birth data by person_id or nickname, calculates the chart,
|
|
|
|
|
- and renders it as a wheel. Output as SVG or raster image (PNG/JPG).
|
|
|
|
|
-
|
|
|
|
|
- PERSON LOOKUP (required):
|
|
|
|
|
- person_id: ID or nickname of a person in the persons database.
|
|
|
|
|
-
|
|
|
|
|
- PERSON LOOKUP (optional):
|
|
|
|
|
- house_system: "placidus" (default), "equal", or "whole_sign".
|
|
|
|
|
- orb_limits: Per-aspect orb overrides.
|
|
|
|
|
- top_n_aspects: Limit aspects to the N tightest by orb.
|
|
|
|
|
-
|
|
|
|
|
- RENDERING OPTIONS:
|
|
|
|
|
- style: {_RENDER_STYLE_HELP}
|
|
|
|
|
- color_mode: {_RENDER_COLOR_HELP}
|
|
|
|
|
- size: {_RENDER_SIZE_HELP}
|
|
|
|
|
- table_position: "none" (wheel only), "below" (portrait), or "right" (landscape).
|
|
|
|
|
- include_planets: {_RENDER_PLANETS_HELP}
|
|
|
|
|
- include_houses: {_RENDER_HOUSES_HELP}
|
|
|
|
|
- title: {_RENDER_TITLE_HELP}
|
|
|
|
|
-
|
|
|
|
|
- Returns:
|
|
|
|
|
- Dict with "svg" (SVG string), "format", "width", "height", "included".
|
|
|
|
|
- """
|
|
|
|
|
- from .chart_renderer import render_natal_wheel
|
|
|
|
|
-
|
|
|
|
|
- chart_data = await calculate_natal_chart_by_id(
|
|
|
|
|
- person_id=person_id,
|
|
|
|
|
- house_system=house_system,
|
|
|
|
|
- orb_limits=orb_limits,
|
|
|
|
|
- top_n_aspects=top_n_aspects,
|
|
|
|
|
- )
|
|
|
|
|
- if "error" in chart_data:
|
|
|
|
|
- return chart_data
|
|
|
|
|
-
|
|
|
|
|
- result = render_natal_wheel(
|
|
|
|
|
- chart_data,
|
|
|
|
|
- style=style,
|
|
|
|
|
- color_mode=color_mode,
|
|
|
|
|
- size=size,
|
|
|
|
|
- table_position=table_position,
|
|
|
|
|
- include_planets=include_planets,
|
|
|
|
|
- include_houses=include_houses,
|
|
|
|
|
- title=title,
|
|
|
|
|
- format=format,
|
|
|
|
|
- )
|
|
|
|
|
- result["included"] = _included_list(table_position, include_planets, include_houses)
|
|
|
|
|
- return result
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
-# ── render_transit_chart ──────────────────────────────────────────────
|
|
|
|
|
-
|
|
|
|
|
-@mcp.tool()
|
|
|
|
|
-async def render_transit_chart(
|
|
|
|
|
- # ── Birth data + transit date (same as calculate_transit_chart) ─
|
|
|
|
|
- birth_datetime: str,
|
|
|
|
|
- transit_datetime: str,
|
|
|
|
|
- latitude: float,
|
|
|
|
|
- longitude: float,
|
|
|
|
|
- transit_latitude: float | None = None,
|
|
|
|
|
- transit_longitude: float | None = None,
|
|
|
|
|
- elevation: float = 0.0,
|
|
|
|
|
- house_system: str = "placidus",
|
|
|
|
|
- orb_limits: dict[str, float] | None = None,
|
|
|
|
|
- # ── Rendering options ───────────────────────────────────────────
|
|
|
|
|
- style: str = "modern",
|
|
|
|
|
- color_mode: str = "color",
|
|
|
|
|
- size: int = 600,
|
|
|
|
|
- title: str | None = None,
|
|
|
|
|
- format: str = "svg",
|
|
|
|
|
-) -> dict[str, Any]:
|
|
|
|
|
- """Render a transit chart as a bi-wheel (natal inner, transit outer).
|
|
|
|
|
-
|
|
|
|
|
- Calculates natal and transiting planet positions, then renders a bi-wheel
|
|
|
|
|
- showing natal planets inside and transiting planets outside, with
|
|
|
|
|
- transit-to-natal aspect lines. Output as SVG or raster image (PNG/JPG).
|
|
|
|
|
-
|
|
|
|
|
- BIRTH DATA (required):
|
|
|
|
|
- birth_datetime: ISO 8601 birth datetime with timezone.
|
|
|
|
|
- transit_datetime: ISO 8601 transit datetime (the "now" or future date).
|
|
|
|
|
- latitude: Birth latitude in decimal degrees.
|
|
|
|
|
- longitude: Birth longitude in decimal degrees.
|
|
|
|
|
-
|
|
|
|
|
- TRANSIT LOCATION (optional):
|
|
|
|
|
- transit_latitude: Location latitude for transit calculation. Defaults to birth latitude.
|
|
|
|
|
- transit_longitude: Location longitude for transit calculation. Defaults to birth longitude.
|
|
|
|
|
-
|
|
|
|
|
- CHART OPTIONS:
|
|
|
|
|
- elevation: Birth elevation in meters (default: 0).
|
|
|
|
|
- house_system: "placidus" (default), "equal", or "whole_sign".
|
|
|
|
|
- orb_limits: Per-aspect orb overrides.
|
|
|
|
|
-
|
|
|
|
|
- RENDERING OPTIONS:
|
|
|
|
|
- style: {_RENDER_STYLE_HELP}
|
|
|
|
|
- color_mode: {_RENDER_COLOR_HELP}
|
|
|
|
|
- size: {_RENDER_SIZE_HELP}
|
|
|
|
|
- title: {_RENDER_TITLE_HELP}
|
|
|
|
|
- format: Output format — "svg" (default), "png", or "jpg".
|
|
|
|
|
-
|
|
|
|
|
- Returns:
|
|
|
|
|
- Dict with "content", "format", "content_type", "width", "height", "included".
|
|
|
|
|
- """
|
|
|
|
|
- from .chart_renderer import render_transit_wheel
|
|
|
|
|
-
|
|
|
|
|
- chart_data = await calculate_transit_chart(
|
|
|
|
|
- birth_datetime=birth_datetime,
|
|
|
|
|
- transit_datetime=transit_datetime,
|
|
|
|
|
- latitude=latitude,
|
|
|
|
|
- longitude=longitude,
|
|
|
|
|
- transit_latitude=transit_latitude,
|
|
|
|
|
- transit_longitude=transit_longitude,
|
|
|
|
|
- elevation=elevation,
|
|
|
|
|
- house_system=house_system,
|
|
|
|
|
- orb_limits=orb_limits,
|
|
|
|
|
- )
|
|
|
|
|
- if "error" in chart_data:
|
|
|
|
|
- return chart_data
|
|
|
|
|
-
|
|
|
|
|
- result = render_transit_wheel(
|
|
|
|
|
- chart_data,
|
|
|
|
|
- style=style,
|
|
|
|
|
- color_mode=color_mode,
|
|
|
|
|
- size=size,
|
|
|
|
|
- title=title,
|
|
|
|
|
- format=format,
|
|
|
|
|
- )
|
|
|
|
|
- result["included"] = ["wheel"]
|
|
|
|
|
- return result
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
-# ── render_transit_chart_by_id ────────────────────────────────────────
|
|
|
|
|
-
|
|
|
|
|
-@mcp.tool()
|
|
|
|
|
-async def render_transit_chart_by_id(
|
|
|
|
|
- # ── Person lookup + transit date ────────────────────────────────
|
|
|
|
|
- person_id: str,
|
|
|
|
|
- transit_datetime: str,
|
|
|
|
|
- transit_latitude: float | None = None,
|
|
|
|
|
- transit_longitude: float | None = None,
|
|
|
|
|
- house_system: str = "placidus",
|
|
|
|
|
- orb_limits: dict[str, float] | None = None,
|
|
|
|
|
- # ── Rendering options ───────────────────────────────────────────
|
|
|
|
|
- style: str = "modern",
|
|
|
|
|
- color_mode: str = "color",
|
|
|
|
|
- size: int = 600,
|
|
|
|
|
- title: str | None = None,
|
|
|
|
|
- format: str = "svg",
|
|
|
|
|
-) -> dict[str, Any]:
|
|
|
|
|
- """Render a transit bi-wheel for a person from the database.
|
|
|
|
|
-
|
|
|
|
|
- Looks up birth data by person_id, calculates transits for the given
|
|
|
|
|
- date, and renders a bi-wheel chart. Output as SVG or raster image (PNG/JPG).
|
|
|
|
|
-
|
|
|
|
|
- PERSON LOOKUP (required):
|
|
|
|
|
- person_id: ID or nickname of a person in the persons database.
|
|
|
|
|
- transit_datetime: ISO 8601 transit datetime.
|
|
|
|
|
-
|
|
|
|
|
- TRANSIT LOCATION (optional):
|
|
|
|
|
- transit_latitude: Location latitude. Defaults to birth latitude.
|
|
|
|
|
- transit_longitude: Location longitude. Defaults to birth longitude.
|
|
|
|
|
-
|
|
|
|
|
- CHART OPTIONS:
|
|
|
|
|
- house_system: "placidus" (default), "equal", or "whole_sign".
|
|
|
|
|
- orb_limits: Per-aspect orb overrides.
|
|
|
|
|
-
|
|
|
|
|
- RENDERING OPTIONS:
|
|
|
|
|
- style: {_RENDER_STYLE_HELP}
|
|
|
|
|
- color_mode: {_RENDER_COLOR_HELP}
|
|
|
|
|
- size: {_RENDER_SIZE_HELP}
|
|
|
|
|
- title: {_RENDER_TITLE_HELP}
|
|
|
|
|
-
|
|
|
|
|
- Returns:
|
|
|
|
|
- Dict with "svg", "format", "width", "height", "included".
|
|
|
|
|
- """
|
|
|
|
|
- from .chart_renderer import render_transit_wheel
|
|
|
|
|
-
|
|
|
|
|
- chart_data = await calculate_transit_chart_by_id(
|
|
|
|
|
- person_id=person_id,
|
|
|
|
|
- transit_datetime=transit_datetime,
|
|
|
|
|
- transit_latitude=transit_latitude,
|
|
|
|
|
- transit_longitude=transit_longitude,
|
|
|
|
|
- house_system=house_system,
|
|
|
|
|
- orb_limits=orb_limits,
|
|
|
|
|
- )
|
|
|
|
|
- if "error" in chart_data:
|
|
|
|
|
- return chart_data
|
|
|
|
|
-
|
|
|
|
|
- result = render_transit_wheel(
|
|
|
|
|
- chart_data,
|
|
|
|
|
- style=style,
|
|
|
|
|
- color_mode=color_mode,
|
|
|
|
|
- size=size,
|
|
|
|
|
- title=title,
|
|
|
|
|
- format=format,
|
|
|
|
|
- )
|
|
|
|
|
- result["included"] = ["wheel"]
|
|
|
|
|
- return result
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
-# ── render_synastry_chart ─────────────────────────────────────────────
|
|
|
|
|
-
|
|
|
|
|
-@mcp.tool()
|
|
|
|
|
-async def render_synastry_chart(
|
|
|
|
|
- # ── Two people's birth data (same as calculate_synastry_chart) ──
|
|
|
|
|
- person1_datetime: str,
|
|
|
|
|
- person1_latitude: float,
|
|
|
|
|
- person1_longitude: float,
|
|
|
|
|
- person2_datetime: str,
|
|
|
|
|
- person2_latitude: float,
|
|
|
|
|
- person2_longitude: float,
|
|
|
|
|
- elevation: float = 0.0,
|
|
|
|
|
- house_system: str = "placidus",
|
|
|
|
|
- orb_limits: dict[str, float] | None = None,
|
|
|
|
|
- top_n_aspects: int | None = None,
|
|
|
|
|
- # ── Rendering options ───────────────────────────────────────────
|
|
|
|
|
- style: str = "modern",
|
|
|
|
|
- color_mode: str = "color",
|
|
|
|
|
- size: int = 800,
|
|
|
|
|
- title: str | None = None,
|
|
|
|
|
- format: str = "svg",
|
|
|
|
|
-) -> dict[str, Any]:
|
|
|
|
|
- """Render a synastry (relationship) chart with two side-by-side wheels.
|
|
|
|
|
-
|
|
|
|
|
- Calculates both natal charts and renders them side by side with
|
|
|
|
|
- interaspect lines between the two charts. Output as SVG or raster image (PNG/JPG).
|
|
|
|
|
-
|
|
|
|
|
- PERSON 1 (required):
|
|
|
|
|
- person1_datetime: ISO 8601 birth datetime with timezone.
|
|
|
|
|
- person1_latitude: Birth latitude in decimal degrees.
|
|
|
|
|
- person1_longitude: Birth longitude in decimal degrees.
|
|
|
|
|
-
|
|
|
|
|
- PERSON 2 (required):
|
|
|
|
|
- person2_datetime: ISO 8601 birth datetime with timezone.
|
|
|
|
|
- person2_latitude: Birth latitude in decimal degrees.
|
|
|
|
|
- person2_longitude: Birth longitude in decimal degrees.
|
|
|
|
|
-
|
|
|
|
|
- CHART OPTIONS:
|
|
|
|
|
- elevation: Birth elevation in meters (default: 0).
|
|
|
|
|
- house_system: "placidus" (default), "equal", or "whole_sign".
|
|
|
|
|
- orb_limits: Per-aspect orb overrides.
|
|
|
|
|
- top_n_aspects: Limit interaspects to the N tightest by orb.
|
|
|
|
|
-
|
|
|
|
|
- RENDERING OPTIONS:
|
|
|
|
|
- style: {_RENDER_STYLE_HELP}
|
|
|
|
|
- color_mode: {_RENDER_COLOR_HELP}
|
|
|
|
|
- size: {_RENDER_SIZE_HELP}
|
|
|
|
|
- title: {_RENDER_TITLE_HELP}
|
|
|
|
|
-
|
|
|
|
|
- Returns:
|
|
|
|
|
- Dict with "svg", "format", "width", "height", "included".
|
|
|
|
|
- """
|
|
|
|
|
- from .chart_renderer import render_synastry_wheel
|
|
|
|
|
-
|
|
|
|
|
- chart_data = await calculate_synastry_chart(
|
|
|
|
|
- person1_datetime=person1_datetime,
|
|
|
|
|
- person1_latitude=person1_latitude,
|
|
|
|
|
- person1_longitude=person1_longitude,
|
|
|
|
|
- person2_datetime=person2_datetime,
|
|
|
|
|
- person2_latitude=person2_latitude,
|
|
|
|
|
- person2_longitude=person2_longitude,
|
|
|
|
|
- elevation=elevation,
|
|
|
|
|
- house_system=house_system,
|
|
|
|
|
- orb_limits=orb_limits,
|
|
|
|
|
- top_n_aspects=top_n_aspects,
|
|
|
|
|
- )
|
|
|
|
|
- if "error" in chart_data:
|
|
|
|
|
- return chart_data
|
|
|
|
|
-
|
|
|
|
|
- result = render_synastry_wheel(
|
|
|
|
|
- chart_data,
|
|
|
|
|
- style=style,
|
|
|
|
|
- color_mode=color_mode,
|
|
|
|
|
- size=size,
|
|
|
|
|
- title=title,
|
|
|
|
|
- format=format,
|
|
|
|
|
- )
|
|
|
|
|
- result["included"] = ["wheel"]
|
|
|
|
|
- return result
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
-# ── render_synastry_chart_by_id ───────────────────────────────────────
|
|
|
|
|
-
|
|
|
|
|
-@mcp.tool()
|
|
|
|
|
-async def render_synastry_chart_by_id(
|
|
|
|
|
- # ── Two person IDs ──────────────────────────────────────────────
|
|
|
|
|
- person1_id: str,
|
|
|
|
|
- person2_id: str,
|
|
|
|
|
- house_system: str = "placidus",
|
|
|
|
|
- orb_limits: dict[str, float] | None = None,
|
|
|
|
|
- top_n_aspects: int | None = None,
|
|
|
|
|
- # ── Rendering options ───────────────────────────────────────────
|
|
|
|
|
- style: str = "modern",
|
|
|
|
|
- color_mode: str = "color",
|
|
|
|
|
- size: int = 800,
|
|
|
|
|
- title: str | None = None,
|
|
|
|
|
- format: str = "svg",
|
|
|
|
|
-) -> dict[str, Any]:
|
|
|
|
|
- """Render a synastry chart for two people from the database.
|
|
|
|
|
-
|
|
|
|
|
- Looks up both persons by ID or nickname, calculates their synastry,
|
|
|
|
|
- and renders side-by-side natal wheels with interaspect lines.
|
|
|
|
|
- Output as SVG or raster image (PNG/JPG).
|
|
|
|
|
-
|
|
|
|
|
- PERSON LOOKUP (required):
|
|
|
|
|
- person1_id: ID or nickname of person 1 in the persons database.
|
|
|
|
|
- person2_id: ID or nickname of person 2 in the persons database.
|
|
|
|
|
-
|
|
|
|
|
- CHART OPTIONS:
|
|
|
|
|
- house_system: "placidus" (default), "equal", or "whole_sign".
|
|
|
|
|
- orb_limits: Per-aspect orb overrides.
|
|
|
|
|
- top_n_aspects: Limit interaspects to the N tightest by orb.
|
|
|
|
|
-
|
|
|
|
|
- RENDERING OPTIONS:
|
|
|
|
|
- style: {_RENDER_STYLE_HELP}
|
|
|
|
|
- color_mode: {_RENDER_COLOR_HELP}
|
|
|
|
|
- size: {_RENDER_SIZE_HELP}
|
|
|
|
|
- title: {_RENDER_TITLE_HELP}
|
|
|
|
|
-
|
|
|
|
|
- Returns:
|
|
|
|
|
- Dict with "svg", "format", "width", "height", "included".
|
|
|
|
|
- """
|
|
|
|
|
- from .chart_renderer import render_synastry_wheel
|
|
|
|
|
-
|
|
|
|
|
- chart_data = await calculate_synastry_chart_by_id(
|
|
|
|
|
- person1_id=person1_id,
|
|
|
|
|
- person2_id=person2_id,
|
|
|
|
|
- house_system=house_system,
|
|
|
|
|
- orb_limits=orb_limits,
|
|
|
|
|
- top_n_aspects=top_n_aspects,
|
|
|
|
|
- )
|
|
|
|
|
- if "error" in chart_data:
|
|
|
|
|
- return chart_data
|
|
|
|
|
-
|
|
|
|
|
- result = render_synastry_wheel(
|
|
|
|
|
- chart_data,
|
|
|
|
|
- style=style,
|
|
|
|
|
- color_mode=color_mode,
|
|
|
|
|
- size=size,
|
|
|
|
|
- title=title,
|
|
|
|
|
- format=format,
|
|
|
|
|
- )
|
|
|
|
|
- result["included"] = ["wheel"]
|
|
|
|
|
- return result
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
-# ── render_composite_chart ────────────────────────────────────────────
|
|
|
|
|
-
|
|
|
|
|
-@mcp.tool()
|
|
|
|
|
-async def render_composite_chart(
|
|
|
|
|
- # ── Two people's birth data (same as calculate_composite_chart) ─
|
|
|
|
|
- person1_datetime: str,
|
|
|
|
|
- person1_latitude: float,
|
|
|
|
|
- person1_longitude: float,
|
|
|
|
|
- person2_datetime: str,
|
|
|
|
|
- person2_latitude: float,
|
|
|
|
|
- person2_longitude: float,
|
|
|
|
|
- elevation: float = 0.0,
|
|
|
|
|
- house_system: str = "placidus",
|
|
|
|
|
- orb_limits: dict[str, float] | None = None,
|
|
|
|
|
- # ── Rendering options ───────────────────────────────────────────
|
|
|
|
|
- style: str = "modern",
|
|
|
|
|
- color_mode: str = "color",
|
|
|
|
|
- size: int = 600,
|
|
|
|
|
- table_position: str = "none",
|
|
|
|
|
- include_planets: bool = False,
|
|
|
|
|
- include_houses: bool = False,
|
|
|
|
|
- title: str | None = None,
|
|
|
|
|
- format: str = "svg",
|
|
|
|
|
-) -> dict[str, Any]:
|
|
|
|
|
- """Render a composite chart (midpoint method) as a single wheel.
|
|
|
|
|
-
|
|
|
|
|
- Calculates the composite chart from two people's birth data and renders
|
|
|
|
|
- it as a standard natal-style wheel representing the relationship.
|
|
|
|
|
- Output as SVG or raster image (PNG/JPG).
|
|
|
|
|
-
|
|
|
|
|
- PERSON 1 (required):
|
|
|
|
|
- person1_datetime: ISO 8601 birth datetime with timezone.
|
|
|
|
|
- person1_latitude: Birth latitude in decimal degrees.
|
|
|
|
|
- person1_longitude: Birth longitude in decimal degrees.
|
|
|
|
|
-
|
|
|
|
|
- PERSON 2 (required):
|
|
|
|
|
- person2_datetime: ISO 8601 birth datetime with timezone.
|
|
|
|
|
- person2_latitude: Birth latitude in decimal degrees.
|
|
|
|
|
- person2_longitude: Birth longitude in decimal degrees.
|
|
|
|
|
-
|
|
|
|
|
- CHART OPTIONS:
|
|
|
|
|
- elevation: Birth elevation in meters (default: 0).
|
|
|
|
|
- house_system: "placidus" (default), "equal", or "whole_sign".
|
|
|
|
|
- orb_limits: Per-aspect orb overrides.
|
|
|
|
|
-
|
|
|
|
|
- RENDERING OPTIONS:
|
|
|
|
|
- style: {_RENDER_STYLE_HELP}
|
|
|
|
|
- color_mode: {_RENDER_COLOR_HELP}
|
|
|
|
|
- size: {_RENDER_SIZE_HELP}
|
|
|
|
|
- table_position: "none" (wheel only), "below" (portrait), or "right" (landscape).
|
|
|
|
|
- include_planets: {_RENDER_PLANETS_HELP}
|
|
|
|
|
- include_houses: {_RENDER_HOUSES_HELP}
|
|
|
|
|
- title: {_RENDER_TITLE_HELP}
|
|
|
|
|
-
|
|
|
|
|
- Returns:
|
|
|
|
|
- Dict with "svg", "format", "width", "height", "included".
|
|
|
|
|
- """
|
|
|
|
|
- from .chart_renderer import render_natal_wheel
|
|
|
|
|
-
|
|
|
|
|
- chart_data = await calculate_composite_chart(
|
|
|
|
|
- person1_datetime=person1_datetime,
|
|
|
|
|
- person1_latitude=person1_latitude,
|
|
|
|
|
- person1_longitude=person1_longitude,
|
|
|
|
|
- person2_datetime=person2_datetime,
|
|
|
|
|
- person2_latitude=person2_latitude,
|
|
|
|
|
- person2_longitude=person2_longitude,
|
|
|
|
|
- elevation=elevation,
|
|
|
|
|
- house_system=house_system,
|
|
|
|
|
- orb_limits=orb_limits,
|
|
|
|
|
- )
|
|
|
|
|
- if "error" in chart_data:
|
|
|
|
|
- return chart_data
|
|
|
|
|
-
|
|
|
|
|
- result = render_natal_wheel(
|
|
|
|
|
- chart_data,
|
|
|
|
|
- style=style,
|
|
|
|
|
- color_mode=color_mode,
|
|
|
|
|
- size=size,
|
|
|
|
|
- table_position=table_position,
|
|
|
|
|
- include_planets=include_planets,
|
|
|
|
|
- include_houses=include_houses,
|
|
|
|
|
- title=title,
|
|
|
|
|
- format=format,
|
|
|
|
|
- )
|
|
|
|
|
- result["included"] = _included_list(table_position, include_planets, include_houses)
|
|
|
|
|
- return result
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
-# ── render_composite_chart_by_id ──────────────────────────────────────
|
|
|
|
|
-
|
|
|
|
|
-@mcp.tool()
|
|
|
|
|
-async def render_composite_chart_by_id(
|
|
|
|
|
- # ── Two person IDs ──────────────────────────────────────────────
|
|
|
|
|
- person1_id: str,
|
|
|
|
|
- person2_id: str,
|
|
|
|
|
- house_system: str = "placidus",
|
|
|
|
|
- orb_limits: dict[str, float] | None = None,
|
|
|
|
|
- # ── Rendering options ───────────────────────────────────────────
|
|
|
|
|
- style: str = "modern",
|
|
|
|
|
- color_mode: str = "color",
|
|
|
|
|
- size: int = 600,
|
|
|
|
|
- table_position: str = "none",
|
|
|
|
|
- include_planets: bool = False,
|
|
|
|
|
- include_houses: bool = False,
|
|
|
|
|
- title: str | None = None,
|
|
|
|
|
- format: str = "svg",
|
|
|
|
|
-) -> dict[str, Any]:
|
|
|
|
|
- """Render a composite chart for two people from the database.
|
|
|
|
|
-
|
|
|
|
|
- Looks up both persons by ID, calculates the composite chart, and
|
|
|
|
|
- renders it as a single natal-style wheel. Output as SVG or raster image (PNG/JPG).
|
|
|
|
|
-
|
|
|
|
|
- PERSON LOOKUP (required):
|
|
|
|
|
- person1_id: ID or nickname of person 1 in the persons database.
|
|
|
|
|
- person2_id: ID or nickname of person 2 in the persons database.
|
|
|
|
|
-
|
|
|
|
|
- CHART OPTIONS:
|
|
|
|
|
- house_system: "placidus" (default), "equal", or "whole_sign".
|
|
|
|
|
- orb_limits: Per-aspect orb overrides.
|
|
|
|
|
-
|
|
|
|
|
- RENDERING OPTIONS:
|
|
|
|
|
- style: {_RENDER_STYLE_HELP}
|
|
|
|
|
- color_mode: {_RENDER_COLOR_HELP}
|
|
|
|
|
- size: {_RENDER_SIZE_HELP}
|
|
|
|
|
- table_position: "none" (wheel only), "below" (portrait), or "right" (landscape).
|
|
|
|
|
- include_planets: {_RENDER_PLANETS_HELP}
|
|
|
|
|
- include_houses: {_RENDER_HOUSES_HELP}
|
|
|
|
|
- title: {_RENDER_TITLE_HELP}
|
|
|
|
|
- format: Output format — "svg" (default), "png", or "jpg".
|
|
|
|
|
-
|
|
|
|
|
- Returns:
|
|
|
|
|
- Dict with "content", "format", "content_type", "width", "height", "included".
|
|
|
|
|
- """
|
|
|
|
|
- from .chart_renderer import render_natal_wheel
|
|
|
|
|
-
|
|
|
|
|
- chart_data = await calculate_composite_chart_by_id(
|
|
|
|
|
- person1_id=person1_id,
|
|
|
|
|
- person2_id=person2_id,
|
|
|
|
|
- house_system=house_system,
|
|
|
|
|
- orb_limits=orb_limits,
|
|
|
|
|
- )
|
|
|
|
|
- if "error" in chart_data:
|
|
|
|
|
- return chart_data
|
|
|
|
|
-
|
|
|
|
|
- result = render_natal_wheel(
|
|
|
|
|
- chart_data,
|
|
|
|
|
- style=style,
|
|
|
|
|
- color_mode=color_mode,
|
|
|
|
|
- size=size,
|
|
|
|
|
- table_position=table_position,
|
|
|
|
|
- include_planets=include_planets,
|
|
|
|
|
- include_houses=include_houses,
|
|
|
|
|
- title=title,
|
|
|
|
|
- format=format,
|
|
|
|
|
- )
|
|
|
|
|
- result["included"] = _included_list(table_position, include_planets, include_houses)
|
|
|
|
|
- return result
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
-# ── render_davison_chart ──────────────────────────────────────────────
|
|
|
|
|
-
|
|
|
|
|
-@mcp.tool()
|
|
|
|
|
-async def render_davison_chart(
|
|
|
|
|
- # ── Two people's birth data (same as calculate_davison_chart) ───
|
|
|
|
|
- person1_datetime: str,
|
|
|
|
|
- person1_latitude: float,
|
|
|
|
|
- person1_longitude: float,
|
|
|
|
|
- person2_datetime: str,
|
|
|
|
|
- person2_latitude: float,
|
|
|
|
|
- person2_longitude: float,
|
|
|
|
|
- elevation: float = 0.0,
|
|
|
|
|
- house_system: str = "placidus",
|
|
|
|
|
- orb_limits: dict[str, float] | None = None,
|
|
|
|
|
- # ── Rendering options ───────────────────────────────────────────
|
|
|
|
|
- style: str = "modern",
|
|
|
|
|
- color_mode: str = "color",
|
|
|
|
|
- size: int = 600,
|
|
|
|
|
- table_position: str = "none",
|
|
|
|
|
- include_planets: bool = False,
|
|
|
|
|
- include_houses: bool = False,
|
|
|
|
|
- title: str | None = None,
|
|
|
|
|
- format: str = "svg",
|
|
|
|
|
-) -> dict[str, Any]:
|
|
|
|
|
- """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
|
|
|
|
|
- it as a standard natal-style wheel. Output as SVG or raster image (PNG/JPG).
|
|
|
|
|
-
|
|
|
|
|
- PERSON 1 (required):
|
|
|
|
|
- person1_datetime: ISO 8601 birth datetime with timezone.
|
|
|
|
|
- person1_latitude: Birth latitude in decimal degrees.
|
|
|
|
|
- person1_longitude: Birth longitude in decimal degrees.
|
|
|
|
|
-
|
|
|
|
|
- PERSON 2 (required):
|
|
|
|
|
- person2_datetime: ISO 8601 birth datetime with timezone.
|
|
|
|
|
- person2_latitude: Birth latitude in decimal degrees.
|
|
|
|
|
- person2_longitude: Birth longitude in decimal degrees.
|
|
|
|
|
-
|
|
|
|
|
- CHART OPTIONS:
|
|
|
|
|
- elevation: Birth elevation in meters (default: 0).
|
|
|
|
|
- house_system: "placidus" (default), "equal", or "whole_sign".
|
|
|
|
|
- orb_limits: Per-aspect orb overrides.
|
|
|
|
|
-
|
|
|
|
|
- RENDERING OPTIONS:
|
|
|
|
|
- style: {_RENDER_STYLE_HELP}
|
|
|
|
|
- color_mode: {_RENDER_COLOR_HELP}
|
|
|
|
|
- size: {_RENDER_SIZE_HELP}
|
|
|
|
|
- table_position: "none" (wheel only), "below" (portrait), or "right" (landscape).
|
|
|
|
|
- include_planets: {_RENDER_PLANETS_HELP}
|
|
|
|
|
- include_houses: {_RENDER_HOUSES_HELP}
|
|
|
|
|
- title: {_RENDER_TITLE_HELP}
|
|
|
|
|
-
|
|
|
|
|
- Returns:
|
|
|
|
|
- Dict with "svg", "format", "width", "height", "included".
|
|
|
|
|
- """
|
|
|
|
|
- from .chart_renderer import render_natal_wheel
|
|
|
|
|
-
|
|
|
|
|
- chart_data = await calculate_davison_chart(
|
|
|
|
|
- person1_datetime=person1_datetime,
|
|
|
|
|
- person1_latitude=person1_latitude,
|
|
|
|
|
- person1_longitude=person1_longitude,
|
|
|
|
|
- person2_datetime=person2_datetime,
|
|
|
|
|
- person2_latitude=person2_latitude,
|
|
|
|
|
- person2_longitude=person2_longitude,
|
|
|
|
|
- elevation=elevation,
|
|
|
|
|
- house_system=house_system,
|
|
|
|
|
- orb_limits=orb_limits,
|
|
|
|
|
- )
|
|
|
|
|
- if "error" in chart_data:
|
|
|
|
|
- return chart_data
|
|
|
|
|
-
|
|
|
|
|
- result = render_natal_wheel(
|
|
|
|
|
- chart_data,
|
|
|
|
|
- style=style,
|
|
|
|
|
- color_mode=color_mode,
|
|
|
|
|
- size=size,
|
|
|
|
|
- table_position=table_position,
|
|
|
|
|
- include_planets=include_planets,
|
|
|
|
|
- include_houses=include_houses,
|
|
|
|
|
- title=title,
|
|
|
|
|
- format=format,
|
|
|
|
|
- )
|
|
|
|
|
- result["included"] = _included_list(table_position, include_planets, include_houses)
|
|
|
|
|
- return result
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
-# ── render_davison_chart_by_id ────────────────────────────────────────
|
|
|
|
|
-
|
|
|
|
|
-@mcp.tool()
|
|
|
|
|
-async def render_davison_chart_by_id(
|
|
|
|
|
- # ── Two person IDs ──────────────────────────────────────────────
|
|
|
|
|
- person1_id: str,
|
|
|
|
|
- person2_id: str,
|
|
|
|
|
- house_system: str = "placidus",
|
|
|
|
|
- orb_limits: dict[str, float] | None = None,
|
|
|
|
|
- # ── Rendering options ───────────────────────────────────────────
|
|
|
|
|
- style: str = "modern",
|
|
|
|
|
- color_mode: str = "color",
|
|
|
|
|
- size: int = 600,
|
|
|
|
|
- table_position: str = "none",
|
|
|
|
|
- include_planets: bool = False,
|
|
|
|
|
- include_houses: bool = False,
|
|
|
|
|
- title: str | None = None,
|
|
|
|
|
- format: str = "svg",
|
|
|
|
|
-) -> dict[str, Any]:
|
|
|
|
|
- """Render a Davison chart for two people from the database.
|
|
|
|
|
-
|
|
|
|
|
- Looks up both persons by ID, calculates the Davison chart, and
|
|
|
|
|
- renders it as a single natal-style wheel. Output as SVG or raster image.
|
|
|
|
|
-
|
|
|
|
|
- PERSON LOOKUP (required):
|
|
|
|
|
- person1_id: ID or nickname of person 1 in the persons database.
|
|
|
|
|
- person2_id: ID or nickname of person 2 in the persons database.
|
|
|
|
|
-
|
|
|
|
|
- CHART OPTIONS:
|
|
|
|
|
- house_system: "placidus" (default), "equal", or "whole_sign".
|
|
|
|
|
- orb_limits: Per-aspect orb overrides.
|
|
|
|
|
-
|
|
|
|
|
- RENDERING OPTIONS:
|
|
|
|
|
- style: {_RENDER_STYLE_HELP}
|
|
|
|
|
- color_mode: {_RENDER_COLOR_HELP}
|
|
|
|
|
- size: {_RENDER_SIZE_HELP}
|
|
|
|
|
- table_position: "none" (wheel only), "below" (portrait), or "right" (landscape).
|
|
|
|
|
- include_planets: {_RENDER_PLANETS_HELP}
|
|
|
|
|
- include_houses: {_RENDER_HOUSES_HELP}
|
|
|
|
|
- title: {_RENDER_TITLE_HELP}
|
|
|
|
|
-
|
|
|
|
|
- Returns:
|
|
|
|
|
- Dict with "svg", "format", "width", "height", "included".
|
|
|
|
|
- """
|
|
|
|
|
- from .chart_renderer import render_natal_wheel
|
|
|
|
|
-
|
|
|
|
|
- chart_data = await calculate_davison_chart_by_id(
|
|
|
|
|
- person1_id=person1_id,
|
|
|
|
|
- person2_id=person2_id,
|
|
|
|
|
- house_system=house_system,
|
|
|
|
|
- orb_limits=orb_limits,
|
|
|
|
|
- )
|
|
|
|
|
- if "error" in chart_data:
|
|
|
|
|
- return chart_data
|
|
|
|
|
-
|
|
|
|
|
- result = render_natal_wheel(
|
|
|
|
|
- chart_data,
|
|
|
|
|
- style=style,
|
|
|
|
|
- color_mode=color_mode,
|
|
|
|
|
- size=size,
|
|
|
|
|
- table_position=table_position,
|
|
|
|
|
- include_planets=include_planets,
|
|
|
|
|
- include_houses=include_houses,
|
|
|
|
|
- title=title,
|
|
|
|
|
- format=format,
|
|
|
|
|
- )
|
|
|
|
|
- result["included"] = _included_list(table_position, include_planets, include_houses)
|
|
|
|
|
- return result
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
-# ── Helper ────────────────────────────────────────────────────────────
|
|
|
|
|
-
|
|
|
|
|
-def _included_list(table_position: str, planets: bool, houses: bool) -> list[str]:
|
|
|
|
|
- result = ["wheel"]
|
|
|
|
|
- if table_position in ("below", "right"):
|
|
|
|
|
- if planets:
|
|
|
|
|
- result.append("planet_table")
|
|
|
|
|
- if houses:
|
|
|
|
|
- result.append("house_table")
|
|
|
|
|
- return result
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|