diff --git a/__pycache__/server.cpython-311.pyc b/__pycache__/server.cpython-311.pyc index 7752140..c4fc1f4 100644 Binary files a/__pycache__/server.cpython-311.pyc and b/__pycache__/server.cpython-311.pyc differ diff --git a/server.py b/server.py index 90904f1..8c738fa 100644 --- a/server.py +++ b/server.py @@ -116,6 +116,11 @@ async def analyze_photo(file: UploadFile = File(...)): exif_result = extract_exif(contents) ocr_result = run_ocr(contents) if HAS_TESSERACT else {"available": False, "text": ""} + # Extract machine ID from OCR for auto-lookup + machine_id = None + if ocr_result.get("match_5dash6"): + machine_id = re.sub(r"\D", "", ocr_result["match_5dash6"])[-5:] + return { "filename": file.filename, "saved_as": fname, @@ -123,9 +128,60 @@ async def analyze_photo(file: UploadFile = File(...)): "file_size_kb": round(file_size / 1024, 1), "exif": exif_result, "ocr": ocr_result, + "machine_id": machine_id, } +@app.get("/api/lookup") +async def lookup_asset(machine_id: str = ""): + """Look up an asset by machine_id in the canteen assets database.""" + if not machine_id or not machine_id.strip(): + return {"found": False, "reason": "No machine_id provided"} + + import sqlite3 + from pathlib import Path as _Path + + db_path = _Path(__file__).parent.parent / "canteen-asset-tracker" / "assets.db" + if not db_path.exists(): + return {"found": False, "reason": f"Database not found at {db_path}"} + + try: + conn = sqlite3.connect(str(db_path)) + conn.row_factory = sqlite3.Row + row = conn.execute( + "SELECT id, machine_id, name, category, status, address, building_name, " + "floor, room, latitude, longitude, make, model, description, photo_path " + "FROM assets WHERE machine_id = ?", + (machine_id.strip(),), + ).fetchone() + conn.close() + + if row: + return { + "found": True, + "asset": { + "id": row["id"], + "machine_id": row["machine_id"], + "name": row["name"], + "category": row["category"], + "status": row["status"], + "address": row["address"], + "building_name": row["building_name"], + "floor": row["floor"], + "room": row["room"], + "latitude": row["latitude"], + "longitude": row["longitude"], + "make": row["make"], + "model": row["model"], + "description": row["description"], + "photo_path": row["photo_path"], + }, + } + return {"found": False, "reason": f"No asset with machine_id '{machine_id}'"} + except Exception as e: + return {"found": False, "reason": str(e)} + + @app.get("/api/uploads") async def list_uploads(): """List previously uploaded files.""" diff --git a/static/index.html b/static/index.html index 2df721e..f1c3832 100644 --- a/static/index.html +++ b/static/index.html @@ -155,6 +155,23 @@ } .filter-chip.active { background: var(--accent); border-color: var(--accent); color: #fff; } + /* Asset match card */ + .asset-card { + background: var(--card2); border-radius: var(--radius-sm); + padding: 12px; margin-top: 10px; border-left: 3px solid var(--green); + } + .asset-card .asset-name { + font-size: 15px; font-weight: 700; margin-bottom: 4px; + } + .asset-card .asset-meta { + font-size: 12px; color: var(--text2); + display: flex; flex-wrap: wrap; gap: 8px; + } + .asset-card .asset-meta span { + background: var(--card); padding: 2px 8px; border-radius: 10px; + white-space: nowrap; + } + /* Server section */ .section { background: var(--card); border-radius: var(--radius); @@ -410,7 +427,10 @@ async function uploadSelected() { html += '
' + esc(ocr.raw_text) + '
'; } if (ocr.match_5dash6) { - html += '
✅ Matched: ' + esc(ocr.match_5dash6) + ' → machine ID: ' + ocr.match_5dash6.replace(/[^0-9]/g,'').slice(-5) + '
'; + const mid = ocr.match_5dash6.replace(/[^0-9]/g,'').slice(-5); + html += '
✅ Matched: ' + esc(ocr.match_5dash6) + ' → machine ID: ' + mid + '
'; + // Auto-lookup + lookupAsset(mid); } else if (ocr.match_5plus) { html += '
⚠️ Digits: ' + esc(ocr.match_5plus) + ' (no 5-6 pattern)
'; } else { @@ -424,6 +444,42 @@ async function uploadSelected() { } } +async function lookupAsset(machineId) { + const div = document.getElementById('serverResults'); + try { + const resp = await fetch('/api/lookup?machine_id=' + encodeURIComponent(machineId)); + const data = await resp.json(); + if (data.found) { + const a = data.asset; + let extra = ''; + if (a.latitude && a.longitude) { + extra += '📍 ' + Number(a.latitude).toFixed(5) + ', ' + Number(a.longitude).toFixed(5) + ''; + } + if (a.address) extra += '🏠 ' + esc(a.address) + ''; + if (a.building_name) extra += '🏢 ' + esc(a.building_name) + ''; + if (a.floor) extra += '📶 Floor ' + esc(a.floor) + ''; + if (a.room) extra += '🚪 ' + esc(a.room) + ''; + + div.innerHTML += + '
' + + '
' + esc(a.name) + '
' + + '
' + + '🆔 ' + esc(a.machine_id) + '' + + '📦 ' + esc(a.category) + '' + + '' + (a.status === 'active' ? '🟢 Active' : '⚪ ' + esc(a.status)) + '' + + extra + + '
' + + '
'; + } else { + div.innerHTML += + '
⚠️ No asset found for machine ID ' + esc(machineId) + '
'; + } + } catch (e) { + div.innerHTML += + '
❌ Lookup error: ' + esc(e.message) + '
'; + } +} + function resetAll() { allPhotos = []; selectedIdx = -1; diff --git a/uploads/8d25107e64874e48a5ef6d8356feba9a.jpg b/uploads/8d25107e64874e48a5ef6d8356feba9a.jpg new file mode 100644 index 0000000..9880972 Binary files /dev/null and b/uploads/8d25107e64874e48a5ef6d8356feba9a.jpg differ diff --git a/uploads/b083576095b6499b84eb896c2ee85735.jpg b/uploads/b083576095b6499b84eb896c2ee85735.jpg new file mode 100644 index 0000000..3fdb023 Binary files /dev/null and b/uploads/b083576095b6499b84eb896c2ee85735.jpg differ diff --git a/uploads/d54b437bb36744928701fabdb8c168ed.jpg b/uploads/d54b437bb36744928701fabdb8c168ed.jpg new file mode 100644 index 0000000..aa742b2 Binary files /dev/null and b/uploads/d54b437bb36744928701fabdb8c168ed.jpg differ