feat: harden production data and backups
CI / backend-and-migrations (push) Canceled after 0s
CI / astro-build (push) Canceled after 0s
CI / compose-e2e (push) Canceled after 0s

This commit is contained in:
ik
2026-09-06 14:08:07 +07:00
parent a4bd395856
commit 870d9cc7f9
17 changed files with 215 additions and 36 deletions
+35 -10
View File
@@ -5,6 +5,7 @@ from uuid import UUID
from sqlalchemy import select
from .config import settings
from .database import SessionLocal
from .models import Bait, BaitKind, CatchReport, Fish, ModerationStatus, SourceType, Spot, Waterbody
@@ -23,19 +24,38 @@ IDS = {
def seed() -> None:
with SessionLocal.begin() as db:
if db.scalar(select(Fish.id).limit(1)) is not None:
def entity(model, key: str, value: str, **values):
item = db.scalar(select(model).where(getattr(model, key) == value))
if item is None:
item = model(**values)
db.add(item)
db.flush()
return item
vyunok = entity(Waterbody, "slug", "vyunok", id=IDS["vyunok"], slug="vyunok", name_ru="Вьюнок", unlock_level=1)
kuori = entity(Waterbody, "slug", "kuori", id=IDS["kuori"], slug="kuori", name_ru="Куори", unlock_level=16)
pike = entity(Fish, "slug", "pike", id=IDS["pike"], slug="pike", name_ru="Щука", trophy_weight_g=10_000)
trout = entity(Fish, "slug", "lake-trout", id=IDS["trout"], slug="lake-trout", name_ru="Озёрная форель", trophy_weight_g=10_000)
spiker = entity(Bait, "normalized_name", "spiker #2 01-015", id=IDS["spiker"], name="Spiker #2 01-015", normalized_name="spiker #2 01-015", kind=BaitKind.lure)
shad = entity(Bait, "normalized_name", "salmon t1 shad 12 005", id=IDS["shad"], name="Salmon T1 Shad 12 005", normalized_name="salmon t1 shad 12 005", kind=BaitKind.lure)
def spot(waterbody: Waterbody, identity: str, x: int, y: int, description: str) -> Spot:
item = db.scalar(select(Spot).where(Spot.waterbody_id == waterbody.id, Spot.x == x, Spot.y == y))
if item is None:
item = Spot(id=IDS[identity], waterbody=waterbody, x=x, y=y, description=description)
db.add(item)
db.flush()
return item
spot1 = spot(vyunok, "spot1", 110, 103, "Кромка травы у северного берега")
spot2 = spot(kuori, "spot2", 85, 92, "Свальчик в глубину")
if not settings.seed_demo_data:
return
vyunok = Waterbody(id=IDS["vyunok"], slug="vyunok", name_ru="Вьюнок", unlock_level=1)
kuori = Waterbody(id=IDS["kuori"], slug="kuori", name_ru="Куори", unlock_level=16)
pike = Fish(id=IDS["pike"], slug="pike", name_ru="Щука", trophy_weight_g=10_000)
trout = Fish(id=IDS["trout"], slug="lake-trout", name_ru="Озёрная форель", trophy_weight_g=10_000)
spiker = Bait(id=IDS["spiker"], name="Spiker #2 01-015", normalized_name="spiker #2 01-015", kind=BaitKind.lure)
shad = Bait(id=IDS["shad"], name="Salmon T1 Shad 12 005", normalized_name="salmon t1 shad 12 005", kind=BaitKind.lure)
spot1 = Spot(id=IDS["spot1"], waterbody=vyunok, x=110, y=103, description="Кромка травы у северного берега")
spot2 = Spot(id=IDS["spot2"], waterbody=kuori, x=85, y=92, description="Свальчик в глубину")
db.add_all([vyunok, kuori, pike, trout, spiker, shad, spot1, spot2])
now = datetime.now(timezone.utc)
for index in range(12):
external_id = f"seed:pike:{index}"
if db.scalar(select(CatchReport.id).where(CatchReport.source_external_id == external_id)):
continue
db.add(CatchReport(
fish=pike, spot=spot1, waterbody=vyunok, bait=spiker,
weight_g=2_600 + index * 480, fishing_method="spinning",
@@ -43,9 +63,13 @@ def seed() -> None:
caught_at=now - timedelta(minutes=25 + index * 47),
reported_at=now - timedelta(minutes=20 + index * 47),
player_name=f"DemoPlayer{index % 7 + 1}", source_type=SourceType.manual_import,
source_external_id=external_id,
source_confidence=80 + index % 3 * 5, moderation_status=ModerationStatus.approved,
))
for index in range(5):
external_id = f"seed:trout:{index}"
if db.scalar(select(CatchReport.id).where(CatchReport.source_external_id == external_id)):
continue
db.add(CatchReport(
fish=trout, spot=spot2, waterbody=kuori, bait=shad,
weight_g=4_200 + index * 900, fishing_method="spinning",
@@ -53,6 +77,7 @@ def seed() -> None:
caught_at=now - timedelta(hours=2 + index * 4),
reported_at=now - timedelta(hours=2 + index * 4),
player_name=f"DemoAngler{index + 1}", source_type=SourceType.manual_import,
source_external_id=external_id,
source_confidence=85, moderation_status=ModerationStatus.approved,
))