226 lines
9.2 KiB
Markdown
226 lines
9.2 KiB
Markdown
# Field Map — Canteen Asset Tracker
|
|
|
|
Every column in `assets.db`, how each pipeline touches it.
|
|
|
|
## Source Legend
|
|
|
|
| Source | Pipeline | File(s) | How to Run |
|
|
|--------|----------|---------|------------|
|
|
| **MSFS** | ADB pull → Dynamics 365 extraction | `pull_msfs_data.py` → `sync_assets_to_canteen.py` | Button on extraction viewer `:8910` **Manual** |
|
|
| **CANT** | Cantaloupe staged import | `cantaloupe_sync.py` in admin server | Dashboard upload **Manual** |
|
|
| **SEED** | Seed Excel import | `parser.py` → `db_writer.py` → `sync.py` | CLI `--mode=seed` or `--mode=update` **Manual** |
|
|
| **APP** | Main app (technician check-in, photo, GPS) | `server.py` | Used by techs in field |
|
|
| **ADMIN** | Admin dashboard edits | `admin_server.py` | Dashboard Cleanup/Mass Edit |
|
|
|
|
## Policy Legend
|
|
|
|
- **always** — pipeline overwrites this field every run
|
|
- **if_empty** — only filled when currently NULL/empty
|
|
- **never** — pipeline never touches this column
|
|
- **if_numeric** — only overwritten if value is bare number (Cantaloupe default ID)
|
|
|
|
---
|
|
|
|
## 🔵 MSFS Sync — Field Policy
|
|
|
|
| Column | Policy | Dynamics 365 Source |
|
|
|--------|--------|---------------------|
|
|
| `serial_number` | always | `hsl_serialnumber` |
|
|
| `connect_id` | always | `hsl_connectid` |
|
|
| `equipment_id` | always | `hsl_equipmentid` |
|
|
| `canteen_connect_guid` | always | `hsl_canteenconnectguid` |
|
|
| `barcode` | always | `hsl_barcode` |
|
|
| `manufacturer` | always | `hsl_manufacturertext` |
|
|
| `model` | always | `hsl_modeltext` or `hsl_modelname` |
|
|
| `route_name` | always | `hsl_routeidname` |
|
|
| `install_date` | always | `hsl_opendate` |
|
|
| `device` | always | `msdyn_deviceid` |
|
|
| `name` | if_numeric | `msdyn_name` or `hsl_name` |
|
|
| `company` | if_empty | Account `name` |
|
|
| `customer_name` | if_empty | Account `name` |
|
|
| `place` | if_empty | Account `address1_city` |
|
|
| `address` | if_empty | Account `address1_line1` + `line2` |
|
|
| `make` | **never** | _(uses `manufacturer` → separate column)_ |
|
|
| `building_name` | **never** | — |
|
|
| `floor` | **never** | — |
|
|
| `room` | **never** | — |
|
|
| `building_number` | **never** | — |
|
|
| `trailer_number` | **never** | — |
|
|
| `location_area` | **never** | — |
|
|
| `latitude` | **never** | _(mapped but only if NULL in code)_ |
|
|
| `longitude` | **never** | _(mapped but only if NULL in code)_ |
|
|
| `photo_path` | **never** | — |
|
|
| `category` | **never** | — |
|
|
| `status` | **never** | — |
|
|
| `description` | **never** | — |
|
|
|
|
> All other columns (sales data, alerts, disney flags, etc.) are **never** touched by MSFS sync.
|
|
|
|
---
|
|
|
|
## 🟡 Cantaloupe Sync — Field Policy
|
|
|
|
| Column | Policy | Notes |
|
|
|--------|--------|-------|
|
|
| `machine_id` | never (match key) | — |
|
|
| `serial_number` | always | — |
|
|
| `name` | always | — |
|
|
| `description` | always | — |
|
|
| `category` | always | Validated against categories table |
|
|
| `status` | always | Validated against statuses |
|
|
| `make` | always | — |
|
|
| `model` | always | — |
|
|
| `address` | always | — |
|
|
| `building_name` | always | — |
|
|
| `building_number` | always | — |
|
|
| `floor` | always | — |
|
|
| `room` | always | — |
|
|
| `trailer_number` | always | — |
|
|
| `walking_directions` | always | — |
|
|
| `map_link` | always | — |
|
|
| `parking_location` | always | — |
|
|
| `photo_path` | always | — |
|
|
| `latitude` | always | — |
|
|
| `longitude` | always | — |
|
|
| `geofence_radius_meters` | always | — |
|
|
| `dex_report_date` | always | — |
|
|
| `deployed` | always | — |
|
|
| `pulled_date` | always | — |
|
|
| `customer_id` | always | Via customer name lookup/creation |
|
|
| `location_id` | always | Via location name lookup/creation |
|
|
| `is_disney` | always | Derived from customer name prefix `D-` |
|
|
|
|
> **No preserve logic.** Any non-empty field in the import file overwrites the existing value.
|
|
|
|
---
|
|
|
|
## 🟢 Seed Import — Field Policy (db_writer.py)
|
|
|
|
### Seed Mode (`--mode=seed`)
|
|
**DESTROYS AND RE-INSERTS** — `DELETE FROM assets` first, then inserts all rows fresh.
|
|
|
|
### Update Mode (`--mode=update`)
|
|
Upsert by `machine_id`. **MSFS is primary data source** — seed fills gaps only.
|
|
|
|
**ALL fields** are treated as ONLY_IF_EMPTY for existing records:
|
|
| Column | Policy | Excel Source / Generation |
|
|
|--------|--------|--------------------------|
|
|
| `serial_number` | if_empty | Serial Number column + OCR corrections |
|
|
| `name` | if_empty | Generated: `{make} @ {place}` |
|
|
| `make` | if_empty | Parsed from Type column |
|
|
| `model` | if_empty | Parsed from Type column |
|
|
| `class` | if_empty | Parsed from Type column |
|
|
| `is_glass_front` | if_empty | Parsed from Type column |
|
|
| `company` | if_empty | Parsed from Customer column |
|
|
| `place` | if_empty | Parsed from Place column |
|
|
| `building_name` | if_empty | Parsed from Place column |
|
|
| `suite` | if_empty | Parsed from Place column |
|
|
| `room` | if_empty | Parsed from Place column |
|
|
| `trailer` | if_empty | Parsed from Place column |
|
|
| `floor` | if_empty | Parsed from Place column |
|
|
| `zone` | if_empty | Parsed from Place column |
|
|
| `zone_type` | if_empty | Parsed from Place column |
|
|
| `address` | if_empty | Address column |
|
|
| `location_area` | if_empty | City column |
|
|
| `latitude` | if_empty | Excel (existing GPS preserved) |
|
|
| `longitude` | if_empty | Excel (existing GPS preserved) |
|
|
| `photo_path` | if_empty | Excel (existing photo preserved) |
|
|
| `install_date` | if_empty | Added Date (existing date preserved) |
|
|
| `deployed` | if_empty | Deployed column (existing preserved) |
|
|
| `pulled_date` | if_empty | Pulled Date (existing preserved) |
|
|
| `dex_report_date` | if_empty | Last Dex Report Time |
|
|
| `disney_park` | if_empty | Parsed from Customer column |
|
|
| `disney_area` | if_empty | Derived from disney_park |
|
|
| `remote_pricing_status` | if_empty | Status column |
|
|
| `alerts` | if_empty | Alerts column + Coil/Product Alerts |
|
|
| `telemetry_provider` | if_empty | Parsed from Device column |
|
|
| `card_reader_brand` | if_empty | Parsed from Device column |
|
|
| `priority` | if_empty | Computed from yearly_sales |
|
|
| `yearly_sales` | if_empty | Yearly Sales column |
|
|
| `monthly_sales` | if_empty | Monthly Sales column |
|
|
| `weekly_sales` | if_empty | Weekly Sales column |
|
|
| `days_since_restock` | if_empty | Days Since Restock column |
|
|
| `prepick_group` | if_empty | Prepick Group column |
|
|
| `has_cashless` | if_empty | Has Cashless column |
|
|
|
|
> **New assets** (not found in DB by machine_id) are inserted with full seed data.
|
|
> **Existing assets**: only empty/null fields are filled. Any field already populated by MSFS, cleanup, or field use is preserved.
|
|
|
|
---
|
|
|
|
## 🟠 Seed Import — Sync (`sync.py` → main app DB)
|
|
|
|
After `db_writer.py`, this script pushes the seed DB into the main app's schema.
|
|
**Now follows ONLY_IF_EMPTY policy** for existing assets — same as db_writer update mode.
|
|
|
|
| Column | Policy |
|
|
|--------|--------|
|
|
| `machine_id` | never (match key) |
|
|
| `serial_number` | if_empty |
|
|
| `name` | if_empty |
|
|
| `description` | if_empty |
|
|
| `category` | if_empty |
|
|
| `status` | if_empty |
|
|
| `make` | if_empty |
|
|
| `model` | if_empty |
|
|
| `address` | if_empty |
|
|
| `building_name` | if_empty |
|
|
| `building_number` | if_empty |
|
|
| `floor` | if_empty |
|
|
| `room` | if_empty |
|
|
| `trailer_number` | if_empty |
|
|
| `walking_directions` | if_empty |
|
|
| `map_link` | if_empty |
|
|
| `parking_location` | if_empty |
|
|
| `photo_path` | if_empty |
|
|
| `latitude` | if_empty |
|
|
| `longitude` | if_empty |
|
|
| `geofence_radius_meters` | if_empty |
|
|
| `disney_park` | if_empty |
|
|
| `is_disney` | if_empty |
|
|
| `dex_report_date` | if_empty |
|
|
| `install_date` | if_empty |
|
|
| `deployed` | if_empty |
|
|
| `pulled_date` | if_empty |
|
|
| `customer_id` | if_empty (via upsert customers table) |
|
|
| `location_id` | if_empty (via upsert locations table) |
|
|
|
|
> **Also creates/updates** `customers` and `locations` tables.
|
|
|
|
---
|
|
|
|
## ⚪ Admin Tools — Policy
|
|
|
|
### Batch Update (Cleanup page)
|
|
- `make`, `model`, `name`, `company`, `place`, `building_name`, `building_number`, `floor`, `room`, `location_area`, `address`, `category`, `status`, `customer_name`
|
|
- **Always overwrites** with user-provided value.
|
|
|
|
### Clear GPS
|
|
- `latitude`, `longitude` — set to NULL
|
|
|
|
### Mass Edit (existing tool)
|
|
- Same allowed fields as Batch Update.
|
|
|
|
---
|
|
|
|
## ⚫ Main App (Tech Field) — What Touches What
|
|
|
|
| Action | Fields Affected |
|
|
|--------|----------------|
|
|
| Barcode scan check-in | `updated_at` |
|
|
| GPS check-in | `latitude`, `longitude`, `updated_at` |
|
|
| Photo upload | `photo_path`, `updated_at` |
|
|
| OCR scan (name search) | (read-only) |
|
|
| Register new asset | All columns with defaults |
|
|
|
|
---
|
|
|
|
## Pipeline Activity Status
|
|
|
|
| Pipeline | Currently Scheduled? | Frequency | Risk to Cleanup |
|
|
|----------|--------------------|-----------|-----------------|
|
|
| **MSFS Sync** | 🔴 **Manual only** | On-demand via extraction viewer | ✅ GPS never touched. Preserves name/company/place/building/floor. Only `model` overwritten. |
|
|
| **Cantaloupe Sync** | 🔴 **No cron** | Manual upload on admin dashboard | ⚠️ Preserves GPS now (fixed — none from Cantaloupe). Still overwrites other fields. |
|
|
| **Seed Import** `--mode=update` | 🔴 **Manual CLI only** | `python3 main.py -m update` | ✅ **GPS never touched** (check-in only). Fills gaps only for other fields. |
|
|
| **Seed Import** `--mode=seed` | 🔴 **Manual CLI only** | `python3 main.py -m seed` | 🔥 **NUCLEAR** — `DELETE FROM assets`. Full re-insert. GPS set to NULL. |
|