Welcome. This doc is the front door for anyone new to
PostgreDataMigrationAppWithCSVLoader — what the project does, the two or
three ideas you need before the code makes sense, how to get it running, and
where to go next depending on what you're trying to do.
If you just want the fastest path to a running app, skip to Get it running. Everything else here is context that makes the rest of the docs click.
It's a web app for getting CSV files into PostgreSQL, built for Test &
Evaluation (T&E) programme management. A user drops a CSV in a browser, the
backend validates and loads it, and the row counts / errors show up in the
UI. The browser never talks to PostgreSQL directly — every read and write
goes through the FastAPI backend (api/).
There are three layers, and it helps to know which one you're touching:
| Layer | Where | Answers |
|---|---|---|
| Backend | api/ (FastAPI + psycopg2) |
How is a CSV parsed, validated, and inserted? |
| Frontend | csv-table-hub-main/ (React 19 + TanStack) |
What does the user click, and what do they see? |
| Database | build/ (schema) + infra/ (deployment) |
What does the data look like, and where does it live per environment? |
For the full "every file belongs to exactly one category" breakdown, see
docs/ARCHITECTURE.md.
Every CSV upload picks one of two modes, and most confusion about "why did this table get created" or "why did my upload get rejected" traces back to which mode was used:
-
Dynamic mode — the CSV can be anything. The backend infers column types from the data and auto-creates a new table (
csv_<hash>) to hold it. No schema to match. This is the "just import my spreadsheet" path. Implemented inapi/services/dynamic_loader.py. -
T&E mode — the CSV must match one of the 12 fixed T&E tables (
organisations,test_programs,test_cases,defect_reports, etc. — full list in the mainREADME.md). Columns are validated against that table's schema and inserted row-by-row, with bad rows isolated rather than failing the whole file. This is the "load real programme data into the real schema" path. Implemented inapi/services/te_loader.py.
Both modes go through the same POST /api/csv/upload endpoint, with a
mode: "dynamic" | "te" field distinguishing them — see the Swagger UI at
http://localhost:8000/docs (API running) for the full request/response shapes.
The same codebase runs against four isolated databases — Dev, Test, Staging,
Prod — each with its own database name, schema, and app user (table in the
main README.md under "Multi-Environment Setup"). Staging and Prod are never
seeded with sample data; tests/system_integration/environments/ enforces that automatically.
If you're not sure which environment a command is touching, check
docs/DATABASE_ENVIRONMENTS.md and docs/ENV_VARIABLES.md before running
anything destructive.
Full step-by-step instructions (prerequisites, provisioning the dev
database, starting both servers, verifying end to end) live in
docs/QUICKSTART.md — follow that doc directly rather than a summary
here, since it's the one kept current with exact commands and Windows-specific
gotchas.
The short version, once you've followed QUICKSTART: two terminals running
(.\scripts\start-api.ps1 and .\scripts\start-frontend.ps1), UI open at
http://localhost:8080, API health check green at
http://localhost:8000/api/health.
Once the app is running locally, this sequence exercises both modes and the test suite in about ten minutes — a good way to confirm your setup actually works, not just that servers started.
- Sign in. The app has required authentication since ART Milestone 2, so
http://localhost:8080opens on the sign-in page, not the upload area. Follow QUICKSTART's "Create your sign-in (first run only)" section to create a tenant admin, then sign in with it. - Upload a CSV in dynamic mode. Any small CSV works — three columns,
a few rows. Drop it on the upload area, preview it, confirm the import.
Check the Migrated Files table for the new
csv_<hash>table and row count. - Upload a CSV in T&E mode. Pick one of the 12 T&E tables (e.g.
organisations), match your CSV's headers to its columns, and import. This exercises the validation path dynamic mode skips. - Re-upload the dynamic-mode file from step 2. Confirm duplicate detection kicks in — the upload is rejected with "Duplicate file" on either a matching filename or a matching content hash. Dynamic mode only: a T&E re-upload replaces the previous registry entry instead of refusing, since per-tenant and per-record dedup is still M5 scope (#109).
- Run the fast test suite to confirm the backend is sane:
python -m pytest tests\ -m "unit or regression or security or snapshot" -q
- Run the frontend tests:
cd csv-table-hub-main npm test
If all six pass/succeed, your environment is a faithful match for CI.
| I want to... | Read |
|---|---|
| Set up my dev environment from scratch | docs/QUICKSTART.md |
| Understand the repo's folder structure and why it's split that way | docs/ARCHITECTURE.md |
| See every API route and its request/response shape | http://localhost:8000/docs while the API is running (Swagger UI) |
| Understand how the API is put together, its auth model, known gaps and migrations | docs/API_INTEGRATION.md |
| Look up an environment variable | docs/ENV_VARIABLES.md |
| Understand Dev/Test/Staging/Prod differences | docs/DATABASE_ENVIRONMENTS.md |
| Deploy to Azure | docs/AZURE_DEPLOY.md (non-prod) / docs/PROD_DEPLOY.md (prod) |
| Trace a business requirement to its test coverage | docs/VCRM.md |
| Check known gaps against the VCRM | docs/VCRM_GAPS.md |
| See the project's own standards self-audit | docs/STANDARDS_AUDIT.md |
These aren't optional style preferences — they're enforced by tests or exist because of a specific past incident:
- All SQL identifiers (table/column names) go through
psycopg2.sql.Identifier()— never f-string interpolation. This is AST-checked bytests/unit/test_security.py. - pytest markers matter:
unit,integration,e2e,regression,security,snapshot,parity. Rununit(and friends) for a fast, DB-free sanity check;integrationande2eneed a live, provisioned PostgreSQL. Seepytest.inifor the full marker list. - Staging and Prod are never seeded — if you're touching provisioning
scripts, don't make that possible even accidentally; there's a contract
test for it in
tests/system_integration/environments/.
docs/QUICKSTART.md has a troubleshooting table for the common local setup
failures (wrong PG password, port mismatch, API not running, etc.) — check
there first. For anything else, docs/BUG_REPORT.md and
docs/DEFECT_INSERTED_ROWS.md document known issues and their status.