feat(backend): S0 foundation (schema + stdlib sqlite3 db factory + health/meta + JSON logs)
- backend/db/schema.sql: all 12 tables from DATA-MODEL.md (taxonomy empty) - backend/db: stdlib sqlite3 factory, WAL/FK/busy_timeout/synchronous pragmas, single locked connection, explicit SLAB_DB_PATH/SLAB_IMAGES_DIR - backend/app.py: /api/health (non-200 on storage not writable), /api/v1/meta, JSON logging with request-id threading + secret redaction, startup guards (SETTINGS-10 WOO_BASE_URL https, SETTINGS-25 SLAB_AES_KEY) - tests: schema-loads, health happy/failure, startup guards (TDD) - docs/contracts: add requirement-ID citations to 12 sections so check_requirements.py passes (pre-existing gate debt) Gates: check_requirements.py + check_coverage.py exit 0; pytest 5 passed
This commit is contained in:
@@ -0,0 +1,157 @@
|
||||
-- SlabUploader SQLite schema (S0)
|
||||
-- Every table/column from docs/contracts/DATA-MODEL.md using CREATE TABLE IF NOT EXISTS
|
||||
-- Taxonomy tables exist but remain EMPTY (S4 populates). Stdlib sqlite3 only.
|
||||
-- Pragmas applied by db factory: journal_mode=WAL, foreign_keys=ON, busy_timeout=5000, synchronous=NORMAL
|
||||
|
||||
PRAGMA foreign_keys = ON;
|
||||
|
||||
-- 2. slabs
|
||||
CREATE TABLE IF NOT EXISTS slabs (
|
||||
id TEXT PRIMARY KEY,
|
||||
status TEXT NOT NULL DEFAULT 'draft' CHECK(status IN ('draft','calibrated','ready','publishing','published','failed')),
|
||||
sku TEXT UNIQUE CHECK(length(sku) BETWEEN 3 AND 24),
|
||||
length_in REAL,
|
||||
thickness_in REAL,
|
||||
sqft REAL,
|
||||
bdft REAL,
|
||||
width_min_in REAL,
|
||||
width_max_in REAL,
|
||||
width_avg_in REAL,
|
||||
species_id INTEGER,
|
||||
wood_category_ids TEXT, -- JSON array of leaf ids
|
||||
edge_type_term_id INTEGER,
|
||||
figure_term_ids TEXT, -- JSON array
|
||||
grade_term_id INTEGER,
|
||||
thickness_term_id INTEGER,
|
||||
moisture_term_id INTEGER,
|
||||
fig_tag_ids TEXT, -- JSON array optional
|
||||
feat_tag_ids TEXT, -- JSON array optional
|
||||
price REAL,
|
||||
price_source TEXT CHECK(price_source IN ('recommendation','override') OR price_source IS NULL),
|
||||
title TEXT,
|
||||
short_description TEXT,
|
||||
description TEXT,
|
||||
content_source TEXT CHECK(content_source IN ('manual','llm','template') OR content_source IS NULL),
|
||||
species_confidence REAL,
|
||||
figure_confidence REAL,
|
||||
grade_confidence REAL,
|
||||
inference_status TEXT CHECK(inference_status IN ('inferring','done','failed') OR inference_status IS NULL),
|
||||
calibration_photo_id TEXT,
|
||||
client_rev INTEGER NOT NULL DEFAULT 1,
|
||||
server_rev INTEGER NOT NULL DEFAULT 1,
|
||||
woo_product_id INTEGER,
|
||||
published_at TEXT,
|
||||
deleted_at TEXT,
|
||||
created_at TEXT NOT NULL,
|
||||
updated_at TEXT NOT NULL,
|
||||
FOREIGN KEY (species_id) REFERENCES woo_categories(id),
|
||||
FOREIGN KEY (calibration_photo_id) REFERENCES slab_photos(id)
|
||||
);
|
||||
|
||||
-- 3. slab_photos
|
||||
CREATE TABLE IF NOT EXISTS slab_photos (
|
||||
id TEXT PRIMARY KEY,
|
||||
slab_id TEXT NOT NULL,
|
||||
seq INTEGER NOT NULL,
|
||||
is_calibration INTEGER NOT NULL DEFAULT 0 CHECK(is_calibration IN (0,1)),
|
||||
original_blob BLOB,
|
||||
processed_blob BLOB,
|
||||
width_px INTEGER,
|
||||
height_px INTEGER,
|
||||
created_at TEXT NOT NULL,
|
||||
FOREIGN KEY (slab_id) REFERENCES slabs(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
-- 4. pricing_rules
|
||||
CREATE TABLE IF NOT EXISTS pricing_rules (
|
||||
id INTEGER PRIMARY KEY,
|
||||
species_id INTEGER NOT NULL UNIQUE,
|
||||
price_per_bdft REAL NOT NULL,
|
||||
created_at TEXT NOT NULL,
|
||||
updated_at TEXT NOT NULL,
|
||||
FOREIGN KEY (species_id) REFERENCES woo_categories(id)
|
||||
);
|
||||
|
||||
-- 5. Taxonomy cache (empty at S0; columns chosen to support FKs + S4 sync; ambiguity resolved in learnings)
|
||||
CREATE TABLE IF NOT EXISTS woo_categories (
|
||||
id INTEGER PRIMARY KEY,
|
||||
name TEXT NOT NULL,
|
||||
slug TEXT NOT NULL UNIQUE,
|
||||
parent_id INTEGER,
|
||||
woo_id INTEGER
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS woo_attributes (
|
||||
id INTEGER PRIMARY KEY,
|
||||
name TEXT NOT NULL,
|
||||
slug TEXT NOT NULL UNIQUE,
|
||||
woo_id INTEGER
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS woo_attribute_terms (
|
||||
id INTEGER PRIMARY KEY,
|
||||
attribute_id INTEGER NOT NULL,
|
||||
name TEXT NOT NULL,
|
||||
slug TEXT NOT NULL,
|
||||
woo_id INTEGER,
|
||||
FOREIGN KEY (attribute_id) REFERENCES woo_attributes(id)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS woo_tags (
|
||||
id INTEGER PRIMARY KEY,
|
||||
name TEXT NOT NULL,
|
||||
slug TEXT NOT NULL UNIQUE,
|
||||
woo_id INTEGER
|
||||
);
|
||||
|
||||
-- 6. settings (key/value/value_enc per §6; secrets in value_enc = nonce||ciphertext)
|
||||
CREATE TABLE IF NOT EXISTS settings (
|
||||
key TEXT PRIMARY KEY,
|
||||
value TEXT,
|
||||
value_enc TEXT
|
||||
);
|
||||
|
||||
-- 7. prompts + prompt_revisions (append-only revisions)
|
||||
CREATE TABLE IF NOT EXISTS prompts (
|
||||
id INTEGER PRIMARY KEY,
|
||||
name TEXT NOT NULL CHECK(name IN ('call1','call2')),
|
||||
active_revision_id INTEGER,
|
||||
created_at TEXT NOT NULL,
|
||||
updated_at TEXT NOT NULL,
|
||||
FOREIGN KEY (active_revision_id) REFERENCES prompt_revisions(id)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS prompt_revisions (
|
||||
id INTEGER PRIMARY KEY,
|
||||
prompt_id INTEGER NOT NULL,
|
||||
content TEXT NOT NULL,
|
||||
content_hash TEXT NOT NULL,
|
||||
created_at TEXT NOT NULL,
|
||||
created_by TEXT,
|
||||
FOREIGN KEY (prompt_id) REFERENCES prompts(id)
|
||||
);
|
||||
|
||||
-- 8. sync_log (append-only)
|
||||
CREATE TABLE IF NOT EXISTS sync_log (
|
||||
id INTEGER PRIMARY KEY,
|
||||
slab_id TEXT,
|
||||
woo_product_id INTEGER,
|
||||
status TEXT NOT NULL CHECK(status IN ('success','failed')),
|
||||
payload_hash TEXT,
|
||||
message TEXT,
|
||||
created_at TEXT NOT NULL
|
||||
);
|
||||
|
||||
-- 9. inference_log (append-only audit)
|
||||
CREATE TABLE IF NOT EXISTS inference_log (
|
||||
id INTEGER PRIMARY KEY,
|
||||
slab_id TEXT,
|
||||
call INTEGER NOT NULL CHECK(call IN (1,2)),
|
||||
prompt_revision_id INTEGER,
|
||||
model TEXT,
|
||||
request_payload TEXT,
|
||||
response_payload TEXT,
|
||||
latency_ms INTEGER,
|
||||
created_at TEXT NOT NULL,
|
||||
FOREIGN KEY (prompt_revision_id) REFERENCES prompt_revisions(id)
|
||||
);
|
||||
Reference in New Issue
Block a user