feat: add is_disney column for reliable Disney/Non-Disney filtering

- Added is_disney INTEGER DEFAULT 0 column to assets table
- Backfilled 996 Disney assets based on D- customer prefix
- Server: is_disney in AssetCreate/AssetUpdate models, INSERT/UPDATE
- Server: disney_filter query param now uses is_disney column (no JOIN needed)
- import_cantaloupe.py: sets is_disney=1 when customer starts with D-
- import_machines.py: sets is_disney=1 when customer starts with D-
- disney_classify.py: sets is_disney=1 when classifying
- Frontend: new Location Type dropdown with Disney/Non-Disney toggle
  (replaces the __disney__/__non_disney__ park dropdown pseudo-options)
This commit is contained in:
2026-05-22 18:34:13 -04:00
parent 60dfd3434a
commit e8a918fc7b
5 changed files with 39 additions and 25 deletions
+1 -1
View File
@@ -277,7 +277,7 @@ def classify_all_assets(db_path: str) -> dict:
if park:
conn.execute(
"UPDATE assets SET disney_park = ?, updated_at = datetime('now') WHERE id = ?",
"UPDATE assets SET disney_park = ?, is_disney = 1, updated_at = datetime('now') WHERE id = ?",
(park, row['id'])
)
results["classified"].append({
+4 -3
View File
@@ -141,17 +141,18 @@ def import_assets():
# Customer lookup
customer_id = customer_map.get(customer_name)
is_disney = 1 if customer_name.startswith('D-') else 0
try:
cursor = conn.execute("""
INSERT INTO assets
(machine_id, name, description, category, status,
make, model, serial_number, address, room,
customer_id)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
customer_id, is_disney)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
""", (
machine_id, name, description, "Equipment", status,
make, model, serial, full_address, place,
customer_id,
customer_id, is_disney,
))
inserted += 1
except sqlite3.IntegrityError as e:
+4 -3
View File
@@ -63,6 +63,7 @@ def main(xlsx_path):
machine_id = _sanitize_machine_id(str(int(row[2])))
location_str = str(row[1]) if row[1] else '' # "Company - Address"
customer_name = str(row[21]) if row[21] else '' # Customer
is_disney = 1 if customer_name.strip().startswith('D-') else 0
address = str(row[6]) if row[6] else ''
city = str(row[5]) if row[5] else ''
state = str(row[33]) if row[33] else ''
@@ -136,10 +137,10 @@ def main(xlsx_path):
cursor = conn.execute("""
INSERT INTO assets
(machine_id, name, serial_number, make, model, category,
status, address, customer_id, created_at, updated_at, description)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, datetime('now'), datetime('now'), ?)
status, address, customer_id, is_disney, created_at, updated_at, description)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, datetime('now'), datetime('now'), ?)
""", (machine_id, name, serial, make, model, cat,
status, full_address, customer_id,
status, full_address, customer_id, is_disney,
device + ' | Branch: ' + branch if branch else device))
aid = cursor.lastrowid
existing_assets[machine_id] = aid
+11 -8
View File
@@ -165,6 +165,7 @@ def _create_v2_tables(conn: sqlite3.Connection):
longitude REAL DEFAULT NULL,
geofence_radius_meters INTEGER DEFAULT 50,
disney_park TEXT DEFAULT NULL,
is_disney INTEGER DEFAULT 0,
created_at TEXT NOT NULL DEFAULT (datetime('now')),
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
);
@@ -629,6 +630,7 @@ class AssetCreate(BaseModel):
latitude: Optional[float] = None
longitude: Optional[float] = None
geofence_radius_meters: Optional[int] = 50
is_disney: Optional[bool] = None
keys: Optional[List[AssetKey]] = []
badges: Optional[List[str]] = []
@@ -658,6 +660,7 @@ class AssetUpdate(BaseModel):
latitude: Optional[float] = None
longitude: Optional[float] = None
geofence_radius_meters: Optional[int] = None
is_disney: Optional[bool] = None
keys: Optional[List[AssetKey]] = None
badges: Optional[List[str]] = None
@@ -867,7 +870,7 @@ def _build_asset_insert(body: AssetCreate, machine_id: str, name: str):
"make", "model", "address", "building_name", "building_number", "floor",
"room", "trailer_number", "walking_directions", "map_link",
"parking_location", "photo_path", "customer_id", "location_id",
"assigned_to", "latitude", "longitude", "geofence_radius_meters",
"assigned_to", "latitude", "longitude", "geofence_radius_meters", "is_disney",
]
values = (
machine_id, body.serial_number or "", name, body.description or "",
@@ -880,6 +883,7 @@ def _build_asset_insert(body: AssetCreate, machine_id: str, name: str):
body.customer_id, body.location_id, body.assigned_to,
body.latitude, body.longitude,
body.geofence_radius_meters if body.geofence_radius_meters is not None else 50,
body.is_disney if body.is_disney is not None else None,
)
return columns, values
@@ -1010,9 +1014,9 @@ def list_assets(
conditions.append("disney_park = ?")
params.append(disney_park)
if disney_filter == "disney":
conditions.append("(disney_park IS NOT NULL OR c.name LIKE 'D-%')")
conditions.append("is_disney = 1")
elif disney_filter == "non_disney":
conditions.append("(disney_park IS NULL AND (c.name IS NULL OR c.name NOT LIKE 'D-%'))")
conditions.append("(is_disney IS NULL OR is_disney = 0)")
if model:
conditions.append("model = ?")
params.append(model)
@@ -1031,10 +1035,7 @@ def list_assets(
params.extend([like, like, like, like])
where = " AND ".join(conditions)
join_clause = ""
if disney_filter:
join_clause = " LEFT JOIN customers c ON assets.customer_id = c.id"
sql = f"SELECT assets.* FROM assets{join_clause}"
sql = "SELECT * FROM assets"
if where:
sql += f" WHERE {where}"
sql += " ORDER BY created_at DESC LIMIT ? OFFSET ?"
@@ -1042,7 +1043,7 @@ def list_assets(
rows = conn.execute(sql, params).fetchall()
# Get total count for pagination
count_sql = f"SELECT COUNT(*) FROM assets{join_clause}"
count_sql = "SELECT COUNT(*) FROM assets"
if where:
count_sql += f" WHERE {where}"
total = conn.execute(count_sql, params[:len(params)-2]).fetchone()[0]
@@ -1206,6 +1207,8 @@ def update_asset(asset_id: int, body: AssetUpdate):
updates["longitude"] = body.longitude
if body.geofence_radius_meters is not None:
updates["geofence_radius_meters"] = body.geofence_radius_meters
if body.is_disney is not None:
updates["is_disney"] = 1 if body.is_disney else 0
# Auto-populate address from GPS when lat/lng are provided
lat = updates.get("latitude") if "latitude" in updates else None
+19 -10
View File
@@ -1129,13 +1129,22 @@
</select>
</div>
</div>
<div class="fp-row">
<div style="flex:1">
<div class="fp-label">Location Type</div>
<select id="filterDisneyFilter" class="input-field" onchange="onFilterChange()">
<option value="">All locations</option>
<option value="disney">🏰 Disney</option>
<option value="non_disney">🏢 Non-Disney</option>
</select>
</div>
<div style="flex:1"></div>
</div>
<div class="fp-row">
<div style="flex:1">
<div class="fp-label">Disney Park</div>
<select id="filterDisneyPark" class="input-field" onchange="onFilterChange()">
<option value="">All parks</option>
<option value="__disney__">🏰 Disney only</option>
<option value="__non_disney__">🏢 Non-Disney</option>
<option disabled>──────────</option>
<option value="magic-kingdom">🏰 Magic Kingdom</option>
<option value="epcot">🌍 Epcot</option>
@@ -3203,6 +3212,7 @@
function onFilterChange() {
var catEl = document.getElementById('filterCategory');
var makeEl = document.getElementById('filterMake');
var disneyFilterEl = document.getElementById('filterDisneyFilter');
var parkEl = document.getElementById('filterDisneyPark');
var custEl = document.getElementById('filterCustomer');
var locEl = document.getElementById('filterLocation');
@@ -3211,15 +3221,13 @@
assetFilters.category = catEl.value || null;
assetFilters.make = makeEl.value || null;
// Handle Disney/Non-Disney special options
var parkVal = parkEl.value;
if (parkVal === '__disney__') {
// Location Type (Disney/Non-Disney) and specific park are mutually exclusive
var disneyFilterVal = disneyFilterEl.value;
if (disneyFilterVal) {
assetFilters.disney_filter = disneyFilterVal;
assetFilters.disney_park = null;
assetFilters.disney_filter = 'disney';
} else if (parkVal === '__non_disney__') {
assetFilters.disney_park = null;
assetFilters.disney_filter = 'non_disney';
} else {
var parkVal = parkEl.value;
assetFilters.disney_park = parkVal || null;
assetFilters.disney_filter = null;
}
@@ -3329,7 +3337,7 @@
document.getElementById('filterDisneyPark').value = '';
assetFilters.disney_park = null;
} else if (key === 'disney_filter') {
document.getElementById('filterDisneyPark').value = '';
document.getElementById('filterDisneyFilter').value = '';
assetFilters.disney_filter = null;
} else if (key === 'customer_id') {
document.getElementById('filterCustomer').value = '';
@@ -3357,6 +3365,7 @@
document.getElementById('filterCategory').value = '';
document.getElementById('filterMake').value = '';
document.getElementById('filterDisneyPark').value = '';
document.getElementById('filterDisneyFilter').value = '';
document.getElementById('filterCustomer').value = '';
document.getElementById('filterLocation').innerHTML = '<option value="">All locations</option>';
document.getElementById('filterLocation').disabled = true;