feat: add data freshness indicator with relative time and patient count (Task 5.3)
This commit is contained in:
@@ -242,9 +242,9 @@ Drawer selection → update_drug_selection → app-state store → load_pathway_
|
||||
- **Checkpoint**: Loading spinner appears during data fetch, empty state shows message
|
||||
|
||||
### 5.3 Data freshness indicator
|
||||
- [ ] Header shows: green dot + "{N} records" + "Last updated: {relative_time}"
|
||||
- [ ] Pull from `pathway_refresh_log` via `queries.load_initial_data()`
|
||||
- [ ] Format as relative time (e.g., "2h ago", "yesterday")
|
||||
- [x] Header shows: green dot + "{N} patients" + "Last updated: {relative_time}"
|
||||
- [x] Pull from `pathway_refresh_log` via `queries.load_initial_data()` (uses total_patients from root node as fallback when source_row_count is 0)
|
||||
- [x] Format as relative time (e.g., "2h ago", "yesterday")
|
||||
- **Checkpoint**: Header shows correct data freshness
|
||||
|
||||
### 5.4 Remove Reflex + final validation
|
||||
|
||||
@@ -1,7 +1,39 @@
|
||||
"""Callbacks for reference data loading and filter state management."""
|
||||
from datetime import datetime
|
||||
from dash import Input, Output, State, callback, ctx, no_update
|
||||
|
||||
|
||||
def _format_relative_time(iso_timestamp: str) -> str:
|
||||
"""Format an ISO timestamp as relative time (e.g., '2h ago', 'yesterday')."""
|
||||
if not iso_timestamp:
|
||||
return "Unknown"
|
||||
try:
|
||||
dt = datetime.fromisoformat(iso_timestamp)
|
||||
now = datetime.now()
|
||||
delta = now - dt
|
||||
seconds = int(delta.total_seconds())
|
||||
|
||||
if seconds < 60:
|
||||
return "just now"
|
||||
minutes = seconds // 60
|
||||
if minutes < 60:
|
||||
return f"{minutes}m ago"
|
||||
hours = minutes // 60
|
||||
if hours < 24:
|
||||
return f"{hours}h ago"
|
||||
days = hours // 24
|
||||
if days == 1:
|
||||
return "yesterday"
|
||||
if days < 7:
|
||||
return f"{days}d ago"
|
||||
if days < 30:
|
||||
weeks = days // 7
|
||||
return f"{weeks}w ago"
|
||||
return dt.strftime("%d %b %Y")
|
||||
except (ValueError, TypeError):
|
||||
return iso_timestamp[:10] if len(iso_timestamp) >= 10 else "Unknown"
|
||||
|
||||
|
||||
def register_filter_callbacks(app):
|
||||
"""Register reference data loading and filter state callbacks."""
|
||||
|
||||
@@ -16,10 +48,18 @@ def register_filter_callbacks(app):
|
||||
from dash_app.data.queries import load_initial_data
|
||||
|
||||
ref = load_initial_data()
|
||||
|
||||
total = ref.get("total_records", 0)
|
||||
record_text = f"{total:,} records" if total else "Data loaded"
|
||||
patients = ref.get("total_patients", 0)
|
||||
if total:
|
||||
record_text = f"{total:,} records"
|
||||
elif patients:
|
||||
record_text = f"{patients:,} patients"
|
||||
else:
|
||||
record_text = "Data loaded"
|
||||
|
||||
last_updated = ref.get("last_updated", "")
|
||||
updated_text = last_updated[:10] if last_updated else "Unknown"
|
||||
updated_text = _format_relative_time(last_updated)
|
||||
|
||||
return ref, record_text, updated_text
|
||||
|
||||
|
||||
@@ -92,12 +92,24 @@ def load_initial_data(db_path: Path) -> dict:
|
||||
""")
|
||||
available_trusts = [row[0] for row in cursor.fetchall()]
|
||||
|
||||
# Total patients from default root node (fallback when source_row_count is empty)
|
||||
total_patients = 0
|
||||
if not total_records:
|
||||
cursor.execute("""
|
||||
SELECT value FROM pathway_nodes
|
||||
WHERE level = 0 AND date_filter_id = 'all_6mo' AND chart_type = 'directory'
|
||||
LIMIT 1
|
||||
""")
|
||||
root_row = cursor.fetchone()
|
||||
total_patients = (root_row[0] or 0) if root_row else 0
|
||||
|
||||
return {
|
||||
"available_drugs": available_drugs,
|
||||
"available_directorates": available_directorates,
|
||||
"available_indications": available_indications,
|
||||
"available_trusts": available_trusts,
|
||||
"total_records": total_records,
|
||||
"total_patients": total_patients,
|
||||
"last_updated": last_updated,
|
||||
}
|
||||
except sqlite3.Error as e:
|
||||
|
||||
Reference in New Issue
Block a user