Commit Graph

40 Commits

Author SHA1 Message Date
serversdown 77483c2186 feat(projects): Tidy page for fuzzy-detecting + bulk-merging duplicate projects
Phase 5b first slice.  Surfaces near-duplicate projects (typo variants,
abbreviation differences, spacing variations like "SR81" vs "SR 81")
as side-by-side pairs the operator can merge with one click.

Backend (backend/services/project_tidy.py):
- find_duplicate_pairs(db, threshold=0.85) walks all active projects and
  computes rapidfuzz.WRatio similarity for every pair.  Pre-filters
  too-short normalised names (< 4 chars) to avoid noise.  Skips
  soft-deleted projects.  Returns pairs sorted by score desc, then by
  total content (more assignments → review first).
- Each pair carries a suggested merge target with a human-readable
  reason.  Priorities (in order): manual source over parser source,
  populated project_number, more locations, more assignments, shorter
  name.  Operator can override the suggestion by clicking the OTHER
  direction button.
- O(N^2) over project count.  Fine up to ~500 projects.  Token-prefix
  blocking is the obvious next optimisation if it becomes slow.

Backend (backend/routers/projects.py):
- GET /api/projects/admin/duplicate_pairs?threshold=&max_pairs=  returns
  pairs as JSON for the Tidy page.

Frontend (templates/admin/project_tidy.html):
- New admin page at /settings/developer/project-tidy.  Threshold selector
  (95% / 90% / 85% / 80%) at the top; rescan button next to it; auto-
  scans on load.
- Each pair card shows side-by-side project summaries (name, project_
  number, client, source-badge, location/assignment counts) with the
  suggested target visually highlighted (orange border).  Three buttons:
  "Merge A → B", "Merge B → A", "Not a dup" (hide locally).
- Click-to-merge opens a native confirm with the preview totals
  (assignments/sessions/data files moving, consolidations) — same data
  the project_header.html merge modal shows.  On confirm, hits the
  existing /merge_into endpoint and re-scans automatically.
- Source badges distinguish parser-created (`metadata_backfill`) from
  manual projects — at a glance the operator can see "this duplicate is
  parser-generated; safe to merge into the manual one".

Frontend (templates/admin/metadata_backfill.html):
- Apply-result handling now surfaces failed[] cluster reasons in a
  dedicated failure panel (bottom-left, dismissable).  Previously a 200
  OK with all-failures showed a misleading "1 cluster applied" success
  toast because the count and the failure list weren't being reconciled.
  This bit us during the DB-revert recovery earlier — the
  project_modules table was missing, every apply silently rolled back,
  user saw success toasts.  Fixed.

Smoke-verified against current state (10K events, 9 projects, post-
merge): tool correctly finds 0 pairs at threshold 0.85 (data is clean),
1 false-positive at 0.70 (two unrelated projects sharing the token "81"
— example of why the 0.85 default is correct).

Settings link added under Developer → Project Tidy.

Phase 5c (swap-detection daily background job + notification inbox)
remains deferred to the next session.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-12 21:29:50 +00:00
serversdown b1c2a1d778 feat(projects): "Merge into…" button to consolidate duplicate projects
Operator-facing tool for cleaning up duplicate projects.  Common after
the metadata-backfill parser auto-creates near-duplicates from operator
name variations ("SR81" vs "SR 81", "Swank-Karns Crossing" vs
"Swank-Karns Crossings", "Trumbull-Bryman Mont.Dam" vs
"Trumbull-Brayman-Mont Dam", etc.).

Workflow: visit the duplicate project's detail page, click "Merge into…"
in the header, search for the canonical target project from a typeahead,
review the preview (what assignments / locations / sessions will move,
any conflicts), confirm.  Source is soft-deleted; everything else
re-points to the target.  Smart consolidation: same-named locations in
both projects merge into one (source's assignments move to target's
existing location with the same name; source's empty location is then
deleted).  Different-named locations move as-is.

Backend:
- backend/services/project_merge.py (new): preview() and execute()
  functions.  Transaction-safe.  Per-assignment UnitHistory audit row
  with change_type='assignment_merged' so the deployment timeline shows
  the merge.  Source modules disabled; missing modules added to target.
  Handles edge cases: same project_id rejected, deleted projects rejected,
  orphan project-direct assignments (no location) re-pointed defensively.

- backend/routers/projects.py: new endpoints
    GET  /api/projects/{source_id}/merge_preview?target_id=...
    POST /api/projects/{source_id}/merge_into?target_id=...

Frontend (templates/partials/projects/project_header.html):
- "Merge into…" button in Project Actions area.
- Modal with typeahead (reuses /api/admin/metadata_backfill/projects_search)
  scoped to existing projects only (no create-new option).  Filters out
  the source project from candidates so operator can't accidentally pick
  it as target.
- Preview pane shows totals + per-location plan (consolidate vs move) +
  warnings (mismatched client names, location consolidation note).
- Red "Merge (permanent)" confirm button only enables after a target is
  picked and preview loads.
- On success, browser redirects to target project page.

Smoke verified: "Swank-Karns Crossing" (1 assignment) merged into
"Swank-Karns Crossings"; target now has 2 locations + 2 assignments,
source has 0 dangling rows, 1 project_merge audit entry written.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-12 20:18:42 +00:00
serversdown d46f9fccf8 fix(sfm): broaden Loc-N suffix regex to catch '.Loc' and 'Loc No.' variants
Operators use more separator variations than the original regex caught:
  - "Trumbull-Brayman-JV- Mont.Dam.Loc 2-R-25" — period as separator
  - "CMU - RKM Hall - Loc No. 3 - 4615 Forbes" — "No." between Loc and digit

Added period to the separator character class and optional "No." token
before the digit.  Catches both above patterns plus near-variants
without false-positives on normal project strings.

Real-data impact: 5 more clusters now auto-strip cleanly, including the
1,903-event Trumbull-Brayman-JV- Mont.Dam cluster.  Confidence
distribution: 43 → 44 high.
2026-05-12 19:19:46 +00:00
serversdown 6ebbe28308 feat(sfm): strip "- Loc N" suffix from operator-typed project names
Operators sometimes bake location identifiers into the project string
for email-readability — "Fay - Locks & Dam No3 - Loc 2 - 735 Bunola"
where "Fay - Locks & Dam No3" is the actual project and "- Loc 2 -
735 Bunola" is location info that already lives in sensor_location.
Without stripping, every "- Loc N" variant became a separate project,
fragmenting what should be one project with several locations.

Backend:
- New _extract_project_root() helper.  Regex matches " - Loc N" / "-Loc3" /
  " - Location #5" / etc. with case-insensitive multi-dash support; strips
  from that marker forward and cleans up dangling separators.  Strings
  without a Loc-marker pass through unchanged.

- Cluster dataclass adds project_root field alongside project_raw.
  project_raw stays the operator-typed string for display ("hover to see
  what was actually typed").  project_root is what gets normalised for
  matching and used as the suggested project name.

- _ensure_project + _ensure_location now do normalisation-aware dedup
  before creating: a cluster of "SR81" and a cluster of "SR 81" (which
  normalise to the same string) collapse into one project on apply,
  even when applied in the same bulk operation.  Avoids UNIQUE
  constraint collisions and duplicate-named-by-spacing projects.

Frontend:
- Wizard cluster cards show "↳ stripped trailing 'Loc N' suffix; operator
  typed: <raw>" when project_root differs from project_raw, so the
  operator can see at a glance what the parser did to the string.

Real-data results: against the same 10,055 SFM events, confidence
distribution improved from 37/14/8 (high/med/low) to 43/9/7.  "Fay -
Locks & Dam No3" now appears as ONE project across 6 cluster instances
spanning 3 serials and 6 different locations — exactly the
"one project, many locations" model the user described.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-12 16:49:14 +00:00
serversdown 42de06f441 feat(sfm): Phase 5a — bulk-backfill projects/locations/assignments from event metadata
Operator clicks one button.  Parser reads SFM's events table (operator-typed
project / client / sensor_location strings), clusters by serial + time +
metadata, fuzzy-matches against existing projects, and proposes
Project / MonitoringLocation / UnitAssignment chains to create.
Auto-applies high-confidence non-conflicting clusters in bulk; queues
medium/low confidence for individual review.

Verified against real data: 10,052 events → 59 clusters → 37 high-
confidence + 14 medium + 8 low.  Test-applied one cluster end-to-end;
Project + Module + Location + Assignment + UnitHistory + Decision rows
all created correctly, and Phase 2's attribution walk picked up the
events automatically on the new location's detail page.

Pipeline (backend/services/metadata_backfill.py, ~700 lines):
  1. Pull all SFM events via /db/events per serial.
  2. Pre-filter: drop events already covered by an existing UnitAssignment
     window (Phase 2 handles those automatically).
  3. Time-cluster what's left: serial + 7-day gap is the cluster identity.
  4. Metadata-split each time-cluster on persistent metadata transitions
     (≥ 2 consecutive events) so a single typo doesn't fork the cluster.
  5. Match against existing graph (rapidfuzz.WRatio multi-signal scoring,
     normalisation that handles abbreviations / reorders / separator
     variations).  Thresholds: 0.95 exact, 0.80 fuzzy, min-shorter-input
     5 chars to guardrail false positives on single common words.
  6. Score confidence (high/medium/low) using event count, span,
     blank-meta, conflict, ambiguity rules.
  7. Detect conflicts: overlap with existing UnitAssignment at a different
     location for the same serial → blocking.  Operator must reconcile.
  8. Apply: ensure auto_imported ProjectType exists, ensure
     vibration_monitoring ProjectModule on the project, write
     Project / MonitoringLocation / UnitAssignment / UnitHistory all in
     one transaction.

Migration (backend/migrate_add_metadata_backfill.py): adds
unit_assignments.source column (default 'manual') and
metadata_backfill_decisions table.  Idempotent, non-destructive.

API (backend/routers/metadata_backfill.py):
  GET  /api/admin/metadata_backfill/scan          — clusters + suggestions
  POST /api/admin/metadata_backfill/apply         — bulk apply by cluster_ids
                                                     w/ optional per-cluster
                                                     project/location overrides
  POST /api/admin/metadata_backfill/skip          — mark skipped (persistent)

UI (templates/admin/metadata_backfill.html, accessible at
/settings/developer/metadata-backfill via the Developer tab of Settings):
  - One-button "Run scan" entry.
  - Summary KPI tiles (scanned / already attributed / pending / conflicts).
  - "Apply all high-confidence" bulk button at the top — primary path.
  - Per-cluster cards below with Apply / Skip / Preview event actions.
  - Blank-meta clusters get inline input fields for operator-typed project +
    location names before applying.
  - Blocking-conflict clusters render with the conflicting assignment
    information and a disabled Apply button.
  - Live progress toast during apply.
  - Reuses the Phase 1+2+4 event-detail modal for "Preview event" — operator
    can sanity-check the BW report data against the cluster's sample event.

Dependencies: rapidfuzz==3.10.1 added to requirements.txt.  Pre-built C
wheels for all platforms, ~5s docker build hit.

Phase 5b (deferred to next session): swap-detection daily background job,
notification inbox for auto-applied swaps, recently-applied audit view,
"Tidy" page for renaming/merging auto-created projects.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-12 05:54:57 +00:00
serversdown f1f3da8e61 feat(sfm): unified deployment timeline (deprecate deployment_records)
Phase 4.  Rebuilds the seismograph "Deployment History" + "Timeline"
sections on the unit detail page as a single derived view computed from
three sources: unit_assignments (authoritative project/location windows),
unit_history (calibration/retirement/deployed state changes), and SFM
events overlaid per assignment window (count + peak PVS + last event).

Fixes the wonky-timeline symptoms: missing entries, duplicate/contradictory
rows, and no visibility into what the unit was actually doing during each
deployment window.

Backend:
- backend/services/deployment_timeline.py: new deployment_timeline_for_unit()
  helper.  Merges UnitAssignment rows (with SFM event overlay fetched
  concurrently via httpx), UnitHistory state-change rows (filtered to
  meaningful change_types and de-noised by dropping rows where
  old_value == new_value — there's noise in legacy audit log from
  record_history() being called on every save), and synthetic "gap"
  entries between assignments >= 1 day apart.  Sorts newest first.

- backend/routers/units.py: new GET /api/units/{unit_id}/deployment_timeline
  endpoint with optional include_events=false flag.

- backend/routers/project_locations.py: assign / unassign / swap /
  update endpoints now write UnitHistory rows on every assignment
  lifecycle event.  New change_types: assignment_created,
  assignment_ended, assignment_swapped, assignment_updated.  These
  surface in the unified timeline (where the assignment row itself
  shows the structural data; the audit row is filtered out to avoid
  double-rendering).  Closes a real gap — assignment changes were
  previously invisible to any audit consumer.

- backend/migrate_deprecate_deployment_records.py: non-destructive
  migration.  Adds deployment_records.deprecated_at column.  For each
  legacy row without a matching UnitAssignment, best-effort
  synthesizes one (with the free-text location_name preserved in
  notes).  Marks every processed row.  Idempotent.  DROP TABLE
  deferred to a follow-up release.

Frontend (templates/unit_detail.html):
- Removed legacy "Deployment History" card (with Log Deployment button)
  and the separate "Timeline" card.  Replaced with a single
  "Deployment Timeline" section.
- Three entry visual styles: assignment rows (orange dot, location +
  project link, event-overlay summary), gap rows (dashed outline, idle
  day count), and state_change rows (navy dot, friendly label, old →
  new value).  Active assignments get a green dot + "active" badge.
- Existing loadUnitHistory() and loadDeploymentHistory() functions kept
  as shims that delegate to loadDeploymentTimeline(), so modal-save
  callbacks that referenced them still trigger a refresh of the visible
  section.  Legacy function bodies preserved under _legacy_*_unused
  names for archeology; not called by anything.

Verified end-to-end:
- BE11529 timeline now shows 2 entries (active assignment with 24-event
  overlay + the deployed→benched state change), compared to the previous
  noisy mix that included 6 no-op state-change rows.
- Migration ran against real DB: 1 legacy row processed (had no
  project_id, marked deprecated without backfill).
- Assign / unassign / swap / edit now leave a paper trail in
  unit_history.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-12 00:15:07 +00:00
serversdown 63bd6ad8a2 feat(sfm): project-level vibration events roll-up
Phase 3 of the SFM integration. Adds a "Project-wide vibration events"
KPI card to the Vibration tab of every project detail page, summarising
event activity across all of that project's vibration MonitoringLocations.

Backend:
- backend/services/sfm_events.py: vibration_summary_for_project() helper.
  Concurrently fans out events_for_location() across every vibration
  location in the project; aggregates total events, peak PVS (with the
  location it occurred at), last-event timestamp, false-trigger count;
  and produces a per-location breakdown sorted by event count.

- backend/routers/project_locations.py: new GET /api/projects/{p}/
  vibration_summary endpoint returning an HTML partial (HTMX-friendly,
  matches the locations-list HTMX pattern already used on this page).

Frontend:
- templates/partials/projects/vibration_summary.html: new partial with
  four KPI tiles (total, peak PVS + linked location + date, last event,
  false triggers) and a "Top locations by activity" mini-list showing
  the top 5 by event count.  Empty-state copy when the project has no
  vibration locations yet.

- templates/projects/detail.html: HTMX-load the new summary above the
  locations list inside the Vibration tab.

Verified against terra-view-alpha: 24 events across "Loc 1 - 78 poop
street", peak PVS 14.1351 in/s.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-12 00:09:02 +00:00
serversdown bc5a151faa feat(sfm): per-unit event history with attribution + Unattributed bucket
Phase 2 of the SFM integration.  Adds a "SFM Events" section to the
seismograph unit detail page (/unit/{id}).  Every event SFM has for the
serial is shown, with each event annotated by which project/location
assignment window it falls into.  Events outside every assignment window
get the "⚠ Unattributed" badge plus a "<N>d before/after <nearest location>"
hint — that's the operator's signal that backdating an assignment (Phase 1
edit-pencil) will absorb the orphan events.

Backend:
- backend/services/sfm_events.py: new events_for_unit() helper.  Fetches
  all events for the serial via SFM /db/events (one call, ceiling 5000),
  loads every UnitAssignment for the unit + resolves MonitoringLocation +
  Project names, then annotates each event with attribution or
  nearest_assignment (signed delta_days).  Bucket filter: all /
  attributed / unattributed.  Stats always reflect the full event set so
  the "Unattributed" KPI tile is meaningful regardless of which bucket
  is being viewed.

- backend/routers/units.py: new GET /api/units/{unit_id}/events with
  bucket / date-range / false_trigger / limit query params.  404s on
  unknown unit_id; returns an empty payload for non-seismograph
  device_types so the page can render the section conditionally.

Frontend (templates/unit_detail.html):
- New "SFM Events" section between "Deployment History" and "Timeline",
  styled to match the existing card pattern (border-t divider, same
  heading weight).
- Hidden by default; revealed only when currentUnit.device_type ===
  'seismograph' after the unit data loads.
- Four KPI tiles: Total Events / Unattributed (highlighted amber when
  > 0) / Peak PVS / Last Event.
- Filters: Bucket (all|attributed|unattributed), From/To, False
  Triggers, Limit, + Refresh.
- Event table with Attribution column.  Attributed rows link to the
  project/location detail page; unattributed rows are tinted amber
  and show "<N>d before/after <nearest location>" with a link to the
  nearest location.
- Empty-state copy varies by bucket: e.g. unattributed-with-zero shows
  " All events for this unit are attributed to a project/location".

Verified end-to-end against BE11529 (81 events total, 24 attributed,
57 unattributed — all 57 unattributed events emitted within hours of
the assignment start, which means backdating the assignment by a day
would attribute every one of them).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-11 22:38:46 +00:00
serversdown df771a87de feat(sfm): wire SFM events into project-location detail page
Phase 1 of the SFM project/location integration. When viewing a vibration
monitoring location, operators now see the events that were actually
recorded there — fanned out across every seismograph that was ever
assigned to that location (handles mid-project unit swaps).

Backend:
- backend/services/sfm_events.py: new events_for_location() async helper.
  Walks UnitAssignment rows for the location (active + closed), intersects
  each assignment's [assigned_at, assigned_until] window with the requested
  filter, and concurrently queries SFM /db/events for each (serial, window)
  pair via httpx.AsyncClient.  Unions, sorts newest-first, computes summary
  stats (event count, peak PVS + when/who, last event, false-trigger count)
  over the full set, and trims to the user's display limit.  Over-fetches
  per-window (up to 5000) so stats stay accurate even with a small display
  limit.

- backend/routers/project_locations.py: new GET endpoint
  /api/projects/{project_id}/locations/{location_id}/events.  Validates
  project/location pairing (404 on mismatch).  SLM locations return an
  empty payload rather than 404 so the frontend can render gracefully.

Frontend:
- templates/vibration_location_detail.html: new "Events" tab on the
  location detail page.  KPI tiles (total / peak PVS / last event / false
  triggers), "Seismographs deployed at this location" assignment list
  (transparency: shows each assignment's date range and contributed event
  count), date / false-trigger / limit filters, and the paginated event
  table.  Lazy-loaded on first tab visit; manual refresh button.

Architectural notes:
- SFM remains the single source of truth for events.  No event sync; live
  HTTP per page load.
- UnitAssignment is the join key (not MonitoringSession).
- Events whose timestamp falls outside every assignment window are NOT
  surfaced here.  Those orphan events get a dedicated "Unattributed
  events" view on the per-unit detail page in Phase 2.

Out of scope (this commit):
- Phase 2 (per-unit history view) and Phase 3 (project-level roll-up)
  reuse this helper but ship separately.
- Phase 4 (deprecating deployment_records) is independent.
- Extracting the event-table JS to a shared file is a follow-up.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-11 21:57:14 +00:00
serversdown 64d4423308 feat: add allocated status and project allocation to unit management
- Updated dashboard to display allocated units alongside deployed and benched units.
- Introduced a quick-info modal for units, showing detailed information including calibration status, project allocation, and upcoming jobs.
- Enhanced fleet calendar with a new quick-info modal for units, allowing users to view unit details without navigating away.
- Modified devices table to include allocated status and visual indicators for allocated units.
- Added allocated filter option in the roster view for better unit management.
- Implemented backend migration to add 'allocated' and 'allocated_to_project_id' columns to the roster table.
- Updated unit detail view to reflect allocated status and allow for project allocation input.
2026-03-26 05:05:34 +00:00
serversdown 4f56dea4f3 feat: adds deployment records for seismographs. 2026-03-25 17:36:51 +00:00
serversdown e4d1f0d684 feat: start build of listed reservation system 2026-03-13 21:37:06 +00:00
serversdown c138e8c6a0 feat: add new "out for cal" status for units currently being calibrated.
-retire unit button changed to be more dramatic... lol
2026-03-12 17:59:42 +00:00
serversdown 7516bbea70 feat: add manual SD card data upload for offline NRLs; rename RecordingSession to MonitoringSession
- Add POST /api/projects/{project_id}/nrl/{location_id}/upload-data endpoint
  accepting a ZIP or multi-file select of .rnd/.rnh files from an SD card.
  Parses .rnh metadata for session start/stop times, serial number, and store
  name. Creates a MonitoringSession (no unit assignment required) and DataFile
  records for each measurement file.

- Add Upload Data button and collapsible upload panel to the NRL detail Data
  Files tab, with inline success/error feedback and automatic file list refresh
  via HTMX after import.

- Rename RecordingSession -> MonitoringSession throughout the codebase
  (models.py, projects.py, project_locations.py, scheduler.py, roster_rename.py,
  main.py, init_projects_db.py, scripts/rename_unit.py). DB table renamed from
  recording_sessions to monitoring_sessions; old indexes dropped and recreated.

- Update all template UI copy from Recording Sessions to Monitoring Sessions
  (nrl_detail, projects/detail, session_list, schedule_oneoff, roster).

- Add backend/migrate_rename_recording_to_monitoring_sessions.py for applying
  the table rename on production databases before deploying this build.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-24 19:54:40 +00:00
claude 0f17841218 feat: enhance project management by canceling pending actions for archived and on_hold projects 2026-02-19 18:57:59 +00:00
claude 28942600ab fix: Auto-downloaded files now show up in project_files data. 2026-02-18 19:51:44 +00:00
claude 80861997af Fix: removed duplicate download following scheduled stop. 2026-02-18 06:44:04 +00:00
claude ebe60d2b7d feat: enhance roster unit management with bidirectional pairing sync
fix: scheduler one off
2026-02-11 20:13:27 +00:00
claude 842e9d6f61 feat: add support for one-off recording schedules with start and end datetime 2026-02-10 07:08:03 +00:00
claude eb0a99796d add: Calander and reservation mode implemented. 2026-02-06 20:40:31 +00:00
claude e515bff1a9 fix: tab state persists in url hash. Settings save nolonger reload the page. Scheduler management now cascades to individual events. 2026-02-04 18:12:18 +00:00
claude d78bafb76e fix: improved 24hr cycle via scheduler. Should help prevent issues with DLs. 2026-01-31 22:31:34 +00:00
claude 4957a08198 fix: improvedr pair status sharing. 2026-01-29 16:37:59 +00:00
claude 05482bd903 Add:
- pair_devices.html template for device pairing interface
- SLMM device control lock prevents flooding nl43.
Fix:
- Polling intervals for SLMM.
- modem view now list
- device pairing much improved.
- various other tweaks through out UI.
- SLMM Scheduled downloads fixed.
2026-01-29 07:50:13 +00:00
claude 5ee6f5eb28 feat: Enhance dashboard with filtering options and sync SLM status
- Added a new filtering system to the dashboard for device types and statuses.
- Implemented asynchronous SLM status synchronization to update the Emitter table.
- Updated the status snapshot endpoint to sync SLM status before generating the snapshot.
- Refactored the dashboard HTML to include filter controls and JavaScript for managing filter state.
- Improved the unit detail page to handle modem associations and cascade updates to paired devices.
- Removed redundant code related to syncing start time for measuring devices.
2026-01-28 20:02:10 +00:00
claude 8431784708 feat: Refactor template handling, improve scheduler functions, and add timezone utilities
- Moved Jinja2 template setup to a shared configuration file (templates_config.py) for consistent usage across routers.
- Introduced timezone utilities in a new module (timezone.py) to handle UTC to local time conversions and formatting.
- Updated all relevant routers to use the new shared template configuration and timezone filters.
- Enhanced templates to utilize local time formatting for various datetime fields, improving user experience with timezone awareness.
2026-01-23 06:05:39 +00:00
claude c771a86675 Feat/Fix: Scheduler actions more strictly defined. Commands now working. 2026-01-22 20:25:19 +00:00
claude 65ea0920db Feat: Scheduler implemented, WIP 2026-01-21 23:11:58 +00:00
claude 1ef0557ccb feat: standardize device type for Sound Level Meters (SLM)
- Updated all instances of device_type from "sound_level_meter" to "slm" across the codebase.
- Enhanced documentation to reflect the new device type standardization.
- Added migration script to convert legacy device types in the database.
- Updated relevant API endpoints, models, and frontend templates to use the new device type.
- Ensured backward compatibility by deprecating the old device type without data loss.
2026-01-16 18:31:27 +00:00
claude 6c7ce5aad0 Project data management phase 1. Files can be downloaded to server and downloaded locally. 2026-01-16 07:39:22 +00:00
claude 8a5fadb5df Move SLM control center groundwork onto dev 2026-01-12 18:07:26 +00:00
claude 85b211e532 bugfix: unit status updating based on last heard, not just using cached status 2026-01-05 18:56:20 +00:00
claude dba4ad168c refactor: clean up whitespace and improve formatting in emit_status_snapshot function 2025-12-29 19:18:53 +00:00
claude 27f8719e33 db management system added 2025-12-16 20:02:04 +00:00
claude 690669c697 v0.2.2-series4 endpoint added, dev branch set up at :1001 2025-12-08 22:15:54 +00:00
claude 4cef580185 v0.2.1. many features added and cleaned up. 2025-12-03 21:23:18 +00:00
claude dc853806bb v0.2 fleet overhaul 2025-12-03 07:57:25 +00:00
claude 802601ae8d pre refactor 2025-12-03 00:51:18 +00:00
claude 90ecada35f v0.1.1 update 2025-12-02 06:36:13 +00:00
Claude 247405c361 Add MVP frontend scaffold with FastAPI + HTMX + TailwindCSS
- Created complete frontend structure with Jinja2 templates
- Implemented three main pages: Dashboard, Fleet Roster, and Unit Detail
- Added HTMX auto-refresh for real-time updates (10s interval)
- Integrated dark/light mode toggle with localStorage persistence
- Built responsive card-based UI with sidebar navigation
- Created API endpoints for status snapshot, roster, unit details, and photos
- Added mock data service for development (emit_status_snapshot)
- Implemented tabbed interface on unit detail page (Photos, Map, History)
- Integrated Leaflet maps for unit location visualization
- Configured static file serving and photo management
- Updated requirements.txt with Jinja2 and aiofiles
- Reorganized backend structure into routers and services
- Added comprehensive FRONTEND_README.md documentation

Frontend features:
- Auto-refreshing dashboard with fleet summary and alerts
- Sortable fleet roster table (prioritizes Missing > Pending > OK)
- Unit detail view with status, deployment info, and notes
- Photo gallery with thumbnail navigation
- Interactive maps showing unit coordinates
- Consistent styling with brand colors (orange, navy, burgundy)

Ready for integration with real Series3 emitter data.
2025-11-22 00:16:26 +00:00