Overview

The architecture separates UI, computation, workspace management, and storage so multiple frontends can share the same backend behavior.

Repository Structure (top level)

  • cache: temporary files for computation
  • module: all the program logic
  • scripts: generators for documentation
  • tests: scenario and functionality testing
  • venv: python environment with libraries

Core Layers

  • UI layer: Streamlit, Kivy, or Tauri/Svelte call Python services directly.
  • Services layer: module.services computes positions/aspects and builds charts.
  • Workspace layer: module.workspace loads/saves modular YAML workspaces.
  • Storage layer: module.storage persists positions to DuckDB and Parquet (positions-only).

Data Flow

  1. UI collects inputs and opens a workspace.
  2. Services compute positions (and aspects on demand).
  3. Storage writes positions to DuckDB and/or Parquet.
  4. UI renders charts from computed positions.

Storage Model

  • Positions-only: aspects are computed on demand.
  • Dual engines: Kerykeion (fast) and JPL/Skyfield (extended properties).
  • Radix support: radix_chart_id + is_radix for transit comparisons.
  • Hybrid storage: DuckDB for active queries, Parquet for time series.

Storage (Backend)

Strategy (Python vs Rust)

Python storage (batch):

  • Transit series and large time ranges
  • Background jobs and data migrations
  • Avoids JSON transfer overhead

Rust storage (interactive):

  • Single chart computations
  • User-driven queries
  • Small datasets and quick updates

Both Python and Rust use the same DuckDB schema.

Schema (Positions-Only)

computed_positions stores:

  • chart_id, datetime, object_id
  • longitude, latitude
  • declination, right_ascension, distance
  • altitude, azimuth
  • apparent_magnitude, phase_angle, elongation, light_time
  • speed, retrograde
  • engine, ephemeris_file
  • radix_chart_id, is_radix
  • has_equatorial, has_topocentric, has_physical

Aspects are computed on demand from positions.

Current behavior

  1. Dual engine support

    • Default: Kerykeion (Swisseph)
    • Optional: JPL (extended properties)
  2. Radix-relative storage

    • radix_chart_id links transits to base charts
    • is_radix marks base vs transit rows
  3. Performance optimization

    • Pre-initialized engine for batch series
    • No aspect storage (computed on demand)
  4. 3D-ready fields

    • declination and distance stored for JPL

Storage paths

workspace/
├── workspace.yaml
└── data/
    ├── workspace.db
    └── parquet/
        └── chart_id_YYYY-MM-DD.parquet

Layers by responsibility

🔹 Category 🌞 Horoscope 📊 Chart 🗂️ Workspace
🧱 Key Objects
  • Horoscope
  • CelestialBody
  • Aspect
  • House
  • ChartSubject
  • ChartInstance
  • ChartConfig
  • ChartPreset
  • Workspace
  • EphemerisSource
  • ModelOverrides
  • Annotation
⚙️ Core Functions
  • compute_positions(...)
  • compute_aspects(...)
  • compute_positions_for_chart(...)
  • build_chart_instance(...)
  • prepare_horoscope(...)
  • load_workspace(...)
  • save_workspace_modular(...)
  • get_all_aspect_definitions(...)
✨ Features
  • Immutable snapshot
  • Engine-specific (Western, Vedic, etc.)
  • Supports derived charts (e.g., progressed)
  • Custom points via overrides
  • Preset-driven config
  • Supports ChartMode (NATAL, TRANSIT, etc.)
  • Custom display and ephemeris override
  • Modular YAML structure
  • Per-user model customization
  • Annotations, media, layouts

Key entry points

  • module.services.compute_positions(...)
  • module.services.compute_positions_for_chart(...)
  • module.services.compute_aspects_for_chart(...)
  • module.services.build_chart_instance(...)
  • module.workspace.load_workspace(...)
  • module.workspace.save_workspace_modular(...)

Schéma

editor