update to 0.16.0 #72

Merged
serversdown merged 32 commits from dev into main 2026-06-23 00:59:46 -04:00
3 changed files with 40 additions and 27 deletions
Showing only changes of commit ee6062f9fb - Show all commits
+13
View File
@@ -1135,6 +1135,7 @@ async def get_project_live_stats(project_id: str, db: Session = Depends(get_db))
import os import os
import asyncio import asyncio
import httpx import httpx
import json as _json
project = db.query(Project).filter_by(id=project_id).first() project = db.query(Project).filter_by(id=project_id).first()
if not project: if not project:
@@ -1152,6 +1153,18 @@ async def get_project_live_stats(project_id: str, db: Session = Depends(get_db))
.all() .all()
) )
# Only connected/live-mode NRLs belong in live monitoring. connection_mode
# lives in location_metadata JSON (default "connected"); offline/manual NRLs
# are excluded. With none connected, the caller gets [] and hides the section.
def _is_connected(loc) -> bool:
try:
meta = _json.loads(loc.location_metadata or "{}")
return meta.get("connection_mode", "connected") != "offline"
except Exception:
return True
locations = [loc for loc in locations if _is_connected(loc)]
# Active SLM unit per location (mirrors portal.active_unit_for_location). # Active SLM unit per location (mirrors portal.active_unit_for_location).
def _active_unit(loc_id: str): def _active_unit(loc_id: str):
asg = ( asg = (
@@ -48,34 +48,32 @@
</div> </div>
</div> </div>
{# Separate location lists per module type so vibration points and sound NRLs
don't get mixed in one list. Build the section set from the enabled modules. #}
{% set loc_sections = [] %}
{% if 'vibration_monitoring' in modules %}{% set _ = loc_sections.append(('vibration', 'Vibration Locations', 'Add Location')) %}{% endif %}
{% if 'sound_monitoring' in modules %}{% set _ = loc_sections.append(('sound', 'NRLs', 'Add NRL')) %}{% endif %}
{% if not loc_sections %}{% set _ = loc_sections.append(('', 'Locations', 'Add Location')) %}{% endif %}
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6"> <div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
<div class="bg-white dark:bg-slate-800 rounded-xl shadow-lg p-6"> <div class="space-y-6">
<div class="flex items-center justify-between mb-4"> {% for ltype, title, add_label in loc_sections %}
<h3 class="text-lg font-semibold text-gray-900 dark:text-white"> <div class="bg-white dark:bg-slate-800 rounded-xl shadow-lg p-6">
{% if 'sound_monitoring' in modules and 'vibration_monitoring' not in modules %} <div class="flex items-center justify-between mb-4">
NRLs <h3 class="text-lg font-semibold text-gray-900 dark:text-white">{{ title }}</h3>
{% else %} <button onclick="openLocationModal('{{ ltype }}')" class="text-sm text-seismo-orange hover:text-seismo-navy">{{ add_label }}</button>
Locations </div>
{% endif %} <div id="project-locations{{ '-' + ltype if ltype else '' }}"
</h3> hx-get="/api/projects/{{ project.id }}/locations{{ '?location_type=' + ltype if ltype else '' }}"
<button onclick="openLocationModal('{% if 'sound_monitoring' in modules and 'vibration_monitoring' not in modules %}sound{% elif 'vibration_monitoring' in modules and 'sound_monitoring' not in modules %}vibration{% endif %}')" class="text-sm text-seismo-orange hover:text-seismo-navy"> hx-trigger="load"
{% if 'sound_monitoring' in modules and 'vibration_monitoring' not in modules %} hx-swap="innerHTML">
Add NRL <div class="animate-pulse space-y-3">
{% else %} <div class="bg-gray-200 dark:bg-gray-700 h-16 rounded-lg"></div>
Add Location <div class="bg-gray-200 dark:bg-gray-700 h-16 rounded-lg"></div>
{% endif %} </div>
</button>
</div>
<div id="project-locations"
hx-get="/api/projects/{{ project.id }}/locations{% if 'sound_monitoring' in modules and 'vibration_monitoring' not in modules %}?location_type=sound{% endif %}"
hx-trigger="load"
hx-swap="innerHTML">
<div class="animate-pulse space-y-3">
<div class="bg-gray-200 dark:bg-gray-700 h-16 rounded-lg"></div>
<div class="bg-gray-200 dark:bg-gray-700 h-16 rounded-lg"></div>
<div class="bg-gray-200 dark:bg-gray-700 h-16 rounded-lg"></div>
</div> </div>
</div> </div>
{% endfor %}
</div> </div>
{# Location map — uses the reusable partial that fetches from {# Location map — uses the reusable partial that fetches from
+4 -2
View File
@@ -2287,8 +2287,10 @@ async function loadLiveStats() {
// The NRL list partial reloads via htmx (e.g. the 30s dashboard swap), which // The NRL list partial reloads via htmx (e.g. the 30s dashboard swap), which
// wipes the painted chips — repaint from the last poll as soon as it settles. // wipes the painted chips — repaint from the last poll as soon as it settles.
document.body.addEventListener('htmx:afterSwap', (e) => { document.body.addEventListener('htmx:afterSwap', (e) => {
const id = e.target && e.target.id; const id = (e.target && e.target.id) || '';
if (id === 'project-locations' || id === 'sound-locations') lsPaintInline(lsLastData); // Overview lists are now per-type (project-locations-sound/-vibration); the
// Sound tab uses sound-locations. Repaint chips whenever any of them swap.
if (id.startsWith('project-locations') || id === 'sound-locations') lsPaintInline(lsLastData);
}); });
function startLiveStats() { function startLiveStats() {