Skip to main content

Acquisition, normalization, versioning, and persistence for Riftbound data.

Project description

riftbound-database

riftbound-database est la librairie d'acquisition, de normalisation, d'historisation et de persistance des données Riftbound.

Le projet suit cette direction de dépendances :

riftbound-core
    ↑ utilisé par
riftbound-database
    ↓ utilise
baobab-database

Le modèle métier canonique appartient à riftbound-core. Ce dépôt ne doit pas redéfinir les cartes, sets, domaines, types, tags, coûts ou textes de carte. L'accès aux moteurs, sessions et transactions SQLAlchemy sera encapsulé derrière un adaptateur local de baobab-database.

Prérequis

  • Python 3.12 ou 3.13 ;
  • PostgreSQL pour l'intégration, SQLite étant réservé aux tests rapides.

Installation de développement

python -m venv .venv
python -m pip install --upgrade pip
python -m pip install -e ".[dev]"

La dépendance riftbound-core est résolue depuis PyPI (>=0.1.1,<0.2).

Configuration

Variable Obligatoire Valeur par défaut Rôle
RIFTBOUND_DATABASE_URL Non au bootstrap aucune URL PostgreSQL ; obligatoire en production et pour l'API
RIFTBOUND_STORAGE_PATH Non storage Racine du stockage local
RIFTBOUND_LOG_LEVEL Non INFO Niveau de logs Python
RIFTBOUND_ENVIRONMENT Non development Nom de l'environnement d'exécution
RIFTBOUND_TEST_POSTGRESQL_URL Tests E2E aucune URL PostgreSQL pour pytest -m postgresql

Les secrets éventuellement présents dans RIFTBOUND_DATABASE_URL ne doivent jamais être écrits dans les logs. Les fichiers .env sont ignorés par Git.

Provider de base de données

Les moteurs, sessions et transactions passent exclusivement par riftbound_database.database.BaobabProvider. PostgreSQL utilise baobab-database 2.0.0 avec le driver psycopg; SQLite est limité aux tests sur fichier temporaire.

L'adaptation complète et les options SSL supportées sont documentées dans docs/database-provider.md.

Modèles et migrations

Les modèles SQLAlchemy vivent sous riftbound_database.database.models. Les mappers domaine ↔ persistance sont sous database/mappers. Alembic est configuré via alembic.ini et exécutable avec :

python -m alembic upgrade head

Pour les tests locaux sans PostgreSQL, définir RIFTBOUND_MIGRATION_SQLITE_PATH ou laisser Alembic utiliser la base SQLite par défaut sous .my_cache/.

Repositories

Les écritures applicatives passent par RiftboundUnitOfWork, qui expose les repositories sans créer de session locale. Les retours sont limités aux objets riftbound-core ou aux read models sous database/read_models/.

from riftbound_database.database.baobab_provider import BaobabProvider
from riftbound_database.database.unit_of_work import RiftboundUnitOfWork

provider = BaobabProvider.for_sqlite("test.db")
unit_of_work = RiftboundUnitOfWork(provider)
with unit_of_work.transaction() as repositories:
    card = repositories.cards.find_by_stable_id("card-001")

Collections utilisateur et decks (v1.0.0)

La persistance applicative pour riftbound-api est disponible via les mêmes repositories transactionnels :

with unit_of_work.transaction() as repositories:
    collection = repositories.collections.get_or_create_default_collection(user_id)
    deck = repositories.decks.create_deck(
        user_id,
        DeckCreatePayload(name="Competitive", visibility="private"),
    )

Guide complet : docs/collections-decks.md (tables, migrations, intégration API, remplacement des adaptateurs mémoire).

Ingestion

Les imports passent par BaseImporter, qui orchestre fetch, stockage raw, checksum, batch et persistance via les repositories. SetImporter importe des fichiers JSON locaux de façon idempotente avec normalisation, checksum canonique et stockage sous normalized_data/sets/. CardNormalizer produit des DTO canoniques compatibles avec riftbound-core, calcule un checksum stable et signale les champs ambigus via le statut needs_review. Les importers cartes (LocalFileCardImporter, OfficialApiCardImporter, CardGalleryImporter) normalisent, versionnent et persistent les cartes via les repositories. Les documents normatifs passent par RulesImporter, ErrataImporter et BanlistImporter avec normalisation, checksums stables, historisation (rules_versions, snapshots banlist) et statuts matched / ambiguous / needs_review. Un FakeImporter est disponible pour les tests.

Assets

Les images de cartes sont téléchargées par CardAssetDownloader, validées (MIME, taille minimale, signature binaire) puis stockées sous {RIFTBOUND_STORAGE_PATH}/card_assets/{set_code}/{card_id}/ avec un metadata.json par carte. La persistance relationnelle passe par AssetRepository ; un checksum inchangé évite les écritures inutiles.

from riftbound_database.assets import AssetDownloadRequest, AssetVariant, CardAssetDownloader
from riftbound_database.config.settings import Settings
from riftbound_database.database.baobab_provider import BaobabProvider
from riftbound_database.database.unit_of_work import RiftboundUnitOfWork

settings = Settings()
downloader = CardAssetDownloader()
provider = BaobabProvider.for_sqlite("test.db")
unit_of_work = RiftboundUnitOfWork(provider)
request = AssetDownloadRequest(
    set_code="OGN",
    card_id="card-unit-001",
    variant=AssetVariant.ORIGINAL,
    source_url="https://example.test/card.png",
)
with unit_of_work.transaction() as repositories:
    result = downloader.download(request, settings, repositories.assets)

Synchronisation

Les jobs sous riftbound_database.sync orchestrent les importers via un SyncManifest JSON (ADR-0002) : chemins locaux + liste assets[] avec URLs source explicites. FullSyncJob exécute dans l'ordre sets → cartes → assets → règles → errata → banlists, avec dry_run et SyncReport final.

from riftbound_database.sync import FullSyncJob, SyncContext, SyncManifestLoader

manifest = SyncManifestLoader.load(Path("sync.json"))
context = SyncContext(settings=settings, unit_of_work=unit_of_work, manifest=manifest)
report = FullSyncJob().run(context)

CLI

La CLI Typer est exposée via le script riftbound-database (ou python -m riftbound_database.cli.main). Options globales : --database-url, --sqlite-path, --storage-path, --log-level.

riftbound-database --sqlite-path ./local.db db upgrade
riftbound-database --sqlite-path ./local.db import sets --path ./sets.json
riftbound-database --sqlite-path ./local.db import cards --source local-file --path ./cards.json
riftbound-database --sqlite-path ./local.db import rules --path ./rules.json
riftbound-database --sqlite-path ./local.db import errata --path ./errata.json
riftbound-database --sqlite-path ./local.db import banlist --path ./banlist.json
riftbound-database --sqlite-path ./local.db assets download --manifest ./sync.json
riftbound-database --sqlite-path ./local.db sync full --manifest ./sync.json
riftbound-database checksums verify
riftbound-database export normalized --format json

Les imports supportent --dry-run et --limit. Les commandes retournent un code de sortie non nul en cas d'échec ; les secrets de connexion ne sont jamais loggés.

Qualité

python -m pytest
python -m coverage run -m pytest
python -m coverage report
python -m ruff check .
python -m mypy src tests
python -m bandit -r src

Tests rapides vs intégration

# PostgreSQL local via Docker (optionnel)
docker compose up -d postgres
$env:RIFTBOUND_TEST_POSTGRESQL_URL="postgresql+psycopg://riftbound:riftbound@localhost:5432/riftbound_test"

# unitaires (CI par défaut)
python -m pytest -m "not integration and not postgresql"

# migrations SQLite
python -m pytest tests/integration/riftbound_database/database/migrations/

# PostgreSQL (service local requis)
set RIFTBOUND_TEST_POSTGRESQL_URL=postgresql+psycopg://user:pass@localhost:5432/riftbound_test
python -m pytest -m postgresql

# E2E collections/decks PostgreSQL uniquement
python -m pytest tests/integration/riftbound_database/database/test_collections_decks_e2e_postgresql.py -q

# tests contractuels (BL-027)
python -m pytest -m contract -q

Voir docs/contract-tests.md, docs/e2e-postgresql.md et docs/collections-decks.md.

Couverture minimale configurée : 85 % (pyproject.toml).

CI GitHub Actions

Le workflow .github/workflows/ci.yml exécute sur chaque push/PR vers main :

  • quality : ruff, mypy, bandit ;
  • unit : pytest + coverage (Python 3.12 et 3.13) ;
  • integration : migrations SQLite + test PostgreSQL via service container.

Les dépendances (riftbound-core, baobab-database) sont installées depuis PyPI ; aucun secret GitHub n'est requis pour la CI si tous les packages restent publics.

Documentation projet

Le cahier des charges et le découpage fonctionnel sont disponibles dans docs/.

Guides opérationnels :

Le catalogue initial a été livré via les backlogs BL-001 à BL-014. L'épopée collections/decks (BL-015BL-029) clôt la version 1.0.0.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

riftbound_database-1.0.0.tar.gz (261.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

riftbound_database-1.0.0-py3-none-any.whl (181.7 kB view details)

Uploaded Python 3

File details

Details for the file riftbound_database-1.0.0.tar.gz.

File metadata

  • Download URL: riftbound_database-1.0.0.tar.gz
  • Upload date:
  • Size: 261.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.12

File hashes

Hashes for riftbound_database-1.0.0.tar.gz
Algorithm Hash digest
SHA256 5865f9bc6a912d7225fd0f6311e49e04c73b84cce4c9c9bc97e580349fdd8eb0
MD5 cb5c8e2b6f73b1ef0e94f712fd048203
BLAKE2b-256 d6e533fbb28d769bf8aa45cede807b6b39b28b331d6ed2abd5472ec77fbc888f

See more details on using hashes here.

Provenance

The following attestation bundles were made for riftbound_database-1.0.0.tar.gz:

Publisher: release.yml on baobabgit/riftbound-database

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file riftbound_database-1.0.0-py3-none-any.whl.

File metadata

File hashes

Hashes for riftbound_database-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 329758c6673681cb9238fe47036df97ab661e7249c5304b8facd657a24177b0d
MD5 b52a1c11d3c027285d4692021deeb3cd
BLAKE2b-256 2de46dcdaf82c07598dbe28b388a2128ef5320bf315e2de9930d1b4e6c70f609

See more details on using hashes here.

Provenance

The following attestation bundles were made for riftbound_database-1.0.0-py3-none-any.whl:

Publisher: release.yml on baobabgit/riftbound-database

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page