Reusable profiler and importer chassis for tabular migrations
Project description
migration-workbench
Reusable Django chassis for tabular workbook → app migrations: connectors pull from spreadsheets (Google Sheets) or Coda; profiling produces deterministic bundles; importers validate and apply with structured summaries; the workbook app turns profiles into schema-contract YAML for product repos to harden into real models.
PyPI: migration-workbench — pip install migration-workbench (import package migration_workbench uses underscores).
Prerequisites
Docker
This project uses Docker for building and deploying. Your system user must be in the
docker group to run Docker commands without sudo:
# Add your user to the docker group
sudo usermod -aG docker $USER
# Apply the group change in the current shell session
sg docker -c "docker ps"
After running these commands, log out and back in (or use sg docker -c "your command")
for the group change to take effect permanently.
Troubleshooting: If you see
permission denied while trying to connect to the Docker API at unix:///var/run/docker.sock, you have not completed the steps above.
Who it is for
- Product teams moving messy spreadsheet truth into a maintainable Django app.
- Single-operator or small teams who want a repeatable pipeline (profile → contract → import) instead of one-off scripts.
- Django-adjacent adopters comfortable wiring
INSTALLED_APPS, env vars, and Fly-style SQLite hosting.
Three ways to use it
1. As a library (recommended for product repos)
Add the apps you need to INSTALLED_APPS and wire URLs/commands in your Django project. Set **DJANGO_SETTINGS_MODULE** to your project’s settings module (not migration_workbench.settings) in production. Depend on a released version, e.g. migration-workbench>=0.1.0,<1.
2. Scaffold a new product repo
From a sibling checkout of this repo:
make new-product PRODUCT=my-product # writes ../my-product; git init + initial commit
make new-product PRODUCT=my-product PROVIDER=--coda
Then cd ../my-product && make install && make migrate && make check. Local make install matches the Dockerfile: the product package is editable (pip install -e .) and migration-workbench comes from PyPI via pyproject.toml. The scaffold also includes backend/, Makefile, scripts/entrypoint_product.sh, SQLite/Fly-aligned settings (SQLITE_PATH, /healthz, WAL pragmas), starter docs, and provider-specific config skeletons under config/ (Google Sheets by default; use PROVIDER=--coda for Coda). If git is on PATH, the scaffold initializes a repo and writes one initial commit using a scaffold-local author identity. Use --output-dir / --force on scripts/new_product.py for non-default paths.
3. Develop the chassis (this repo)
Clone, editable install, run the full gate:
python3 -m venv .venv
.venv/bin/pip install -e ".[dev]"
. ./.env.example # or create .env
.venv/bin/python manage.py migrate
make chassis-gate
Quickstart (PyPI)
python3 -m venv .venv
.venv/bin/pip install "migration-workbench[dev]" # omit [dev] if you skip pytest/black
Use wb on your PATH, or import apps (connectors, profiler, importer, workbook, deployment, …). For consumer repos installing the chassis next to your code: pip install -e ../migration-workbench — see profiler/README.md for profiling commands and importer/README.md for import authoring.
Core bundle commands (from a project with manage.py):
python manage.py pull_bundle --config docs/examples/live-config.example.json --output-dir /tmp/bundle
python manage.py snapshot_bundle --config docs/examples/offline-config.example.json --output-dir /tmp/bundle
python manage.py import_reference_example example_data --validate-only
Note: bundled **migration_workbench.settings** is for development; production hosts use their own settings module.
Architecture at a glance
Five Django apps:
| App | Role |
|---|---|
| connectors | Provider adapters (Sheets, Coda). |
| profiler | Read-only profiling → normalized bundle artifacts. |
| importer | BaseImportCommand chassis, preflight/apply, summary JSON. |
| workbook | scaffold_workbook_schema → schema-contract YAML. |
| deployment | Manifest validation, wb CLI (manifest lint, deploy dry-run). |
flowchart LR
sourceConfig[SourceConfigJSON] --> pullBundle[PullBundleCommand]
pullBundle --> providerRouter[ProviderRouter]
providerRouter --> adapters[GoogleSheets_or_Coda]
adapters --> rawRows[RawRows]
rawRows --> normalizer[SpreadsheetNormalizer]
normalizer --> bundle[NormalizedBundle]
bundle --> importer[BaseImportCommandSubclass]
importer --> summary[SummaryArtifactJSON]
More detail: docs/architecture.md.
The pipeline
- Intake — Source config (Drive folder, sheet IDs, Coda doc URLs).
- Profile — Profiler commands emit JSON/Markdown under product-owned
data/profile_snapshots/by default. - Model —
scaffold_workbook_schemaproduces schema-contract YAML for review. - Harden — Importer tiers validate then apply; summary artifacts record outcomes.
- Deploy —
wb manifest lintvalidates deploy/spaces.yml;wb deploy <space> --env <preview|production> --dry-runplans releases (provider mutation deferred — see docs/deployment.md).
Deployment
Fly.io + SQLite on a persistent volume + Litestream replication to Tigris or any S3-compatible bucket. Operator bootstrap, secrets, CI/CD, rollback, and roadmap for the wb control plane: docs/deployment.md.
CI/CD
| Workflow | File | Trigger | Role |
|---|---|---|---|
| CI | .github/workflows/ci.yml | push, PR | make chassis-gate, wheel smoke |
| Deploy | .github/workflows/deploy.yml | after successful CI (workflow_run) |
manifest lint → flyctl deploy → /healthz smoke (main → production, preview/* → preview) |
| Publish PyPI | .github/workflows/publish-pypi.yml | tag v* |
Trusted Publishing to PyPI |
GitHub repository secret **FLY_API_TOKEN** is required for Deploy. Product repos can copy these CI patterns, but workflow files are maintained per repository.
Status and roadmap
Stable on 0.x today
- Profiler (Google Sheets / Drive + Coda), importer chassis, workbook scaffolder.
wb manifest lint,wb deploy --dry-run/--live, PyPI trusted publishing.- Self-hosted Fly path: Litestream + shared Tigris bucket,
fly.toml/fly.preview.toml, entrypoint migrations.
In flight
- Align default Git branch with Deploy workflow (
mainvsmaster). - Production Deploy workflow green end-to-end after secrets and Fly bootstrap.
Next
- Full end-to-end Deploy workflow green (secrets and Fly bootstrap).
- Backup/restore drill documented and exercised for the workbench space.
- Google auth runbook evolution toward WIF (docs/google-auth.md).
- Scaffold-delivered CI/CD templates for client product repos.
- Cross-reference tab detection via workbook code patterns (
\b\d{3}\b) in tab scoring heuristics, penalizing derived tabs from other workbooks (Issue #1).
Later
- Provider interface extraction after a second space is stable on Fly.
- Postgres mode where concurrent writes demand it.
v1.0 criteria
The pipeline is exercised toward v1.0 via a product test repo (farm). v1.0 is reached when:
- End-to-end pipeline — All five stages (Connectors → Profiler → Importer → Workbook → Deployment) exercised on a real corpus via the product repo.
- Schema design loop completed — At least one source corpus has gone through Profile → Observe → Draft → Decide → Author config → Author importer → Gate → Drift check.
- Production deployment live — A scaffolded product is deployed to Fly.io with real imported data, health-check passing.
- PyPI release cut — All gaps identified during the test run are patched upstream, and a new PyPI release is published.
Semantic versioning applies; **0.x** may ship breaking changes — pin ranges in product repos.
Releases
- Bump
**version** in[pyproject.toml](pyproject.toml). - Tag
**v + version** (must matchversion = "x.y.z"). - Trusted Publishing on PyPI for this repo (see publish workflow).
Manual upload: python -m build then twine upload dist/*, or make publish with maintainer credentials. Optional extras: [release] for build/twine only.
Documentation map
| Doc | Purpose |
|---|---|
| This README | Orientation, pipeline, roadmap |
| docs/architecture.md | Layered design |
| docs/deployment.md | Fly, secrets, Litestream/Tigris, CI/CD, control-plane roadmap |
| docs/schema-design-loop.md | Contract-first importer workflow |
| docs/google-auth.md | Sheets/Drive profiling auth |
| docs/google-corpus.md | Drive folder / multi-workbook Sheets corpus profiling |
| docs/coda.md | Coda profiling |
| docs/contributing.md | Development setup, test suite, PR expectations |
| docs/end-to-end-tutorial.md | Step-by-step walkthrough from profile to import |
| docs/pull-bundle.md | Source config, live/offline modes, bundle validation |
| docs/schema-contract.md | YAML contract format reference (v1.0–v1.3) |
| docs/view-manifest.md | View manifest YAML format, admin generation effects |
| docs/pipeline-manifest.md | Machine-generated execution plan format |
| docs/troubleshooting.md | Consolidated FAQ for common errors |
Per-package README.md under connectors/, profiler/, importer/, workbook/, deployment/ |
App-local surfaces |
Changelog
0.9.2
- Rich profiling enrichment: profiler enrichment functions for computed fields, FK candidates, import keys, and entity groupings (
profiler/tools/enrichment_utils.py). - Coda column enrichment:
enrich_coda_columnsaddsProfiler-column metadata for Coda sources. - Cohort corpus enrichment propagation: enrichment fields flow through scaffold contract generation (
suggested_entity,suggested_fk_target,is_computed,is_import_key_candidate,cross_tab_group). - Import key candidates from enrichment: scaffold uses
is_import_key_candidateto proposeunique_onfields instead of always defaulting to the first column. - Domain knowledge flag:
scaffold_workbook_schema --domain-knowledgeloads entity-aware heuristics for contract generation. - Scaffold polish: createsuperuser Make target with env var support, admin URL redirect, sentinel marker in models.py template, PascalCase passthrough in
_to_pascal_case. - Makefile target deduplication: shared
workbook/makefile_targets.pymodule replaces inline Makefile template inscripts/new_product.py. - Unified
wbCLI:wb generate {models,admin,import,manifest}andwb validate contractsubcommands; Makefile targets usewbinstead of$(MANAGE). *_auto.pyoutput convention:generate_modelswritesmodels_auto.pywith stubmodels.pyre-export;generate_adminwritesadmin_auto.pywith stubadmin.py. Hand-edited files are never overwritten.model_namerequired: every contract table must have an explicitmodel_namefield;get_model_name()is a direct accessor, no derivation.- Contract admin blocks authoritative:
generate_adminuses contractadmin:blocks as-is; view manifest is enrichment only. bundle_pathauto-derive: scaffold always derivesimport_config.bundle_pathfrom model name.validate_contractmanagement command: standalone contract validation without code generation.- Clean error on missing
bundle_path: import generation gives actionable guidance instead of raw traceback. --domain-knowledgeexample YAML:domain-knowledge.example.yamlshipped with scaffold output.
0.9.1
- Makefile target deduplication: shared
workbook/makefile_targets.pymodule replaces inline Makefile template in product scaffold. - Documentation coverage: docstrings and module docs for connectors, profiler, deployment, and workbook.
- Documentation index:
docs/INDEX.mdwith cross-references. - Pipeline manifest wiring:
generate-pipeline-manifestwired intogenerate-allMakefile target.
0.9.0
- Live deploy with health gate:
wb deploy <space> --env <env> --liveperforms a real Fly deploy, polls/healthz, and records release events. Outcome taxonomy:deploy_start,deploy_failed,deploy_succeeded_healthy,deploy_succeeded_unhealthy. --localbuild flag:wb deploy --localbuilds with local Docker instead of Fly remote builder.- Release ID propagation: After successful deploy,
RELEASE_IDis set as a Fly secret for the health endpoint. - Improved deploy diagnostics:
--verbose/-vstreams fly deploy stderr/stdout; failed deploys include stderr tail and machine state capture. - Product-aware settings detection:
wbauto-detects product repo settings atbackend/config/settings.py;--django-settingsflag for explicit override. - Harden entrypoint: Docker entrypoint now checks for
/datavolume before creating directories, with actionable error messages. - Manifest validation relaxed: Missing
previeworproductionenvironment blocks no longer fail validation. - Test reliability: Subprocess calls in tests use
sys.executableinstead of hardcoded"python"for venv isolation. - Scaffold improvements:
deploy/spaces.ymlgenerated for new products;deploymentapp added toINSTALLED_APPS. - Code review hardening:
fly secrets setalways warns on failure; conflicting--dry-run/--liveflags error out; missingflyCLI produces actionable install hint; machine state parsing handles non-array API responses.
0.8.0
- Per-tier transaction savepoints:
--tier-atomic(default on) wraps each import tier in its owntransaction.atomic()savepoint. A failing tier rolls back only its own rows; preceding tiers persist.--no-tier-atomicrestores single-transaction behaviour. - Per-row exception catching in generated imports: Generated
_import_<model>()methods now catchIntegrityErrorand other exceptions per row, recording structured errors instead of aborting the entire tier. - New error codes:
type_mismatch,unique_violation, androw_exceptioninFAILURE_SIGNATURE_OWNERSHIPfor structured escalation routing. - Per-model row error counts in summary JSON: Each model's outcome dict now includes
row_errors_countfor quick per-model error tallying. - Expanded parsing edge-case handling: Tests for
None, whitespace-only, and common sentinel values ("N/A","-") across all parsers. - End-to-end import pipeline fixture:
ExampleFarm,ExampleField,ExampleVarietymodels with FK chains,column_mapmulti-source,field_transforms, andfield_parsersexercising the fullgenerate_import→BaseImportCommandpipeline. - Bundle reader multi-source fix:
iter_bundle_tab_rowsnow correctly skips list-valuedcolumn_mapentries instead of raisingTypeError. - Import pipeline smoke test in chassis-gate:
generate_importexercised with multi-model contract in CI.
0.7.0
- Profile-to-contract bridge — designed model detection:
scaffold_workbook_schemanow clusters tabs by overlapping column sets (>50% Jaccard-like overlap) and suggests designed/aggregate models withsource_tab: null. New moduleworkbook.codegen.designed_model_detection. - Contract review checklist round-out:
wb contract reviewnow checks FK lookup target existence, admin inlines target models, and computed_field snake_case naming conventions. validate-contractMake target: Wired into scaffolded product Makefile; aggregatescheck validate-contractfor CI.corpus-codegen-reportMake target: Runs contract review and Django system check on generated files; corpus feedback tracker doc for capturing papercuts.
0.6.0
- Reserved-character sanitization: Tab names containing
|,:,\,/,*,?,",<,>,%are automatically sanitized to underscore at ingestion, with a logged warning. - Tab exclusion by pattern: Configurable
tab_exclude_patternsin scoring heuristics — each entry specifies a regex pattern and penalty weight for matching tab titles. - Column formula structure analysis: Profiler classifies columns as
raw,row_formula,expansion_formula,hybrid, orempty. Classification flows into tab scoring (expansion_formula_ratiopenalty), schema contract field annotations, and column candidate shortlists.
0.5.0
- Migration safety checks:
wb contract safety --old contract-v1.yaml --new contract-v2.yamldetects destructive changes (field removed, nullable→non-nullable → DANGER; class change, max_length decreased, unique=True added, non-nullable field without default → WARNING) with text and--jsonoutput. - Null-key robustness:
_diff_fields()normalises YAMLnull:mapping keys to the string"null"to preventTypeErrorduring kwarg comparison.
0.4.0
- Multi-source column_map with field transforms:
column_mapvalues can be lists of source headers;field_transformsblock accepts lambda expressions for combining columns (default: space join). - Contract composition: Custom
!includeYAML tag resolves relative to including file's directory with cyclic-include detection. - Auto-detect import tier ordering:
assign_import_tiers()topological sorts FK dependency chains; explicit tiers override auto-detection. - Contract diff tool:
wb contract diff --old contract-v1.yaml --new contract-v2.yamlcompares models, fields, and meta with text and JSON (--json) output. - Schema review checklist:
wb contract review --contract <yaml>checks CharField max_length, nullable FK on_delete, missing unique_together, and str_template. - Snapshot testing:
make snapshot-codegen/make check-snapshotsstores generated output for regression detection. check-generatedMakefile target: py_compile validation of generated Python files.
0.3.0
- Admin scaffold maturity:
list_editable,autocomplete_fields,admin.inlinesfield overrides,--diffflag for regeneration preview. - Post-generation hook system:
hooks.after_model,hooks.after_meta,hooks.extra_methodsin contract YAML inject Python source at well-defined points in generated model classes. scaffold_designed_modelcommand: Emit contract table skeletons for designed/aggregate models with no source tab.- Admin
--diffflag: Preview changes before overwriting; forced regeneration shows diff of detected changes.
0.2.0
- Contract schema v1.3:
computed_fields(rendered as@property),is_abstract,source_tab: nullfor designed models,app_labelper table inmodel_meta. - Makefile improvements:
validate-contract,diff-generated,generate-admin-light,generate-admin,post-generatetargets. - Codegen QoL:
generate_models --diff, contract validation warnings at codegen time, import generator skip notes. - Backport AbstractUser admin scaffold support from codegen pipeline.
- Extend contract schema to v1.2: enums, admin config,
model_base, richerMeta. - Initial codegen pipeline:
generate_models,generate_admin,generate_importcommands producing production Django files from hardened schema-contract YAML. - Import generator base class with override hooks.
inject_project_local_config.shhelper for per-checkout config injection.
0.1.2
- Default profile output directory:
data/profile_snapshots/. - Drive folder tree rendered as Markdown artifact.
- Cohort corpus resume support with workbook index and HTTP 429 retry.
- Skeleton config files and raw_notes bucket included in
new-productscaffold. - New product scaffold emits fixed Makefile referencing editable workbench path.
- Bundle reader integration with YAML config files.
0.1.1
- View manifest draft YAML artifact from profiler structural pass.
structure.jsonartifact frompull_bundlecommand — tab- and column-level metadata.- New product scaffold defaults to PyPI
migration-workbench. read_bundle_tabwrapper for normalizing rows from bundle tab CSV.- Git init and initial commit after
new-product. - Consolidated docs folder with cross-cutting operator notes.
- Per-app READMEs at
connectors/,profiler/,importer/,workbook/,deployment/.
0.1.0
- Initial scaffold: profile, import, bundle commands.
- Project bootstrap scripting (
new-product). - Google Sheets / Drive and Coda adapters.
- Deployment documentation for Fly.io + Litestream.
Database modes
DB_ENGINE=sqlite(default)DB_ENGINE=postgreswithDB_NAME,DB_USER,DB_PASSWORD,DB_HOST,DB_PORT
License
See LICENSE.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file migration_workbench-0.9.2.tar.gz.
File metadata
- Download URL: migration_workbench-0.9.2.tar.gz
- Upload date:
- Size: 240.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9d4914f4ab7b3db3f4d0c667596f12f4aed6bb3819a007568772c6dbfbf9699d
|
|
| MD5 |
a594463febbc14e24a7ec308492a98ff
|
|
| BLAKE2b-256 |
b900cfd2451eaa5725570d775441e866994c7feb93acd59dd9a75e8967522829
|
Provenance
The following attestation bundles were made for migration_workbench-0.9.2.tar.gz:
Publisher:
publish-pypi.yml on MrAllatta/migration-workbench
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
migration_workbench-0.9.2.tar.gz -
Subject digest:
9d4914f4ab7b3db3f4d0c667596f12f4aed6bb3819a007568772c6dbfbf9699d - Sigstore transparency entry: 1571119317
- Sigstore integration time:
-
Permalink:
MrAllatta/migration-workbench@ef51ba55888bfe2d693d3ce3eedc89dd84004ffe -
Branch / Tag:
refs/tags/v0.9.2 - Owner: https://github.com/MrAllatta
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-pypi.yml@ef51ba55888bfe2d693d3ce3eedc89dd84004ffe -
Trigger Event:
push
-
Statement type:
File details
Details for the file migration_workbench-0.9.2-py3-none-any.whl.
File metadata
- Download URL: migration_workbench-0.9.2-py3-none-any.whl
- Upload date:
- Size: 299.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3fd617b5f28a2d16379514c91c5626d8b5cea1d44e7e65886fe39e53ee490fed
|
|
| MD5 |
2e7106d64bfede1bf1abae07cc600404
|
|
| BLAKE2b-256 |
8d59df59da0f37772e553006c15dba6c2a8690af9c636c8a4dd1abc862632559
|
Provenance
The following attestation bundles were made for migration_workbench-0.9.2-py3-none-any.whl:
Publisher:
publish-pypi.yml on MrAllatta/migration-workbench
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
migration_workbench-0.9.2-py3-none-any.whl -
Subject digest:
3fd617b5f28a2d16379514c91c5626d8b5cea1d44e7e65886fe39e53ee490fed - Sigstore transparency entry: 1571119413
- Sigstore integration time:
-
Permalink:
MrAllatta/migration-workbench@ef51ba55888bfe2d693d3ce3eedc89dd84004ffe -
Branch / Tag:
refs/tags/v0.9.2 - Owner: https://github.com/MrAllatta
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-pypi.yml@ef51ba55888bfe2d693d3ce3eedc89dd84004ffe -
Trigger Event:
push
-
Statement type: