feat: add compatibility table endpoint and rendering for game libraries
This commit is contained in:
@@ -15,7 +15,7 @@ from fastapi.staticfiles import StaticFiles
|
||||
|
||||
from .. import __version__
|
||||
from .db import configure, init_db
|
||||
from .routes import diff, games, jobs, snapshots
|
||||
from .routes import compat, diff, games, jobs, snapshots
|
||||
|
||||
_STATIC = Path(__file__).parent / "static"
|
||||
|
||||
@@ -29,6 +29,7 @@ def create_app(database_url: str | None = None) -> FastAPI:
|
||||
app.include_router(snapshots.router)
|
||||
app.include_router(diff.router)
|
||||
app.include_router(jobs.router)
|
||||
app.include_router(compat.router)
|
||||
|
||||
@app.get("/health", tags=["meta"])
|
||||
def health() -> dict[str, str]:
|
||||
|
||||
49
ams/api/routes/compat.py
Normal file
49
ams/api/routes/compat.py
Normal file
@@ -0,0 +1,49 @@
|
||||
"""Catalog-wide compatibility table, grouped by library name, as a downloadable file.
|
||||
|
||||
GET /compat?format=md -> Markdown table (attachment compat.md)
|
||||
GET /compat?format=json -> structured JSON (attachment compat.json)
|
||||
|
||||
Builds `ams.compat` entries from every catalogued snapshot, labelling each by its game (or
|
||||
'(bez gry)' when unlinked). Same engine library name across games clusters into surface variants,
|
||||
so divergent copies get an asterisk — exactly what the docs table needs.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from fastapi import APIRouter, Depends, Query
|
||||
from fastapi.responses import Response
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from ...compat import Entry, compatibility_table, render_json, render_markdown
|
||||
from ...snapshot import Snapshot as Surface
|
||||
from .. import models
|
||||
from ..db import get_db
|
||||
|
||||
router = APIRouter(tags=["compat"])
|
||||
|
||||
_NO_GAME = "(bez gry)"
|
||||
|
||||
|
||||
@router.get("/compat")
|
||||
def get_compat(
|
||||
format: str = Query("md", pattern="^(md|json)$", description="md = Markdown, json = structured"),
|
||||
db: Session = Depends(get_db),
|
||||
) -> Response:
|
||||
entries = [
|
||||
Entry(game=snap.game.name if snap.game else _NO_GAME, snapshot=Surface(snap.data))
|
||||
for snap in db.scalars(select(models.Snapshot).order_by(models.Snapshot.id))
|
||||
]
|
||||
table = compatibility_table(entries)
|
||||
|
||||
if format == "json":
|
||||
return Response(
|
||||
content=render_json(table),
|
||||
media_type="application/json",
|
||||
headers={"Content-Disposition": 'attachment; filename="compat.json"'},
|
||||
)
|
||||
return Response(
|
||||
content=render_markdown(table),
|
||||
media_type="text/markdown; charset=utf-8",
|
||||
headers={"Content-Disposition": 'attachment; filename="compat.md"'},
|
||||
)
|
||||
@@ -16,7 +16,11 @@
|
||||
<aside id="sidebar" class="sidebar">
|
||||
<div class="panel-title">
|
||||
Gry / wersje
|
||||
<button id="upload-toggle" class="mini-btn" title="Wgraj grę (ISO / ZIP / DLL)">+ wgraj</button>
|
||||
<span class="panel-actions">
|
||||
<a id="dl-compat" class="mini-btn" href="/compat?format=md" download="compat.md"
|
||||
title="Pobierz tablicę kompatybilności (Markdown) — grupowaną po nazwie biblioteki">⬇ kompat</a>
|
||||
<button id="upload-toggle" class="mini-btn" title="Wgraj grę (ISO / ZIP / DLL)">+ wgraj</button>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<form id="upload-form" class="upload" hidden>
|
||||
|
||||
@@ -19,9 +19,10 @@ body { background: var(--bg); color: var(--fg); font: 13px/1.45 var(--mono); }
|
||||
letter-spacing: 1px; position: sticky; top: 0; background: var(--panel); border-bottom: 1px solid var(--border); }
|
||||
|
||||
.panel-title { display: flex; align-items: center; justify-content: space-between; }
|
||||
.panel-actions { display: flex; gap: 6px; }
|
||||
.mini-btn { background: #16202c; border: 1px solid var(--border); color: var(--accent);
|
||||
border-radius: 5px; padding: 2px 8px; cursor: pointer; font-family: var(--mono); font-size: 11px;
|
||||
letter-spacing: 0; text-transform: none; }
|
||||
letter-spacing: 0; text-transform: none; text-decoration: none; display: inline-block; }
|
||||
.mini-btn:hover { border-color: var(--accent); }
|
||||
|
||||
.upload { display: flex; flex-direction: column; gap: 7px; padding: 10px 12px;
|
||||
|
||||
Reference in New Issue
Block a user