Files
karung-counting-feedmill-se…/ERD.md
T
2026-09-25 14:22:25 +07:00

116 lines
4.7 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# ERD — karung counter (SQLite)
Data model for the karung feedmill counter. SQLite DB at
`$OUTPUT_DIR/jetson_counter.db` (`config.yaml` `output.dir`,
default `/opt/jetson-counter/`; env override `DB_PATH`).
**No DB foreign keys enforced** (zero `FOREIGN KEY` in code) — all relationships
are soft, by naming/best-effort writes.
```mermaid
erDiagram
batches ||--o{ delivery_orders : "soft via batch_id / do_numbers"
batches ||--o{ daily_summaries : "rollup by counting_date"
batches {
INTEGER id PK
TEXT counting_date "NOT NULL, YYYY-MM-DD, cutoff-shifted"
INTEGER batch_number "NOT NULL, per date+camera+label"
TEXT camera_name "NOT NULL, default CC1"
TEXT object_label "NOT NULL, default karung-pakan"
INTEGER count "NOT NULL, gross sack loading"
TEXT start_time "NOT NULL, ISO datetime"
TEXT end_time "NOT NULL, ISO datetime"
TIMESTAMP created_at "default CURRENT_TIMESTAMP"
INTEGER box_loading "NOT NULL, default 0"
INTEGER box_unloading "NOT NULL, default 0"
TEXT model_mode "NOT NULL, default A"
TEXT plate "NOT NULL, default ''"
TEXT do_numbers "NOT NULL, JSON array of No. DO"
INTEGER expected_sack "NOT NULL, default 0"
INTEGER expected_box "NOT NULL, default 0"
INTEGER net_sack "NOT NULL, default 0"
INTEGER net_box "NOT NULL, default 0"
}
delivery_orders {
INTEGER id PK
TEXT counting_date "NOT NULL"
TEXT photo_path "NOT NULL, relative under do_photos/"
TEXT no_do "default ''"
TEXT plate "default ''"
INTEGER expected_sack "default 0"
INTEGER expected_box "default 0"
TEXT ocr_raw "nullable JSON: no_do, plate, ocr_text, engine, ocr_ok, ocr_error"
TEXT status "default 'draft'"
INTEGER batch_id "soft ref to batches.id, nullable"
TIMESTAMP created_at "default CURRENT_TIMESTAMP"
TIMESTAMP updated_at "default CURRENT_TIMESTAMP"
}
daily_summaries {
INTEGER id PK
TEXT counting_date "NOT NULL"
TEXT camera_name "NOT NULL"
TEXT object_label "NOT NULL"
INTEGER total_count "NOT NULL, default 0, SUM(batches.count)"
INTEGER total_batches "NOT NULL, default 0, COUNT(batches.id)"
TIMESTAMP updated_at "default CURRENT_TIMESTAMP"
}
```
## Keys
- `batches`: `UNIQUE(counting_date, batch_number, camera_name, object_label)`
- `daily_summaries`: `UNIQUE(counting_date, camera_name, object_label)`
- `delivery_orders`: `idx_do_date_status` on `(counting_date, status)`
## Relationships (logical)
| From | To | How |
|---|---|---|
| `delivery_orders` → `batches` | N—1 | On stop: `batches.do_numbers` JSON array of `No. DO`; `delivery_orders.batch_id` set best-effort. Active batch: `current_batch.json.do_ids`. |
| `batches` → `daily_summaries` | N—1 | Same `(counting_date, camera_name, object_label)`; stop recompute `SUM(count)`, `COUNT(id)`. |
## Derived columns
- `net_sack = count − sack unloading`, `net_box = box_loading − box_unloading`
(`src/do_batch.py:37`).
- **Sack unloading has no column** — it only survives inside `net_sack`
(legacy rows with `net_sack=0` from the `ALTER TABLE ... DEFAULT 0`
migration are not backfillable).
- History UI shows gross `count` / `box_loading` only (no Net).
## Status (`delivery_orders.status`)
`draft` (photo upload) → `staged` (saved/edited) → `attached` (in
active/stopped batch). Discard stop with both nets 0 returns DOs to
`staged`. `discarded` appears in older notes but **no code writes it** —
discard is a `DELETE`.
## Hazards
- `INSERT OR REPLACE` on `batches` (UNIQUE match = delete+insert) changes
`id` on re-stop → can orphan `delivery_orders.batch_id`.
- Schema divergence: `predict.py init_db()` creates only `batches` +
`daily_summaries` (3 extra cols, no `delivery_orders`); full 3-table
schema (9 extra `batches` cols) comes from `counter_dashboard.py`
`_ensure_db()` at import. Fresh DB touched only by `predict.py` is
incomplete; its DO updates fail silently (`try/except: pass`).
## JSON sidecars + photo tree (not in SQLite)
| Path | Role |
|---|---|
| `$OUTPUT_DIR/batch_mode.json` | `{mode: auto\|do_manual\|manual}` |
| `$OUTPUT_DIR/current_batch.json` | active batch + `do_ids`, plate, expected_*, nets inputs |
| `$OUTPUT_DIR/do_settings.json` | `require_plate`, `require_do`, `ocr_engine` |
| `$OUTPUT_DIR/do_photos/YYYY-MM-DD/do_<uuid>.jpg` | source photos (7-day retention) |
## Sources
- DDL: `counter_dashboard.py:123-198` (`_ensure_db`, superset),
`predict.py:117-166` (`init_db`, partial).
- Writes: `predict.py:249-306`, `counter_dashboard.py:610-668`.
- Doc flow: `docs/do-erd.md` (merged into this file).