Files
canteen-asset-tracker/import_cantaloupe.py
T
shawn 72003e2742 fix: GPS chip visibility and Leaflet Draw geofencing
- Removed hardcoded display:none from GPS center chip on map tab
- Chip now shows when GPS position is obtained in startVisitTracking
- Added leaflet-draw CSS and JS to HTML head
- Added draw control (polygon, rectangle, circle) to map init
- Emits geofenceDrawn custom event for external hooks
2026-05-22 11:50:44 -04:00

175 lines
6.3 KiB
Python

"""Import Cantaloupe machine list XLSX into canteen asset tracker DB.
Mapping (corrected per user feedback):
Asset ID → machine_id (MID — primary search key, e.g. "60006")
Device → description (CC reader telemetry ID, secondary search)
Location → name (human-readable asset name)
Place → room (breakroom name)
Type → stored in description alongside Device
Address+City+State+ZIP → address
Serial Number → serial_number
Make → make
Model → model
Status → all set to "active" (Excel RPC status stored in description only)
Customer → customers table (auto-created)
"""
import sqlite3
import openpyxl
import sys
from pathlib import Path
DB_PATH = str(Path(__file__).parent / "assets.db")
XLSX_PATH = "/home/oplabs/.hermes/profiles/kanban/cache/documents/doc_5c60b6e791ea_Machine List(4).xlsx"
def connect():
conn = sqlite3.connect(DB_PATH)
conn.execute("PRAGMA foreign_keys=ON")
conn.row_factory = sqlite3.Row
return conn
def ensure_makes(conn, make_names):
"""Add missing makes to lookup table."""
existing = {r["name"] for r in conn.execute("SELECT name FROM makes").fetchall()}
for name in make_names:
if name and name not in existing:
conn.execute("INSERT INTO makes (name) VALUES (?)", (name,))
print(f" Added make: {name}")
def ensure_customers(conn, customer_names):
"""Create customer records, return {name: id} map."""
existing = {r["name"]: r["id"] for r in conn.execute("SELECT id, name FROM customers").fetchall()}
for name in customer_names:
if name and name not in existing:
conn.execute("INSERT INTO customers (name) VALUES (?)", (name,))
existing[name] = conn.execute(
"SELECT id FROM customers WHERE name = ?", (name,)
).fetchone()["id"]
return existing
def import_assets():
conn = connect()
# Step 1: Read Excel
print("Reading Excel...")
wb = openpyxl.load_workbook(XLSX_PATH, read_only=True, data_only=True)
ws = wb['Machine List']
# Column index (0-based from header row)
COL = {
"Device": 0, "Location": 1, "Asset ID": 2, "Place": 3, "Type": 4,
"City": 5, "Address": 6,
"Make": 39, "Model": 40, "Serial Number": 37,
"Status": 56, "Customer": 21,
"State": 33, "Postal Code": 34,
}
rows = []
for i, row in enumerate(ws.iter_rows(min_row=2, values_only=True)):
if row[0] is None:
continue
rows.append(row)
print(f"Loaded {len(rows)} rows from Excel")
# Step 2: Collect unique makes and customers
all_makes = set()
all_customers = set()
for r in rows:
make = str(r[COL["Make"]]).strip() if r[COL["Make"]] else ""
if make:
all_makes.add(make)
cust = str(r[COL["Customer"]]).strip() if r[COL["Customer"]] else ""
if cust:
all_customers.add(cust)
print(f"\nUnique makes: {len(all_makes)}{sorted(all_makes)}")
print(f"Unique customers: {len(all_customers)}")
# Step 3: Seed lookup tables
ensure_makes(conn, all_makes)
customer_map = ensure_customers(conn, all_customers)
conn.commit()
# Step 4: Insert assets
inserted = 0
skipped = 0
errors = 0
for r in rows:
device = str(r[COL["Device"]]).strip() if r[COL["Device"]] else ""
location = str(r[COL["Location"]]).strip() if r[COL["Location"]] else ""
place = str(r[COL["Place"]]).strip() if r[COL["Place"]] else ""
machine_type = str(r[COL["Type"]]).strip() if r[COL["Type"]] else ""
address = str(r[COL["Address"]]).strip() if r[COL["Address"]] else ""
city = str(r[COL["City"]]).strip() if r[COL["City"]] else ""
state = str(r[COL["State"]]).strip() if r[COL["State"]] else ""
zip_code = str(r[COL["Postal Code"]]).strip() if r[COL["Postal Code"]] else ""
serial = str(r[COL["Serial Number"]]).strip() if r[COL["Serial Number"]] else ""
make = str(r[COL["Make"]]).strip() if r[COL["Make"]] else ""
model = str(r[COL["Model"]]).strip() if r[COL["Model"]] else ""
raw_status = str(r[COL["Status"]]).strip() if r[COL["Status"]] else ""
customer_name = str(r[COL["Customer"]]).strip() if r[COL["Customer"]] else ""
asset_id_str = str(r[COL["Asset ID"]]).strip() if r[COL["Asset ID"]] else ""
if not asset_id_str or not location:
errors += 1
continue
machine_id = asset_id_str
name = location
# Build full address
parts = [p for p in [address, city, state, zip_code] if p]
full_address = ", ".join(parts)
# Status — all set to active (RPC "On/Action Required/Incompatible" is
# pricing telemetry status, not machine condition. Stored in description.)
status = "active"
# Build description — Device (telemetry ID) + RPC status + Type
desc_parts = []
if device:
desc_parts.append(f"Device: {device}")
if raw_status:
desc_parts.append(f"RPC: {raw_status}")
if machine_type:
desc_parts.append(machine_type)
description = "".join(desc_parts)
# Customer lookup
customer_id = customer_map.get(customer_name)
try:
cursor = conn.execute("""
INSERT INTO assets
(machine_id, name, description, category, status,
make, model, serial_number, address, room,
customer_id)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
""", (
machine_id, name, description, "Equipment", status,
make, model, serial, full_address, place,
customer_id,
))
inserted += 1
except sqlite3.IntegrityError as e:
errors += 1
if errors <= 3:
print(f" SKIP (dupe?): {machine_id}{e}")
conn.commit()
conn.close()
wb.close()
print(f"\n=== Import Summary ===")
print(f" Inserted: {inserted}")
print(f" Skipped: {skipped}")
print(f" Errors: {errors}")
return inserted
if __name__ == "__main__":
count = import_assets()
print(f"\nDone. {count} assets imported.")