Volunteer inventory workflow:
- upload shelf photos
- detect item counts in the fixed 19 categories with Gemini
- store the latest warehouse form totals in the database
- review and edit the detected counts
- submit the reviewed inventory
- compute current inventory levels from the latest warehouse import
- store the result in Postgres
back/: FastAPI backend, Gemini integration, helper scriptsfront/: Next.js frontend for login, upload, and reviewdb/: SQLAlchemy database models and connection setup
The project uses only these 19 categories:
BeveragesJuicesCerealBreakfastMeatFishPoultryFrozenVegetablesFruitsNutsSoupGrainsPastaSnacksSpicesSaucesCondimentsMisc Products
Create back/.env:
DB_HOST=inventory-db.cdkgm4c2klqg.us-east-2.rds.amazonaws.com
DB_PORT=5432
DB_NAME=inventory
DB_USER=admin0
DB_PASSWORD=your_password_here
GEMINI_API_KEY=your_gemini_key_here
DRY_RUN=falseBackend scripts load back/.env directly.
cd back
python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
uvicorn main:app --reload --port 8000Backend URLs:
http://localhost:8000http://localhost:8000/docs
cd front
npm install
npm run devFrontend URL:
http://localhost:3000
Current demo login:
- Pantry ID:
admin - Password:
password
That login currently maps to pantry id 1.
- Start the backend
- Start the frontend
- Open
http://localhost:3000/login - Log in with
admin/password - Make sure a warehouse import exists for that pantry
- Upload a shelf photo
- Review the detected counts
- Edit values if needed
- Click
Submit inventory - Review the returned ratio and
High / Mid / Lowlevel for each category
Direct upload with curl:
curl -X POST http://localhost:8000/upload \
-F "files=@/full/path/to/your-image.jpg" \
-F "pantry_id=1"That route:
- validates image files
- sends the first image to Gemini
- returns detected inventory
- stores a draft for the review page
The pulled schema stores warehouse form imports in inventory_runs too, using:
source = "warehouse-snapshot"inventory = parsed warehouse totalscomparison.note = optional import context
Why this matters:
- this warehouse row is the denominator source for later volunteer ratio calculations
- the newest
warehouse-snapshotrow for a pantry is the one the backend compares against
Store a parsed warehouse form with:
curl -X POST http://localhost:8000/warehouse/inventory/snapshot \
-H "Content-Type: application/json" \
-d '{
"pantryId": "1",
"inventory": {
"Beverages": 10,
"Juices": 5,
"Cereal": 20,
"Breakfast": 8,
"Meat": 4,
"Fish": 3,
"Poultry": 6,
"Frozen": 7,
"Vegetables": 12,
"Fruits": 9,
"Nuts": 2,
"Soup": 5,
"Grains": 11,
"Pasta": 13,
"Snacks": 15,
"Spices": 6,
"Sauces": 4,
"Condiments": 3,
"Misc Products": 1
},
"files": []
}'Notes for order-form extraction:
- Upload all pages of the same form in one request.
- Backend parses each page and sums category totals across pages.
- Extraction uses handwritten
Amount Shippedvalues as-is (no unit-size/case multiplication).
Use your manager form extraction platform to send real warehouse totals to:
POST /warehouse/inventory/snapshot.
The active workflow computes status from:
current pantry stock / latest warehouse import
Thresholds:
Highif ratio> 0.70Midif ratio> 0.30and<= 0.70Outif ratio== 0Lowif ratio> 0and<= 0.30
Volunteer submit is stored in inventory_runs with:
source = "volunteer-submit"inventory = current reviewed pantry stockcomparison.warehouseRunId = the warehouse run used as denominatorcomparison.ratios = computed category ratioscomparison.levels = computed High / Mid / Low valuescomparison.summaryCounts = High / Mid / Low totals
This keeps the table schema lean while still preserving the full calculation context in one row.
Example volunteer submit for pantry 1:
curl -X POST http://localhost:8000/volunteer/inventory/submit \
-H "Content-Type: application/json" \
-d '{
"pantryId": "1",
"inventory": {
"Beverages": 10,
"Juices": 5,
"Cereal": 20,
"Breakfast": 8,
"Meat": 4,
"Fish": 3,
"Poultry": 6,
"Frozen": 7,
"Vegetables": 12,
"Fruits": 9,
"Nuts": 2,
"Soup": 5,
"Grains": 11,
"Pasta": 13,
"Snacks": 15,
"Spices": 6,
"Sauces": 4,
"Condiments": 3,
"Misc Products": 1
}
}'List pantries:
python back/list_pantries.pyRead the latest stored run:
python back/read_latest_inventory_run.pyRead the most recent 5 runs:
python back/read_recent_inventory_runs.pyRun the automated backend workflow check:
python back/run_volunteer_workflow_check.pyThat helper script:
- posts a sample volunteer submission
- reads the latest row from
inventory_runs
Run the active workflow/domain tests:
python -m unittest back/tests/test_inventory_domain.pyRun lint:
cd front
npm run lintManual verification checklist:
- Volunteer flow:
- Upload page shows stepper + empty state before files.
- Detection succeeds and redirects to review.
- Review page allows edit + sticky submit button works on mobile.
- Manager flow:
- Upload order-form pages, click extract, redirect to review.
- Review page grouped categories are editable.
- Sticky save baseline button works on mobile.
- Director flow:
- Dashboard filters/search work.
- Removing pantry credentials requires confirmation modal.
- Account switching:
Switch accountalways returns to/for role selection.
- Accessibility quick pass:
- Keyboard navigation reaches all key controls.
- Focus rings are visible on buttons/inputs.
- Alerts are announced for status/error messages.
db/init_db() is currently destructive because it drops tables before recreating them. Do not use it against shared or production-like data unless you explicitly intend to wipe tables.