chore: beta release v0.5.0-beta
CI / test (push) Has been cancelled
CI / docker (push) Has been cancelled

- Bump version to 0.5.0-beta
- Add Apache-2.0 LICENSE (official text) + NOTICE
- Add Docker support: multi-stage Dockerfile + docker-compose
  * non-root user, tini PID 1, gunicorn + uvicorn workers
  * healthcheck, OCI labels, persistent /data volume
- Add GitHub Actions CI (lint/import-check + Docker smoke-test)
- Backend polish:
  * modern lifespan handler (replaces deprecated on_event)
  * explicit sort validation (400 instead of 500)
  * title min_length=1 schema validation
  * configurable data dir + http timeout via env
  * avg_rating helper extracted (DRY)
- Frontend polish:
  * beta badge in title, footer with live version
  * loading spinner on initial fetch
  * updated README, badges, config table, roadmap
This commit is contained in:
ki
2026-07-21 23:34:01 +02:00
parent 56a8d5d302
commit 0bca2eaaa2
19 changed files with 736 additions and 124 deletions
+11 -9
View File
@@ -1,4 +1,5 @@
"""Datenbank-Operationen (CRUD) für WatchStack."""
# SPDX-License-Identifier: Apache-2.0
from __future__ import annotations
from typing import Iterable, Optional, Sequence
@@ -166,23 +167,24 @@ def bump_progress(db: Session, media: models.Media, payload: schemas.ProgressUpd
# ---------- Statistik --------------------------------------------------------
def _avg(values: Sequence[Optional[float]]) -> Optional[float]:
"""Mittelwert ohne None, gerundet auf 2 Stellen; None wenn leer."""
rated = [v for v in values if v is not None]
if not rated:
return None
return round(sum(rated) / len(rated), 2)
def stats(db: Session) -> schemas.StatsResponse:
def per_kind(kind: str) -> schemas.StatsPerKind:
items = db.scalars(select(models.Media).where(models.Media.kind == kind)).all()
total = len(items)
avg = (
round(sum(m.rating for m in items if m.rating is not None) /
max(1, sum(1 for m in items if m.rating is not None)), 2)
if any(m.rating is not None for m in items)
else None
)
by_status: dict[str, int] = {s: 0 for s in models.STATUS_VALUES}
for m in items:
by_status[m.status] = by_status.get(m.status, 0) + 1
return schemas.StatsPerKind(
kind=kind,
total=total,
avg_rating=avg,
total=len(items),
avg_rating=_avg([m.rating for m in items]),
by_status=[schemas.StatsBucket(status=k, count=v) for k, v in by_status.items()],
)