89 lines
No EOL
5 KiB
Markdown
89 lines
No EOL
5 KiB
Markdown
# Project Architecture and Data Model
|
||
|
||
This document provides an overview of the project files, their roles, and a detailed description of the database architecture and data model.
|
||
|
||
## Project Structure
|
||
|
||
Root directory:
|
||
- **Dockerfile**: Defines the Python 3.12-slim image, installs dependencies (FastAPI, Uvicorn, SQLAlchemy, databases, TimescaleDB support), and runs the app.
|
||
- **docker-compose.yml**: Orchestrates two services:
|
||
- **dereth-tracker**: The FastAPI application container.
|
||
- **db**: A TimescaleDB (PostgreSQL 14 + TimescaleDB extension) container for persistent storage.
|
||
- **README.md**: High-level documentation and usage instructions.
|
||
- **EVENT_FORMATS.json**: Example JSON payloads for all event types (`telemetry`, `spawn`, `chat`, `rare`).
|
||
- **db.py**: Legacy SQLite-based storage (telemetry_log & live_state tables, WAL mode, auto-vacuum).
|
||
- **db_async.py**: Async database definitions for PostgreSQL/TimescaleDB:
|
||
- Table schemas (SQLAlchemy Core): `telemetry_events`, `char_stats`, `rare_stats`, `rare_stats_sessions`, `spawn_events`.
|
||
- `init_db_async()`: Creates tables, enables TimescaleDB extension, and configures a hypertable on `telemetry_events`.
|
||
- **main.py**: The FastAPI application:
|
||
- HTTP endpoints: `/debug`, `/live`, `/history`, `/trails`.
|
||
- WebSocket endpoints: `/ws/position` (plugin data in), `/ws/live` (browser live updates).
|
||
- Pydantic models: `TelemetrySnapshot`, `SpawnEvent`.
|
||
- In-memory state: `live_snapshots`, WebSocket connection registries.
|
||
- **generate_data.py**: Sample WebSocket client that sends synthetic telemetry snapshots.
|
||
- **alembic/** & **alembic.ini**: Migration tooling for evolving the database schema.
|
||
- **static/**: Frontend assets (HTML, CSS, JavaScript, images) for the live map UI.
|
||
- **FIXES.md**, **LESSONSLEARNED.md**, **TODO.md**: Project notes and future work.
|
||
|
||
## Database Overview
|
||
|
||
### Technology Stack
|
||
- **PostgreSQL** 14 with **TimescaleDB** extension for time-series optimization.
|
||
- **databases** library (async) with **SQLAlchemy Core** for schema definitions and queries.
|
||
- Environment variable: `DATABASE_URL` controls the connection string.
|
||
|
||
### Tables and Hypertable
|
||
1. **telemetry_events** (hypertable)
|
||
- Columns:
|
||
- `id`: Integer, primary key.
|
||
- `character_name` (String), `char_tag` (String, nullable), `session_id` (String, indexed).
|
||
- `timestamp` (DateTime with TZ, indexed) — partitioning column for the hypertable.
|
||
- `ew`, `ns`, `z`: Float coordinates.
|
||
- `kills`, `deaths`, `rares_found`, `prismatic_taper_count`: Integer metrics.
|
||
- `kills_per_hour`, `onlinetime` (String), `vt_state` (String).
|
||
- Optional: `mem_mb`, `cpu_pct`, `mem_handles`, `latency_ms`.
|
||
- Created via `SELECT create_hypertable('telemetry_events', 'timestamp', if_not_exists=>true, create_default_indexes=>false)`.
|
||
|
||
2. **char_stats**
|
||
- Tracks cumulative kills per character.
|
||
- Columns: `character_name` (PK), `total_kills` (Integer).
|
||
|
||
3. **rare_stats**
|
||
- Tracks total rare spawns per character.
|
||
- Columns: `character_name` (PK), `total_rares` (Integer).
|
||
|
||
4. **rare_stats_sessions**
|
||
- Tracks rarities per session.
|
||
- Columns: composite PK `(character_name, session_id)`, `session_rares` (Integer).
|
||
|
||
5. **spawn_events**
|
||
- Records individual mob spawn events for heatmapping.
|
||
- Columns: `id` (PK), `character_name` (String), `mob` (String), `timestamp` (DateTime), `ew`, `ns`, `z` (Float).
|
||
- Coordinates (`ew`, `ns`, `z`) can be sent as JSON numbers or strings and are coerced to floats.
|
||
|
||
6. **rare_events**
|
||
- Records each rare spawn event for future heatmaps and analysis.
|
||
- Columns: `id` (PK), `character_name` (String), `name` (String), `timestamp` (DateTime), `ew`, `ns`, `z` (Float).
|
||
|
||
### Initialization and Migrations
|
||
- On startup (`main.py`), `init_db_async()` is called:
|
||
1. Creates all tables via SQLAlchemy’s `metadata.create_all()`.
|
||
2. Enables TimescaleDB extension.
|
||
3. Converts `telemetry_events` to a hypertable, skipping default index creation to avoid PK/index collisions.
|
||
- Alembic is configured for schema migrations (`alembic/` directory).
|
||
|
||
## Data Ingestion Flow
|
||
1. **Plugin** connects to `/ws/position` with a shared secret.
|
||
2. Sends JSON frames of types:
|
||
- `telemetry`: parsed into `TelemetrySnapshot`, upserted into `live_snapshots`, persisted to `telemetry_events`, and broadcast to browser clients.
|
||
- `spawn`: parsed into `SpawnEvent`, inserted into `spawn_events`.
|
||
- `rare`: increments `rare_stats` and `rare_stats_sessions` via upsert operations.
|
||
- `chat`: broadcast to browser clients without DB writes.
|
||
3. **Browser** connects to `/ws/live` to receive live updates and can send commands to plugins.
|
||
|
||
## HTTP Query Endpoints
|
||
- **GET /live**: returns recent snapshots (last 30s) plus rare counts per character.
|
||
- **GET /history**: returns ordered telemetry history with optional time filters.
|
||
- **GET /trails**: returns positional trails for a lookback window.
|
||
|
||
This architecture enables real-time telemetry ingestion, historical time-series analysis, and an interactive front-end map for tracking players and spawn events. |