feat(inventory): photo-first drafts + on-device OCR (digitization R2+R4)
Lower the bulk-digitization cliff with two more routes on top of the already-landed CSV import and "save and add another": - Photo-first drafts (capture now, catalogue later): burst-capture photos (camera or multi-gallery) into unnamed draft varieties, shown in a "to catalogue" tray, hidden from the main list until named. Adds Variety.isDraft (schema), addDraftVariety/watchDrafts/nameDraft, the triage sheet and the inventory banner. - On-device OCR label suggestion (Tesseract, offline, no Google): a "Suggest name from photo" button in the naming dialog behind a LabelTextExtractor interface (Tesseract on Android/iOS, no-op elsewhere). Reads the largest print via hOCR bounding boxes, drops boilerplate/low-confidence noise, preprocesses (grayscale, contrast, upscale) and sweeps rotations (0-315 deg) so tilted packets still read. Bundles tessdata_fast eng+spa; validated on-device against real packets. The photo is written to a temp file deleted immediately in a finally block (the plugin needs a path) - a bounded, documented exception to no-plaintext-at-rest. This commit also carries the co-developed schema evolution v5 to v8 that shares these files (organic flag, species viability years, crop calendar, lot provenance/abundance/preservation format, condition checks) plus their exports/migrations and i18n. Tests: CSV/draft/OCR unit + widget + migration green in isolation. Note: the full widget suite currently hangs (>10 min) - under investigation.
This commit is contained in:
parent
12a2ee2d64
commit
6809dc6143
89 changed files with 17141 additions and 228 deletions
|
|
@ -1,76 +1,90 @@
|
|||
{
|
||||
"version": 1,
|
||||
"note": "Curated starter set of Iberian horticultural species. Family and common names are stable; wikidata_qid/gbif_key are intentionally omitted here and enriched later from the varilla (Wikidata CC0 + GBIF) work.",
|
||||
"version": 2,
|
||||
"note": "Curated starter set of common horticultural species — a seed, not a region-locked list. `common` is a locale-keyed map extensible to ANY language (add es/en/fr/de/ar/zh/…); the app resolves names by the user's locale and falls back gracefully, so this must never assume es/en only. Names are enriched later per-locale from the varilla (Wikidata CC0 + GBIF) work, which carries vernacular names in every language. viability_years is typical seed longevity under normal home storage (public-domain agricultural-extension figures); a conservative single value, used to warn about aging lots.",
|
||||
"species": [
|
||||
{
|
||||
"scientific_name": "Solanum lycopersicum",
|
||||
"family": "Solanaceae",
|
||||
"common": { "es": ["Tomate"], "en": ["Tomato"] }
|
||||
"common": { "es": ["Tomate"], "en": ["Tomato"] },
|
||||
"viability_years": 5
|
||||
},
|
||||
{
|
||||
"scientific_name": "Solanum melongena",
|
||||
"family": "Solanaceae",
|
||||
"common": { "es": ["Berenjena"], "en": ["Aubergine", "Eggplant"] }
|
||||
"common": { "es": ["Berenjena"], "en": ["Aubergine", "Eggplant"] },
|
||||
"viability_years": 5
|
||||
},
|
||||
{
|
||||
"scientific_name": "Capsicum annuum",
|
||||
"family": "Solanaceae",
|
||||
"common": { "es": ["Pimiento", "Guindilla"], "en": ["Pepper", "Chilli"] }
|
||||
"common": { "es": ["Pimiento", "Guindilla"], "en": ["Pepper", "Chilli"] },
|
||||
"viability_years": 3
|
||||
},
|
||||
{
|
||||
"scientific_name": "Phaseolus vulgaris",
|
||||
"family": "Fabaceae",
|
||||
"common": { "es": ["Judía", "Alubia", "Habichuela"], "en": ["Common bean"] }
|
||||
"common": { "es": ["Judía", "Alubia", "Habichuela"], "en": ["Common bean"] },
|
||||
"viability_years": 3
|
||||
},
|
||||
{
|
||||
"scientific_name": "Vicia faba",
|
||||
"family": "Fabaceae",
|
||||
"common": { "es": ["Haba"], "en": ["Broad bean", "Fava bean"] }
|
||||
"common": { "es": ["Haba"], "en": ["Broad bean", "Fava bean"] },
|
||||
"viability_years": 4
|
||||
},
|
||||
{
|
||||
"scientific_name": "Pisum sativum",
|
||||
"family": "Fabaceae",
|
||||
"common": { "es": ["Guisante"], "en": ["Pea"] }
|
||||
"common": { "es": ["Guisante"], "en": ["Pea"] },
|
||||
"viability_years": 3
|
||||
},
|
||||
{
|
||||
"scientific_name": "Zea mays",
|
||||
"family": "Poaceae",
|
||||
"common": { "es": ["Maíz"], "en": ["Maize", "Corn"] }
|
||||
"common": { "es": ["Maíz"], "en": ["Maize", "Corn"] },
|
||||
"viability_years": 2
|
||||
},
|
||||
{
|
||||
"scientific_name": "Cucurbita pepo",
|
||||
"family": "Cucurbitaceae",
|
||||
"common": { "es": ["Calabacín", "Calabaza"], "en": ["Courgette", "Zucchini", "Squash"] }
|
||||
"common": { "es": ["Calabacín", "Calabaza"], "en": ["Courgette", "Zucchini", "Squash"] },
|
||||
"viability_years": 5
|
||||
},
|
||||
{
|
||||
"scientific_name": "Cucumis sativus",
|
||||
"family": "Cucurbitaceae",
|
||||
"common": { "es": ["Pepino"], "en": ["Cucumber"] }
|
||||
"common": { "es": ["Pepino"], "en": ["Cucumber"] },
|
||||
"viability_years": 5
|
||||
},
|
||||
{
|
||||
"scientific_name": "Lactuca sativa",
|
||||
"family": "Asteraceae",
|
||||
"common": { "es": ["Lechuga"], "en": ["Lettuce"] }
|
||||
"common": { "es": ["Lechuga"], "en": ["Lettuce"] },
|
||||
"viability_years": 3
|
||||
},
|
||||
{
|
||||
"scientific_name": "Beta vulgaris",
|
||||
"family": "Amaranthaceae",
|
||||
"common": { "es": ["Acelga", "Remolacha"], "en": ["Chard", "Beetroot"] }
|
||||
"common": { "es": ["Acelga", "Remolacha"], "en": ["Chard", "Beetroot"] },
|
||||
"viability_years": 4
|
||||
},
|
||||
{
|
||||
"scientific_name": "Allium cepa",
|
||||
"family": "Amaryllidaceae",
|
||||
"common": { "es": ["Cebolla"], "en": ["Onion"] }
|
||||
"common": { "es": ["Cebolla"], "en": ["Onion"] },
|
||||
"viability_years": 1
|
||||
},
|
||||
{
|
||||
"scientific_name": "Allium sativum",
|
||||
"family": "Amaryllidaceae",
|
||||
"common": { "es": ["Ajo"], "en": ["Garlic"] }
|
||||
"common": { "es": ["Ajo"], "en": ["Garlic"] },
|
||||
"viability_years": 1
|
||||
},
|
||||
{
|
||||
"scientific_name": "Daucus carota",
|
||||
"family": "Apiaceae",
|
||||
"common": { "es": ["Zanahoria"], "en": ["Carrot"] }
|
||||
"common": { "es": ["Zanahoria"], "en": ["Carrot"] },
|
||||
"viability_years": 3
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
|||
18
apps/app_seeds/assets/ocr_fixtures/README.md
Normal file
18
apps/app_seeds/assets/ocr_fixtures/README.md
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
# OCR validation fixtures
|
||||
|
||||
Real packet photos used by `integration_test/ocr_label_test.dart` to check that
|
||||
on-device Tesseract reads the variety name correctly.
|
||||
|
||||
Drop your photos here with these names:
|
||||
|
||||
- `aubergine.jpg` — the AUBERGINE BLACK BEAUTY packet
|
||||
- `cucumber.jpg` — the CUCUMBER SUPERMARKETER packet
|
||||
|
||||
Then run on a connected Android device/emulator (native OCR does not run on the
|
||||
host test VM):
|
||||
|
||||
flutter test integration_test/ocr_label_test.dart -d <device>
|
||||
|
||||
Each test prints `OCR[<file>] => "<suggestion>"` so you can see exactly what
|
||||
Tesseract read. These fixtures are only for validation — remove the folder from
|
||||
`pubspec.yaml` assets before shipping if you don't want them in the bundle.
|
||||
BIN
apps/app_seeds/assets/ocr_fixtures/aubergine.jpg
Normal file
BIN
apps/app_seeds/assets/ocr_fixtures/aubergine.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 745 KiB |
BIN
apps/app_seeds/assets/ocr_fixtures/cucumber.jpg
Normal file
BIN
apps/app_seeds/assets/ocr_fixtures/cucumber.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 718 KiB |
BIN
apps/app_seeds/assets/tessdata/eng.traineddata
Normal file
BIN
apps/app_seeds/assets/tessdata/eng.traineddata
Normal file
Binary file not shown.
BIN
apps/app_seeds/assets/tessdata/spa.traineddata
Normal file
BIN
apps/app_seeds/assets/tessdata/spa.traineddata
Normal file
Binary file not shown.
6
apps/app_seeds/assets/tessdata_config.json
Normal file
6
apps/app_seeds/assets/tessdata_config.json
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"files": [
|
||||
"eng.traineddata",
|
||||
"spa.traineddata"
|
||||
]
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue