0eb2b0f697
- 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
158 lines
4.8 KiB
SQL
158 lines
4.8 KiB
SQL
-- 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)
|
|
);
|