CI Health Check & Quality Gate / Code, dbt & Live App Health Check (pull_request) Successful in 1m40s
10 KiB
10 KiB
Codebase Context — Streamlit_NBA
Updated: 2026-10-27
Overview
Streamlit_NBA is an end-to-end NBA analytics data warehouse and interactive visualization platform. It extracts raw game, team, and player box score and shot telemetry from PostgreSQL into DuckDB, transforms and models the data through a 3-layer medallion star schema (staging views → intermediate tables → dimension/incremental fact marts) using dbt-duckdb, and serves analytical insights via a 7-page Streamlit application and an Evidence BI report suite. Production DuckDB database artifacts are synced to and from Cloudflare R2 / S3 object storage with automated CI/CD and daily scheduled ETL builds orchestrated through Gitea Actions.
Stack
- Languages: Python 3.12 (pinned in
pyproject.toml:4,Dockerfile:1), SQL / Jinja (dbt SQL dialect for DuckDB), JavaScript / Markdown (Evidence BI) - Data Warehouse & Transformation: dbt (
dbt-duckdb, dbt Core 1.12.5, dbt adapter 1.11.0 inpyproject.toml:6), DuckDB (duckdb>=1.3.0inpyproject.toml:8),dbt-labs/dbt_utils1.3.1 (dbt_nba/packages.yml:2-3) - Dashboards & Visualization: Streamlit (
streamlit>=1.45.1inpyproject.toml:7), Plotly (plotly>=6.1.2inpyproject.toml:9),streamlit-echarts>=0.4.0(pyproject.toml:10), Evidence BI (@evidence-dev/evidence: ^40.1.8indbt_nba/reports/package.json:20) - Storage & Infrastructure: Cloudflare R2 / S3-compatible Object Storage via
boto3>=1.34.0(pyproject.toml:11,scripts/db_storage.py:33-53), Docker (Dockerfile:1-36) - Package & Runtime Management:
uv(pyproject.toml,uv.lock), npm (dbt_nba/reports/package-lock.json) - CI/CD: Gitea Actions (
.gitea/workflows/pipeline.yaml,.gitea/workflows/test.yml)
Layout
streamlit_app/: Frontend application code (streamlit_app/app.py:1-1464containing all 7 analytical views and navigation).dbt_nba/: Core dbt transformation project:models/staging/: 10 views cleaning raw extracts and deriving dynamic metric thresholds.models/intermediate/: 4 tables performing entity resolution, team abbreviation conforming, and performance joins.models/marts/dimensions/: 7 dimension tables (arenas, dates, player archetypes, players, seasons, shot zones, teams).models/marts/facts/: 6 incremental fact tables with 30-day idempotent refresh windows.seeds/: Reference CSVs for team and arena name/abbreviation history across seasons (seeds/team_maps.csv,seeds/arena_maps.csv).reports/: Evidence BI frontend project (Node.js/npm) with DuckDB connector.
scripts/: Operational automation:pipeline.sh: Full extraction from Postgres, dbt rebuild, and R2 artifact upload.extract.sql: DuckDB script attaching to Postgres and pulling source tables intomain.*.db_storage.py: CLI manager for R2 database upload, download, and hash/status checking.run_app.sh: Streamlit launcher with auto-download fallback from R2.
data/: Local storage directory for DuckDB database files (data/DB/dbt_nba.duckdb) and court assets (shot_chart.png,shot_chart.svg)..gitea/workflows/: CI/CD definitions for scheduled daily ETL (pipeline.yaml) and quality checks (test.yml).docs/: Historical documentation, prompts, and tournament records (docs/init_prompt.md,docs/all_nba_cup_games_history.md).
Architecture
- Raw Ingestion Layer: DuckDB PostgreSQL extension (
scripts/extract.sql:5-26) attaches to the live PostgreSQL source (POSTGRES_URL) and copies 7 tables (games,line_scores,player_game_basic_stats,player_game_adv_stats,player_shot_charts,team_game_basic_stats,team_game_adv_stats) intomainschema. - Staging Layer (Views):
dbt_nba/dbt_project.yml:22-25materializes models intomain_staging. Includes percentile breakpoints (dbt_nba/models/staging/stg_season_thresholds.sql) and dynamic tiering (dbt_nba/models/staging/stg_player_game_adv_stats_extended.sql:1-40). - Intermediate Layer (Tables):
dbt_nba/dbt_project.yml:27-30materializes intomain_intermediate. Handles temporal team mapping (dbt_nba/models/intermediate/int_games_enriched.sql:15-35) and unification of field goal shots with box-score free throws (dbt_nba/models/intermediate/int_player_shots_enriched.sql:1-50). - Marts Layer (Star Schema):
dbt_nba/dbt_project.yml:32-44materializes intomain_marts:- 7 Dimensions:
dim_teams,dim_players,dim_seasons,dim_dates,dim_arenas,dim_shot_zones,dim_player_game_archetypes. - 6 Facts (Incremental): Surrogate keys generated via
dbt_utils.generate_surrogate_key(dbt_nba/models/marts/facts/fct_player_game_stats.sql:10-25), using 30-day lookback windows for idempotency.
- 7 Dimensions:
- Application & Presentation:
streamlit_app/app.py:70-83:get_connection()establishes persistent read-only connection todbt_nba.duckdb; queries cached via@st.cache_data(ttl=3600).- Seven dashboard modules: Overview (
app.py:151), Teams (app.py:297), Player Lab (app.py:493), Shot Charts (app.py:769), Head to Head (app.py:1052), Game Trends (app.py:1231), Quarter Analysis (app.py:1364).
Entrypoints & flows
- Daily Automated ETL:
- Triggered via Gitea Actions cron
0 6 * * *(.gitea/workflows/pipeline.yaml:4-6). - Runs
scripts/pipeline.sh(scripts/pipeline.sh:22-45). - Pulls Postgres source into
data/DB/dbt_nba.duckdbvia DuckDB CLI (scripts/extract.sql:18-24). - Executes
dbt depsanddbt build --full-refresh(scripts/pipeline.sh:33-34). - Copies DB to
dbt_nba/reports/sources/nba/dbt_nba.duckdbfor Evidence BI (scripts/pipeline.sh:37). - Uploads the finalized DuckDB database file to Cloudflare R2 bucket via
scripts/db_storage.py upload(scripts/pipeline.sh:41).
- Triggered via Gitea Actions cron
- Local Application Startup:
- User/container launches
./scripts/run_app.shor Docker container entrypoint (Dockerfile:36). - Verifies local DuckDB file; if missing or if
--syncflag is provided, downloads latest snapshot from Cloudflare R2 (scripts/run_app.sh:23-38). - Launches Streamlit application (
streamlit run streamlit_app/app.py).
- User/container launches
Data & integrations
- Source Database: PostgreSQL database (
POSTGRES_URLin.env), read-only extraction. - Analytical Storage: DuckDB (
dbt_nba.duckdb) containing 6 schemas:main(raw tables),main_staging(views),main_intermediate(tables),main_marts(star schema),main_seeds(reference CSVs),main_test_results(audit tables). - Object Storage: Cloudflare R2 (or any S3-compatible store: SeaweedFS, MinIO, Hetzner S3) configured via
S3_ENDPOINT_URL,S3_BUCKET_NAME,S3_ACCESS_KEY_ID,S3_SECRET_ACCESS_KEY,S3_REGION,S3_DB_KEYin.env. - Authentication: Zero authentication on public Streamlit app; API/DB credentials stored in
.envand Gitea Action secrets.
Commands
- Install dependencies:
uv sync(Python) /npm --prefix dbt_nba/reports install(Evidence BI) - Compile Python scripts:
uv run python -m py_compile streamlit_app/app.py scripts/db_storage.py - Validate dbt models:
uv run dbt parse --project-dir dbt_nba --profiles-dir dbt_nba - Install dbt dependencies:
uv run dbt deps --project-dir dbt_nba --profiles-dir dbt_nba - Run dbt tests:
uv run dbt test --project-dir dbt_nba --profiles-dir dbt_nba - Run full dbt build:
uv run dbt build --project-dir dbt_nba --profiles-dir dbt_nba - Check R2 storage status:
uv run python scripts/db_storage.py status - Download database from R2:
uv run python scripts/db_storage.py download - Upload database to R2:
uv run python scripts/db_storage.py upload - Run Streamlit app:
./scripts/run_app.sh(oruv run streamlit run streamlit_app/app.py) - Run full ETL pipeline:
./scripts/pipeline.sh - Build Docker image:
docker build -t streamlit-nba .
Conventions
- Team Abbreviation Conforming: Historical franchise relocations and name changes handled via
dbt_nba/seeds/team_maps.csvusingstart_year/end_yearfiltering in intermediate SQL models. - dbt Testing & Severity: Schema tests configured with
severity: warnandstore_failures: truestoring failures intomain_test_results(dbt_nba/dbt_project.yml:45-50). - Branch & Commit Strategy: Git branches
main(production deployment) anddev(active development); conventional commit prefixes (feat:,fix:,ci:). Current working branch:update. - Streamlit Caching: Data queries decorated with
@st.cache_data(ttl=3600), database connection with@st.cache_resource(streamlit_app/app.py:70-83).
Testing
- dbt Schema & Data Tests: 38 data tests defined across models in
dbt_nba/models/schema.ymland_int_models.yml(testing unique constraints, non-null values, referential integrity). Executed viauv run dbt test --project-dir dbt_nba --profiles-dir dbt_nba. - CI Quality Gate:
.gitea/workflows/test.yml:11-75runs on every push and pull request tomainanddev:- Syntax & bytecode verification (
python -m py_compile). - dbt package and manifest validation (
dbt parse). - Cloudflare R2 storage reachability probe.
- Live production app endpoint healthcheck (
https://streamlit.turbo-data.com/_stcore/health).
- Syntax & bytecode verification (
Open questions
- Postgres Source Availability for Local Dev: Local execution of
scripts/pipeline.shrequires livePOSTGRES_URL; developers without Postgres access rely on pre-built DuckDB downloads from Cloudflare R2 viadb_storage.py download. - Evidence BI Production Deployment: Evidence project in
dbt_nba/reportshas build scripts (npm run build) and queries local DuckDB, but whether it is independently hosted alongside Streamlit or used strictly for static export is not defined in Gitea workflows.
Starting points
- Adding or modifying a Streamlit chart/page: Start in
streamlit_app/app.pyaround lines 150-1464; check available columns inmain_martsormain_intermediate. - Adding or updating a dbt metric / table: Start in
dbt_nba/models/; update corresponding.ymltest definitions indbt_nba/models/schema.ymlor_int_models.yml. - Fixing team or franchise mappings: Edit
dbt_nba/seeds/team_maps.csvorarena_maps.csv, then rundbt seed. - Adjusting ETL pipeline or object storage: Inspect
scripts/pipeline.shandscripts/db_storage.py. - Modifying CI/CD workflows: Inspect
.gitea/workflows/pipeline.yaml(daily pipeline) andtest.yml(health check).