Files
shopdb-flask/CLAUDE.md
cproudlock c386e211df ADR-014 Phase 2: flask plugin prune-schema for lean per-site DBs
A lean site still gets every plugin's tables from the shared core Alembic
baseline. prune-schema drops the tables of plugins not installed on this
site, leaving core + chosen-plugin tables, with no edit to any released
migration (the relocate-into-plugin-baselines alternative would mean
rewriting ~15 released core migrations for a cosmetic gain - see ADR-014).

- shopdb/plugins/cli.py: prune-schema command. Dry-run by default; --yes to
  execute; refuses non-empty tables without --force. Drops by table name (no
  plugin import) so it works on a lean image. MySQL: private AUTOCOMMIT engine
  (db.engine's pooled connections sit idle-in-transaction in a CLI context and
  would deadlock the DROP on a metadata lock). SQLite: db.engine, restoring the
  prior foreign_keys pragma so the StaticPool connection is not left changed.
- tests/test_plugin_prune_schema.py: drop-only-not-installed, full no-op,
  refuse-non-empty, force-drops-non-empty.
- docs/DEPLOY.md: lean provisioning step after upgrade-all.
- ADR-014 ACCEPTED; index updated.

Verified on MySQL: full install then prune = no-op (86 tables); lean install
(machines+printers) then prune drops the other 19 plugin tables; second run
no-op. Full suite 1077 passed.
2026-07-19 11:39:46 -04:00

7.6 KiB

ShopDB Flask Project

Modern rewrite of the classic-ASP shopdb. Built as a framework so sister GE Aerospace sites can adopt it. Plugin system is the product.

Database

  • Active database: shopdb_flask (MySQL, asset-based schema)
  • Legacy database: shopdb (Classic ASP schema, used only for one-time data import via scripts/import_from_mysql.py)
  • Connection: .env file. See .env.example.

Architecture decisions live in docs/adr/. Read those before making schema or contract changes.

  • ADR-001: Asset model is the platform contract (Machine retires) - ACCEPTED
  • ADR-002: Plugin contract versioning (semver) - ACCEPTED
  • ADR-003: Plugin distribution model (in-tree bundled + filesystem-based external) - ACCEPTED
  • ADR-004: Deployment topology (per-site instances, not multi-tenant) - ACCEPTED
  • ADR-005: Equipment vs measuringtools plugin scope - ACCEPTED
  • ADR-006: Plugin collector contract pattern - ACCEPTED
  • ADR-007: Product versioning and releases - ACCEPTED
  • ADR-008: Plugin migration ownership (per-plugin chains) - ACCEPTED
  • ADR-009: Frontend plugin route gating - ACCEPTED
  • ADR-010: Frontend plugin hook contract - ACCEPTED
  • ADR-011: Machines rename + modeltypes retyping - ACCEPTED
  • ADR-012: GE-Enforce manifest ownership in shopdb - ACCEPTED
  • ADR-013: Plugin catalog, curated shelf, and lean per-site builds - PROPOSED
  • ADR-014: Schema-lean per-site builds (retire cross-plugin FKs, prune not-installed plugin tables) - ACCEPTED

Coding convention

CONTRIBUTING.md defines naming rules (DB tables, columns, Python, JS, Vue, API). Pre-commit hook at scripts/check-naming-and-style.sh enforces them. Read CONTRIBUTING.md before naming any new identifier.

Current state (as of 2026-07-13)

Refactor phases 0-5 landed; phase 6 (multi-site distribution readiness) largely complete; the last big milestone is the legacy-data import + a production pilot.

Phases done

  • Phase 0: 6 ADRs accepted, naming convention v1, pre-commit style hook
  • Phase 1: 8 smoke tests + 7 production-config tests, Flask-SQLAlchemy 3 fixtures, uv lockfile, hardened ProductionConfig
  • Phase 2: contract surface defined (__contract_version__, BasePlugin hooks, docs/PLUGIN-HOOKS.md), 51 contract tests
  • Phase 3: manifest-first loader, fail-loud/isolate policy, contract-version range checking, auto-register core blueprints, shopdb.api namespace, BasePlugin.get_setting/set_setting helpers
  • Phase 4: flask plugin new <name> CLI, scaffold templates, 14 canary tests, docs/PLUGIN-QUICKSTART.md
  • Phase 5: ADRs moved to docs/adr/, Alembic baseline migration, per-site deploy artifacts (Dockerfile, docker-compose.yml, docs/DEPLOY.md)

Active state

  • 966 tests, naming/style check green, Gitea Actions CI (backend + naming + frontend build + a migrations-mysql job that runs the real fresh upgrade on utf8mb4 MySQL 8)
  • __contract_version__ at 0.13.0 (0.12.0 added the mailer, 0.13.0 the User model, to the plugin surface) (product __version__ 0.7.0, tags v0.5.0/v0.6.0/v0.7.0 - distinct series, ADR-007)
  • 12 bundled plugins all satisfy contract: computers, employees, geenforce, knowledgebase, machines, measuringtools, network, notifications, printers, slides, usb, warranty
  • Core Alembic chain: baseline 68b3947ae14f -> head 7d26_settings_description_text (33 core migrations). Each plugin owns its own chain (ADR-008); deploy runs flask db upgrade then flask plugin upgrade-all. Reproducible + idempotent from empty (env.py relaxes session sql_mode so the chain runs on strict MySQL 8).
  • Legacy import: docs/IMPORT-API.md is the schema-agnostic import contract; docs/IMPORT-ADOPTION.md + docs/PILOT-DEPLOY.md cover adopting a site; scripts/site_imports/wjf/ is the West Jefferson reference loader (all 15 stages, validated end-to-end including on a Windows + MySQL 8 VM).
  • API is migration-complete: an admin PAT + docs/IMPORT-API.md let a script import the whole legacy DB (X-Import-Mode preserves timestamps).
  • Pre-1.0 framework; sister sites should pin tight core_version ranges until contract reaches 1.0

Deferred

  • Equipment data migration (one-shot script for legacy ASP shopdb -> assets). Per ADR-001, only category='Equipment' AND machinenumber IS NOT NULL migrates. Skill migrating-asset-schema documents the pattern; the actual one-shot script lives in scripts/migration/ when run.
  • Printers retirement: legacy PrinterData model + frontend changes. Coordinated with the equipment data migration.
  • (DONE 2026-07-11) measuringtools plugin (ADR-005) is built and bundled; docs/PLUGIN-GUIDE.md narrates its construction as the plugin tutorial.
  • (DONE) Frontend plugin hook contract (ADR-010): get_settings_cards / get_asset_panels / get_map_overlays / get_asset_presentation shipped; generic renderers for panels/overlays land incrementally.
  • (DONE) Per-plugin Alembic chains (ADR-008): every bundled plugin carries its own chain; no plugin uses db.create_all().
  • Legacy ASP data import against the renamed schema (unblocked; run via docs/IMPORT-API.md) + a production pilot deployment.

Quick start

# Start dev environment
~/start-dev-env.sh

# Activate venv and install deps
cd /home/camp/projects/shopdb-flask
source venv/bin/activate
pip install -r requirements.txt

# Configure environment
cp .env.example .env
# Edit .env with DB credentials, JWT secrets

# Create / update database tables via the Alembic chain
flask db upgrade

# Seed RBAC, default settings, and reference data (all idempotent)
flask seed permissions
flask seed settings
flask seed reference-data

# Restart services
pm2 restart shopdb-flask-api shopdb-flask-ui

Service URLs

Plugin structure

plugins/
  <plugin_name>/
    __init__.py
    plugin.py          # BasePlugin implementation
    manifest.json      # Plugin metadata (name, version, dependencies, api_prefix)
    models/
      __init__.py      # Export all models
      <model>.py       # SQLAlchemy models
    api/
      __init__.py
      routes.py        # Flask Blueprint
    services/          # Optional, business logic
    schemas/           # Optional, marshmallow schemas
    migrations/        # Optional, plugin-specific Alembic migrations

Each plugin must have:

  • models/__init__.py exports all models
  • plugin.py extends BasePlugin
  • manifest.json with metadata (single source of truth per ADR-002)
  • No direct imports from core code (use the contract surface defined in ADR-001)

Key files

  • shopdb/__init__.py - app factory, blueprint registration
  • shopdb/plugins/base.py - BasePlugin ABC, PluginMeta dataclass
  • shopdb/plugins/loader.py - filesystem discovery, dependency-aware loading
  • shopdb/core/api/assets.py - example of optional plugin imports
  • frontend/src/router/index.js - frontend routing
  • frontend/src/components/AppSidebar.vue - navigation menu
  • docs/adr/ - architecture decision records

Migration notes

  • migrations/DATA_MIGRATION_GUIDE.md - one-time import from legacy ASP shopdb
  • migrations/MIGRATE_USB_DEVICES_FROM_EQUIPMENT.md - USB device migration from equipment table
  • migrations/FIX_LOCATIONONLY_EQUIPMENT_TYPES.md - LocationOnly equipment type fix
  • migrations/PRODUCTION_MIGRATION_GUIDE.md - production import methods
  • migrations/rename_underscore_columns.sql - one-time rename of snake_case columns to lowercase concatenated (per CONTRIBUTING.md)
  • migrations/versions/ - the core Alembic chain (baseline 68b3947ae14f -> head 7d26_settings_description_text). Run flask db upgrade to apply.