feat: complete core state variables (Task 3.1)

- Add datetime imports for date handling
- Add data state variables: last_updated, raw_data, latest_date_in_data
- Set last_seen date defaults (6 months ago to today)
- Add last_updated_display computed var for top bar
- Update top bar to show dynamic refresh timestamp
This commit is contained in:
Andrew Charlwood
2026-02-04 14:03:28 +00:00
parent 80997cb0de
commit fead4bf7dd
2 changed files with 80 additions and 21 deletions
+18 -16
View File
@@ -93,24 +93,26 @@ cd pathways_app && timeout 60 python -m reflex run 2>&1 | head -30
## Phase 3: State Management
### 3.1 Core State Variables
- [ ] Define filter state variables in `AppState`:
- [x] Define filter state variables in `AppState`:
- `initiated_filter_enabled: bool = False`
- `initiated_from: datetime`
- `initiated_to: datetime`
- `initiated_from_date: str = ""` (ISO date string)
- `initiated_to_date: str = ""`
- `last_seen_filter_enabled: bool = True`
- `last_seen_from: datetime` (default: 6 months ago)
- `last_seen_to: datetime` (default: latest in dataset)
- `selected_drugs: List[str]` (default: all)
- `selected_indications: List[str]` (default: all)
- `selected_directorates: List[str]` (default: all)
- [ ] Define data state variables:
- `data_loaded: bool`
- `total_records: int`
- `last_updated: datetime`
- `filtered_data: pd.DataFrame` (or computed)
- [ ] Define UI state variables:
- `chart_loading: bool`
- `error_message: str`
- `last_seen_from_date: str` (default: 6 months ago, computed at class definition)
- `last_seen_to_date: str` (default: today, updated on data load)
- `selected_drugs: list[str] = []` (empty = all)
- `selected_indications: list[str] = []` (empty = all)
- `selected_directorates: list[str] = []` (empty = all)
- [x] Define data state variables:
- `data_loaded: bool = False`
- `total_records: int = 0`
- `last_updated: str = ""` (ISO timestamp)
- `raw_data: list[dict[str, Any]] = []` (list of dicts, Reflex-friendly)
- `latest_date_in_data: str = ""` (for "to" date defaults)
- [x] Define UI state variables:
- `chart_loading: bool = False`
- `error_message: str = ""`
- `current_chart: str = "icicle"`
### 3.2 Data Loading
- [ ] Create `load_data()` method that reads from SQLite
+62 -5
View File
@@ -5,6 +5,9 @@ Single-page dashboard with reactive filtering and real-time chart updates.
Design reference: DESIGN_SYSTEM.md
"""
from datetime import datetime, timedelta
from typing import Any
import reflex as rx
from pathways_app.styles import (
@@ -41,24 +44,51 @@ class AppState(rx.State):
Will be expanded in Phase 3 with full filter state and data management.
"""
# Placeholder state variables (expanded in Phase 3)
# =========================================================================
# Data State Variables
# =========================================================================
# Data loading status
data_loaded: bool = False
total_records: int = 0
chart_loading: bool = False
error_message: str = ""
# Data freshness tracking
last_updated: str = "" # ISO format timestamp of last data load
# Raw data storage - list of dicts (Reflex-friendly)
# Each dict represents a patient record with keys like:
# UPID, Drug Name, Intervention Date, Price Actual, Directory, etc.
raw_data: list[dict[str, Any]] = []
# Latest date in dataset (detected on load, used for "to" date defaults)
latest_date_in_data: str = ""
# =========================================================================
# UI State Variables
# =========================================================================
# Placeholder for current chart type (for top bar tabs)
current_chart: str = "icicle"
# =========================================================================
# Filter State Variables
# =========================================================================
# Filter toggle state
initiated_filter_enabled: bool = False
last_seen_filter_enabled: bool = True
# Date filter values (ISO format strings for simplicity)
# Date filter values (ISO format strings YYYY-MM-DD)
# Initiated filter: Defaults empty (filter is OFF by default)
initiated_from_date: str = ""
initiated_to_date: str = ""
last_seen_from_date: str = ""
last_seen_to_date: str = ""
# Last Seen filter: Defaults to last 6 months (filter is ON by default)
# These will be updated on data load to use actual latest date
last_seen_from_date: str = (datetime.now() - timedelta(days=180)).strftime("%Y-%m-%d")
last_seen_to_date: str = datetime.now().strftime("%Y-%m-%d")
# Available options for dropdowns (populated from data in Phase 3)
available_drugs: list[str] = ["Drug A", "Drug B", "Drug C", "Drug D", "Drug E"]
@@ -292,6 +322,33 @@ class AppState(rx.State):
return ""
return f"{self.indication_match_rate:.0f}%"
@rx.var
def last_updated_display(self) -> str:
"""Format last updated timestamp for display in top bar."""
if not self.last_updated:
return "Never"
try:
# Parse ISO format timestamp
dt = datetime.fromisoformat(self.last_updated)
now = datetime.now()
diff = now - dt
if diff.days == 0:
if diff.seconds < 60:
return "Just now"
if diff.seconds < 3600:
mins = diff.seconds // 60
return f"{mins}m ago"
hours = diff.seconds // 3600
return f"{hours}h ago"
if diff.days == 1:
return "Yesterday"
if diff.days < 7:
return f"{diff.days}d ago"
return dt.strftime("%d %b %Y")
except (ValueError, TypeError):
return "Unknown"
# =============================================================================
# Layout Components
@@ -630,7 +687,7 @@ def top_bar() -> rx.Component:
rx.text(
rx.cond(
AppState.data_loaded,
"Last refreshed: recently",
"Refreshed: " + AppState.last_updated_display,
"Connecting...",
),
font_size="11px",