Skip to content

Latest commit

 

History

History
184 lines (147 loc) · 10.7 KB

File metadata and controls

184 lines (147 loc) · 10.7 KB

GW2Analytics

CI Migration test Security scan Docker build Cache warmup codecov Latest tag

Modern combat analytics platform for Guild Wars 2 WvW (World vs World).

Independent third-party platform — no dps.report, no Elite Insights web. WvW combat logs (.zevtc) are parsed locally and stored in a stable internal model from which all analytics, API, and frontend derive.

What's new

Release Highlights
v0.15.0 GlobalStatsBar, timeline activity toggle, CSV removal, heatmap in Analyse, arq-worker, tab fix, unit tests.
v0.14.3 PlayerPositionHeatmap canvas component + presence_pct math fix.
v0.14.2 Plan 173: 14 boon uptimes + presence % + 14 outgoing boons in Combat Readout. Grouped bars + tooltips.
v0.14.1 Slow-path blob walk tests (3 hermetic tests for _contributions_from_blob_walk).
v0.14.0 CI guard against legacy db.query() reintroduction. Coverage plan updated.
v0.13.9 Zero legacy SQLAlchemy queries — last db.query() migrated to select().
v0.13.8 backfill_role_detection tests (67%→85%). Redundant limiter.reset() cleanup.
v0.13.7 Rate limiting integration tests. Guilds route tests (0%→70%).
v0.13.6 Players route coverage (30%→69%). Guilds router mounted in main.py.

See CHANGELOG.md for the full per-release history.

Highlights

  • 🎯 Per-target / per-subgroup / per-skill roll-ups on every fight — DPS, healing, and buff removals via stable pydantic aggregations with deterministic ordering + cross-field invariants.
  • 📈 Account-level historical timelines — per-day / per-fight bucketing, linear / log Y-axis, and player-name resolution on the fight drilldown's TargetFilter.
  • 🔌 Webhook subscriptions for parse-completion notifications — HMAC-SHA256 signed, 3-attempt retry + DLQ + replay, with SSRF block (HTTPS-only + universal private-IP gate).
  • 🎭 Heuristic role detection — per-(fight, account) DPS / HEAL / STRIP / BOON / MIXED classification from the 3 magnitudes + spec/profession hint table.
  • 📊 Per-player timeline overlay — one per-bucket series per player agent for multi-line chart overlays.
  • 🎨 GW2Mists-inspired frontend — dark palette, sticky glass header, inline SVG logo, favicon, and Next.js <Link> navigation.
  • ⚔️ Combat-readout UI — per-player Damage / Heal / Boons / Defense 4-table roll-up via /fights/[id]?tab=readout (default tab), with native HTML sortable tables, boon In/Out columns (14 boons), GlobalStatsBar (squad DPS/Heal/Strips/Cleanses/CC/Healers/Supports), compact FightSummaryCards (Top 3 per category), timeline activity toggle ("Toute la durée" / "Activité seulement"), and 2D position heatmap with play/pause animation.
  • 🧪 Comprehensive multi-layer test suite — pytest (libs + apps) + vitest (web components) + Playwright e2e (web flows), 117 tests, 84% coverage, all gated and green.
  • 🛡️ Audit hardening — Caddyfile HSTS/CSP, CI pip-audit/pnpm-audit, Next.js error boundaries, headers() defense-in-depth, Trivy filesystem scan + detect-secrets pre-commit hook.
  • 📊 Observability — OpenTelemetry tracing (FastAPI + SQLAlchemy + Redis) with OTLP HTTP export, structured JSON logging, Prometheus metrics endpoint, Grafana dashboard.
  • 📦 Pure monorepo — libs/gw2_core (no I/O), libs/gw2_evtc_parser (replaceable Protocol), libs/gw2_analytics (frozen pydantic), apps/api (FastAPI), web (Next.js).
  • 🔧 Zero legacy SQLAlchemy — all production queries use select() (SQLAlchemy 2.x style). CI guard prevents regression.
  • ⚡ Arq worker — dedicated background worker process for .zevtc parsing, eliminating in-request GIL contention on parallel uploads.

Architecture

                               gw2_evtc_parser
                                      │
                                      ▼ produces
                                   gw2_core ◀──constrains── gw2_analytics
                                      │
                                      ▼
                                 apps/api  ──gw2_api_client── GW2 v2
                                      │
                                      ▼
                                 web (Next.js)
Component Role
libs/gw2_core Stable Pydantic models (combat + API). Single source of truth. No I/O.
libs/gw2_evtc_parser Binary .zevtc parser behind an EvtcParser Protocol. V1.3 layout.
libs/gw2_analytics Single-, multi-fight, and event-driven aggregations. Frozen pydantic shapes with deterministic ordering + cross-field invariants.
libs/gw2_api_client Typed async httpx wrapper for the Guild Wars 2 REST API v2.
apps/api FastAPI gateway. MinIO blobs + Alembic + Postgres.
web Next.js frontend. AG Grid Community tables + SSR fetches.

API Surface

Method Path Description
POST /api/v1/uploads Ingest a .zevtc log; returns 201 (parse runs in background).
GET /api/v1/uploads/{id} Upload metadata.
GET /api/v1/fights[/{id}] List fights (paginated) or fetch a single fight.
GET /api/v1/fights/{id}/events Per-target trio (DPS + healing + buff removal) + per-bucket event windows.
GET /api/v1/fights/{id}/squads Per-subgroup roll-up.
GET /api/v1/fights/{id}/skills Per-skill hit count + damage / heal / strip totals.
GET /api/v1/fights/{id}/readout Combat readout — per-player Damage / Heal / Boons / Defense 4-table roll-up.
GET /api/v1/fights/{id}/timeline?window_s=N Per-fight temporal view (3-series, M:SS relative time).
GET /api/v1/fights/{id}/timeline/players Per-player timeline overlay.
GET /api/v1/fights/{id}/players/{account}/skills Per-player skill roll-up + loadout.
GET /api/v1/players?profession=&limit=&offset= Cross-fight player roll-up (paginated).
GET /api/v1/players/{account_name:path} Player profile + per-fight breakdown.
GET /api/v1/players/{account_name:path}/timeline Account-level historical timeline.
POST/GET/DELETE /api/v1/webhooks[/{id}] Webhook subscription management (HTTPS-only URLs).
GET /api/v1/health/summary Operational drift probe.
GET /api/v1/healthz Liveness probe.

Screenshots

Route Capture
/ Landing
/upload Upload flow
/fights Fights grid
/players Players grid
/players/[account_name] Player profile
/fights/[id]?tab=replay Replay drilldown

Quickstart

# 1. Install uv (Python package manager)
curl -LsSf https://astral.sh/uv/install.sh | sh

# 2. Install all monorepo deps including libs + apps
uv sync

# 3. Install git hooks
uv run pre-commit install

# 4. Bring up the infra (Postgres + MinIO + Redis + arq-worker)
#    The arq-worker handles .zevtc parsing in a dedicated background
#    process with its own GIL, avoiding in-request latency.
docker compose up -d

#    If the infra is already running from a previous version, start
#    just the new arq-worker:
#    docker compose up -d arq-worker

# 5. Configure local app env (DB + S3 creds; never commit the real .env)
cp .env.example .env

# 6. Apply the Postgres schema
cd apps/api && uv run alembic upgrade head && cd ../..

# 7. Boot the API (http://localhost:8000/docs)
uv run fastapi dev apps/api/src/gw2analytics_api/main.py

# 8. Frontend
cd web
pnpm install
pnpm dev   # http://localhost:3000

Development

# Run all backend checks (lint + typecheck + tests)
uv run ruff check libs apps
uv run pytest libs apps -q

# Run all frontend checks (typecheck + lint + tests)
cd web
pnpm typecheck && pnpm lint && pnpm test:unit

Documentation

File Purpose
CHANGELOG.md Canonical per-commit history.
CONTRIBUTING.md Workflow conventions, branch protection rules, CI gates.
docs/ROADMAP.md Forward-looking candidates and technical-debt ledger.
monitoring/grafana-dashboard.json Pre-built Grafana dashboard (upload rate, parse duration, errors, queue, drift).
plans/README.md Senior-advisor audit trails and scoped cycle implementation plans.

Contributing

See CONTRIBUTING.md for branch protection, pre-commit, code style, and test requirements.

Principles

  1. gw2_core is the only contract between layers. Everything depends on it; it depends on nothing but Pydantic.
  2. The parser is replaceable behind the EvtcParser Protocol. Swap Python for Rust + PyO3 with zero churn elsewhere.
  3. The frontend never knows about EVTC, parser internals, or DB schema — only the OpenAPI surface.
  4. Each component evolves independently — enforced by pyproject.toml per lib.

License

Proprietary software — all rights reserved. Copyright (c) 2024-2026 Roddy. See LICENSE for the full legal text and NOTICE.md for a plain-language summary of what is and isn't permitted without explicit written permission from the copyright holder.

The project is independent third-party software — no affiliation with ArenaNet or any Guild Wars 2 trademark holder. All Guild Wars 2 game content references are nominative fair use under the GW2 content policy.