Commit Graph

116 Commits

Author SHA1 Message Date
serversdown d3b5a3fd26 feat(sfm): inline typeahead override of project + location on each cluster card
Operator no longer has to accept the parser's suggested project /
location verbatim.  Each cluster card now has editable typeahead inputs
that search existing projects (and existing locations within the chosen
project), with a "Create new: <typed>" fallback always available.

Solves the I-80-North-Fork case: of the 20+ cluster variants
("I-80-North Fork Bridges-I80 E. Abutment", "I-80- North Fork
Bridges-543 Plank Rd", etc.), operator types "I-80" in the Project
input, picks the existing project from the dropdown, and the cluster
attaches to it.  Repeat for the other variants.  No need to pre-create
the canonical project — though pre-creation still works fine if you'd
rather.

Backend (backend/routers/metadata_backfill.py):
- GET /api/admin/metadata_backfill/projects_search?q=&limit=
  Returns existing projects matching by case-insensitive substring OR
  rapidfuzz WRatio score >= 0.50.  Substring matches sort to the top
  (treated as exact for ordering).  Includes location_count and
  project_number/client_name in each result for disambiguation.  Always
  emits a "Create new: <q>" suggestion alongside the matches.

- GET /api/admin/metadata_backfill/locations_search?project_id=&q=&limit=
  Same shape, scoped to a single project's vibration locations.

- POST /api/admin/metadata_backfill/apply now accepts four override
  keys per cluster (was previously two):
    project_id       → attach to existing Project (operator picked from
                       typeahead)
    project_name     → create new with this name (operator typed a
                       custom name; existing project_name behaviour)
    location_id      → attach to existing MonitoringLocation; validated
                       against the chosen project_id so a stale location
                       FK can't sneak in
    location_name    → create new location with this name

Frontend (templates/admin/metadata_backfill.html):
- Each non-blank-meta cluster card now has two editable typeahead inputs
  (Project + Location) pre-populated with the parser's suggested
  values.  Old static "Project: + Create new: X" / "≈ Fuzzy match" pills
  replaced with compact hint lines under the inputs showing what the
  current value will do.
- Typeahead dropdown opens on focus, debounced 150ms on type.  Shows
  matched existing entities with score badges (exact / NN%) plus a
  "Create new: <typed>" option at the bottom.  Click-to-pick fills the
  text input and writes the entity id into a hidden field.
- Picking a new project clears the location id (forces re-pick under
  the new project, avoids cross-project location FKs).
- _gatherOverrides re-wired to emit the new project_id / location_id
  keys when the operator picked from the dropdown, falling back to
  *_name when they typed free-form.

Backward-compatible: blank-meta clusters keep their existing "project_name
/ location_name" plain inputs and the override path still honours them.

Verified end-to-end:
- /projects_search?q=I-80 returns the existing "I-80 - North Fork
  Bridge" project (score 1.0, has 4 locations) plus a "Create new"
  option.
- /locations_search requires project_id (400 without it).
- Wizard page renders with typeahead wiring confirmed in HTML.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-12 19:48:09 +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 21844b4d65 feat(sfm): download buttons in event-detail modal (Blastware binary + sidecar JSON)
Two new action buttons at the top of the Source File section of the
event-detail modal:

1. Download Blastware file — primary orange button.  Pulls the raw .AB0
   /.G10/.6R0/etc. binary from SFM (/db/events/{id}/blastware_file) via
   terra-view's /api/sfm proxy.  The browser saves it with the original
   on-disk filename (using the HTML5 `download` attribute pointed at
   sidecar.blastware.filename).  Operator can then open the file
   directly in Blastware on a Windows box for full waveform analysis,
   archive it, or attach it to a compliance report.

   Greyed-out "Blastware file unavailable" placeholder shown when
   sidecar.blastware.available is false (rare — would mean SFM stored
   the metadata but lost the binary).

2. Download sidecar JSON — secondary outlined button.  Pulls the same
   .sfm.json the modal renders from.  Saved as <binary>.sfm.json.
   Useful for ops/diagnostics and for the future metadata-driven
   project parser (Phase 5) which can chew on these directly.

End-to-end verified through the proxy: 8882-byte Blastware binary
intact with "Instantel" magic header preserved.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-12 04:05:28 +00:00
serversdown 80fa76208a feat(sfm): shared event-detail modal with rich BW report fields
Clicking any event row in any of the three event tables (/sfm Events,
project-location Events tab, unit detail SFM Events) now opens a modal
populated from the SFM .sfm.json sidecar.  Previously the /sfm page had
a basic inline modal showing only the columns already in the table;
this rebuilds it as a shared component and exposes the rich fields
that the BW ASCII report unlocks.

Shared component:
- backend/static/event-modal.js — single ~250-line module.  Public API:
  showEventDetail(eventId) fetches /api/sfm/db/events/{id}/sidecar
  live (no extra terra-view caching) and renders sections for:
    • Event (serial, timestamp, record type, sample rate, rec time,
      waveform key)
    • Project Info (operator-typed user notes — project / client /
      operator / sensor_location — flagged in the UI as "as typed
      into the seismograph at session start", not the terra-view
      assignment)
    • Peak Particle Velocity (per-channel + vector sum, with the
      time-of-vector-sum-peak when bw_report is available)
    • Microphone (Peak dB(L) + psi, ZC frequency, time of peak)
    • Sensor Self-Check table (per-channel freq + ratio/amplitude +
      pass/fail)
    • Device & Recording Metadata (firmware, battery, calibration
      date + by-whom, geo range, stop mode, units)
    • Source File (Blastware filename, size, SHA-256, capture time)
  closeEventDetailModal() closes; Escape key also closes.

- templates/partials/event_detail_modal.html — modal shell partial
  (sticky title bar, scrollable body, click-outside-to-close).

Wired into three pages:
- templates/sfm.html: removed the old inline modal + showEventDetail /
  ppvCard / closeEventModal functions (replaced by the shared module).
  Row onclick now passes just the event id instead of the full JSON.
- templates/vibration_location_detail.html: row click on the Events
  tab opens the modal.  The /unit/{serial} link inside the row has
  event.stopPropagation() so the link navigates instead of opening
  the modal.
- templates/unit_detail.html: row click on the SFM Events table opens
  the modal.  The attribution-cell project/location links also got
  stopPropagation.

Graceful degradation: older events forwarded before the watcher's
_ASCII.TXT pairing fix don't have a bw_report block in their sidecar.
The modal renders an amber banner explaining that and shows just the
event + project_info + peak_values + source-file sections.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-12 03:55:41 +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 09db988a35 feat(sfm): editable UnitAssignment date windows (backdate deployments)
Operators couldn't change a unit's assigned_at / assigned_until after
creating the assignment, so a unit physically deployed in December 2025
but only recorded in terra-view today would show "deployed today" and
all its real events would be invisible on the project's location page.

Backend:
- PATCH /api/projects/{project_id}/assignments/{assignment_id}
  Accepts JSON body with optional assigned_at, assigned_until, notes.
  - assigned_at is required (cannot be cleared)
  - assigned_until can be null to mark active / indefinite
  - assigned_until must be after assigned_at
  - rejects overlaps with other assignments of the same unit at the
    same location (different units overlapping is fine — that's a
    legitimate swap window)
  - assignment.status flips to "active" when assigned_until is cleared,
    "completed" when set
  - 404 if the assignment doesn't belong to {project_id} (security)

Frontend (vibration_location_detail.html):
- Pencil icon next to each row in the "Seismographs deployed at this
  location" card. Click to open a modal with datetime-local inputs for
  From + Until (blank = active) and a Notes textarea. Save reloads the
  Events tab so KPI tiles and the event table reflect the new window.
- Helper line under the assignment list explains the workflow:
  "Click the pencil to backdate a deployment so historical events get
  attributed to this location."

Verified end-to-end against real data: backdating BE11529's assignment
on a vibration location from 2026-04-14 to 2025-12-01 surfaced 10
additional events (24 -> 34) that were previously invisible.

Validation suite (all returning correct HTTP codes):
  - assigned_until < assigned_at -> 400
  - cross-project assignment_id -> 404
  - assigned_at cleared -> 400
  - notes-only update -> 200

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-11 22:30:32 +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 63ba63edaf Merge pull request 'Merge dev into sfm-integration branch' (#45) from dev into feature/sfm-integration
Reviewed-on: #45
2026-04-13 22:06:25 -04:00
claude 2ba20c7809 feat(sfm): add SFM proxy router and event data page
- backend/routers/sfm.py: HTTP proxy to SFM backend (localhost:8200),
  mirrors the SLMM proxy pattern. SFM_BASE_URL env var for docker-compose.
  Catch-all /{path} forwards to SFM root (no /api/ prefix). 60s timeout.

- templates/sfm.html: full SFM dashboard with 5 tabs:
  Events (DB listing, filters by serial/date/false-trigger, flag/unflag FT),
  Units (known serials + stats, filter events by unit),
  Monitor Log (continuous monitoring intervals),
  ACH Sessions (call-home history),
  Live Device (TCP connect, device info cards, start/stop monitoring,
  push project config, download events from device, operation log).

- backend/main.py: import sfm router, include router, add GET /sfm route
- templates/base.html: add SFM Live Data nav link under Seismographs

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-13 17:14:36 -04:00
claude f84d0818d2 fix: improve roster behavior with in-place rerfresh.
docs: update for 0.9.4
2026-04-10 22:22:25 +00:00
serversdown f50cf2b7f6 feat: add functionality to manage deleted projects in settings
- Introduced a new section for displaying soft-deleted projects.
- Implemented loading of deleted projects via an API call.
- Added restore and permanently delete options for each deleted project.
- Integrated loading of deleted projects when the data tab is shown.
2026-04-01 05:42:10 +00:00
serversdown 73a6ff4d20 feat: Refactor project creation and management to support modular project types
- Updated project creation modal to allow selection of optional modules (Sound and Vibration Monitoring).
- Modified project dashboard and header to display active modules and provide options to add/remove them.
- Enhanced project detail view to dynamically adjust UI based on enabled modules.
- Implemented a new migration script to create a `project_modules` table and seed it based on existing project types.
- Adjusted form submissions to handle module selections and ensure proper API interactions for module management.
2026-03-30 21:44:15 +00:00
serversdown 184f0ddd13 doc: update to 0.9.3 2026-03-28 01:53:13 +00:00
serversdown 27eeb0fae6 fix: adds timeline bars to SLM calendar view, more conscise and legible. 2026-03-27 22:44:53 +00:00
serversdown 49bc625c1a feat: add report_date to monitoring sessions and update related functionality
fix: chart properly renders centered
2026-03-27 22:18:50 +00:00
serversdown 95fedca8c9 feat: monitoring session improvements — UTC fix, period hours, calendar, session detail
- Fix UTC display bug: upload_nrl_data now wraps RNH datetimes with
  local_to_utc() before storing, matching patch_session behavior.
  Period type and label are derived from local time before conversion.

- Add period_start_hour / period_end_hour to MonitoringSession model
  (nullable integers 0–23). Migration: migrate_add_session_period_hours.py

- Update patch_session to accept and store period_start_hour / period_end_hour.
  Response now includes both fields.

- Update get_project_sessions to compute "Effective: M/D H:MM AM → M/D H:MM AM"
  string from period hours and pass it to session_list.html.

- Rework period edit UI in session_list.html: clicking the period badge now
  opens an inline editor with period type selector + start/end hour inputs.
  Selecting a period type pre-fills default hours (Day: 7–19, Night: 19–7).

- Wire period hours into _build_location_data_from_sessions: uses
  period_start/end_hour when set, falls back to hardcoded defaults.

- RND viewer: inject SESSION_PERIOD_START/END_HOUR from template context.
  renderTable() dims rows outside the period window (opacity-40) with a
  tooltip; shows "(N in period window)" in the row count.

- New session detail page at /api/projects/{id}/sessions/{id}/detail:
  shows breadcrumb, files list with View/Download/Report actions,
  editable session info form (label, period type, hours, times).

- Add local_datetime_input Jinja filter for datetime-local input values.

- Monthly calendar view: new get_sessions_calendar endpoint returns
  sessions_calendar.html partial; added below sessions list in detail.html.
  Color-coded per NRL with legend, HTMX prev/next navigation, session dots
  link to detail page.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-27 21:52:52 +00:00
serversdown e8e155556a refactor: unify active assignment checks and add project-type guards
- Replace all UnitAssignment "active" checks from `status == "active"` to
  `assigned_until == None` in both project_locations.py and projects.py.
  This aligns with the canonical definition: active = no end date set.
  (status field is still set in sync, but is no longer the query criterion)

- Add `_require_sound_project()` helper to both routers and call it at the
  top of every sound-monitoring-specific endpoint (FTP browser, FTP downloads,
  RND file viewer, all Excel report endpoints, combined report wizard,
  upload-all, NRL live status, NRL data upload). Vibration projects hitting
  these endpoints now receive a clear 400 instead of silently failing or
  returning empty results.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-27 21:12:38 +00:00
serversdown 33e962e73d feat: add edit session times functionality with modal for monitoring sessions 2026-03-27 20:54:04 +00:00
serversdown ac48fb2977 feat: add swap functionality for unit and modem assignments in vibration monitoring locations 2026-03-27 20:33:13 +00:00
serversdown 3c4b81cf78 docs: updates to 0.9.2 2026-03-27 17:01:43 +00:00
serversdown d135727ebd feat: add in-line quick editing for seismograph details (cal date, notes, deployment status) 2026-03-26 06:10:03 +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 57a85f565b feat: add location_slots to job_reservations for full slot persistence and update version to 0.9.1
Fix: modems do not show as "missing" any more, cleans up the dashboard.
2026-03-24 01:13:29 +00:00
claude e6555ba924 feat: Update series4 heartbeat to accept new source_id field with fallback to legacy source 2026-03-20 17:13:21 -04:00
serversdown 8694282dd0 Update version to 0.9.0 with Job Planner 2026-03-20 04:48:22 +00:00
serversdown bc02dc9564 feat: Enhance project and reservation management
- Updated reservation list to display estimated units and improved count display.
- Added "Upcoming" status to project dashboard and header with corresponding styles.
- Implemented a dropdown for quick status updates in project header.
- Modified project list compact view to reflect new status labels.
- Updated project overview to include a tab for upcoming projects.
- Added migration script to introduce estimated_units column in job_reservations table.
2026-03-19 22:52:35 +00:00
serversdown b3ec249c5e feat: add location names to reservation slots and promote-to-project
- Each monitoring location slot can now have a named location (e.g. "North Gate")
- Location names and slot order are persisted and restored in the planner
- Location names display in the expanded reservation card view
- Added "Promote to Project" button that converts a reservation into a
  tracked project with monitoring locations and unit assignments pre-filled

Requires DB migration on prod:
  ALTER TABLE job_reservation_units ADD COLUMN location_name TEXT;
  ALTER TABLE job_reservation_units ADD COLUMN slot_index INTEGER;
2026-03-18 22:15:46 +00:00
serversdown b6e74258f1 Merge dev into reservation branch to pick up v0.8.0 watcher manager changes 2026-03-18 20:13:19 +00:00
serversdown 1a87ff13c9 doc: update docs for 0.8.0 2026-03-18 19:59:34 +00:00
serversdown 22c62c0729 fix: watcher manager now displays "heard from" status, instead of unit status. Refreshes ever 60 mins, if havent heard from in >60 mins display missing. 2026-03-18 19:45:39 +00:00
serversdown 0e3f512203 Feat: expands project reservation system.
-Reservation list view
-expandable project cards
2026-03-15 05:25:23 +00:00
claude 15d962ba42 feat: watcher agent management system implemented. 2026-03-13 17:38:43 -04:00
serversdown e4d1f0d684 feat: start build of listed reservation system 2026-03-13 21:37:06 +00:00
claude 5a5426cceb v0.7.1 - Add out for call status, starting to work on reservation mode, fixed a big brain fart too. 2026-03-12 22:38:22 +00:00
claude 66eddd6fe2 chore: db cleanups and migration script fixes. 2026-03-12 22:09:57 +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 e89a04f58c fix: SLM report line graph border added, combined report wizard spacing fix. 2026-03-07 07:16:10 +00:00
serversdown e4ef065db8 Version bump to v0.7.0.
Docs: Update readme/changelog for 0.7.0
2026-03-07 01:39:19 +00:00
serversdown 86010de60c Fix: combined report generation formatting fixed and cleaned up. (i think its good now?) 2026-03-07 01:32:49 +00:00
serversdown f89f04cd6f feat: support for day time monitoring data, combined report generation now compaitible with mixed day and night types. 2026-03-07 00:16:58 +00:00
serversdown 67a2faa2d3 fix: separate days now are in separate .xlsx files, NRLs still 1 per sheet.
add: rebuild script for prod.

fix: Improved data parsing, now filters out unneeded Lp files and .xlsx files.
2026-03-06 23:37:24 +00:00
serversdown 14856e61ef Feat: Full combined report now working properly. Lotsa stuff fixed. 2026-03-06 22:32:54 +00:00
serversdown 2b69518b33 fix: add slm start time grace_minutes=15 grace period to include data starting at start time. 2026-03-05 22:48:21 +00:00
serversdown 6070d03e83 fix: nl32 data date now reads from start_time. 2026-03-05 22:28:10 +00:00
serversdown 240552751c feat: enhance mass upload parsing, no longer imports tons of unneeded Lp Files. 2026-03-05 22:22:19 +00:00