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
This commit is contained in:
@@ -0,0 +1,174 @@
|
||||
"""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.")
|
||||
Reference in New Issue
Block a user