delphi-pump-analytics / pages /2_pump_cycles.py
pandeydigant31's picture
Initial deploy: DELPHI Pump Analytics (renamed from MURPHY)
199bfa3 verified
Raw
History Blame Contribute Delete
3.98 kB
"""
Feature 2: Testing Cycle Browser
Month-year filter with pre-computed cycles from the cycle_periods DB table.
"""
import streamlit as st
import pandas as pd
from core.db_connector import get_db_connector
from ui.components import page_header, metric_row
from ui.plotly_charts import create_cycle_timeline
page_header(
"Testing Cycles",
"Cycles are identified from motor activity — >0% starts a cycle and returning to 0% ends a cycle. "
"To qualify, a testing cycle must show either >5% motor speed OR >10 sec duration observed."
)
db = get_db_connector()
@st.cache_data(ttl=3600)
def get_available_months():
return db.get_available_months()
@st.cache_data(ttl=3600)
def get_cycle_months():
return db.get_cycle_months()
@st.cache_data(ttl=600)
def load_cycles_for_month(month_str: str):
"""Load pre-computed cycles from cycle_periods table for a given month."""
return db.get_cycle_periods(month_str)
@st.cache_data(ttl=600)
def load_all_cycles():
"""Load all pre-computed cycles (for cross-page use)."""
return db.get_cycle_periods()
# Sidebar: month selector
with st.sidebar:
st.subheader("Select Period")
sensor_months = get_available_months()
cycle_months = get_cycle_months()
months = sorted(set(sensor_months) | set(cycle_months))
if not months:
st.error("No data months found in database.")
st.stop()
selected_month = st.selectbox(
"Month",
months,
index=len(months) - 1, # Default to most recent
key="cycle_month",
)
# Load cycles for selected month
cycles = load_cycles_for_month(selected_month)
# Also cache all cycles for the comparison page
all_cycles = load_all_cycles()
st.session_state.all_cycles = all_cycles
if not cycles:
st.info(f"No testing cycles found in {selected_month}.")
else:
# Summary metrics
total_runtime = sum(c['duration_minutes'] for c in cycles)
peak_pressures = [c.get('peak_pressure', 0) for c in cycles if c.get('peak_pressure')]
max_pressure = max(peak_pressures) if peak_pressures else 0
total_dispensed = sum(c.get('total_kg_dispensed', 0) for c in cycles)
metrics = [
{'label': 'Cycles', 'value': len(cycles), 'unit': ''},
{'label': 'Total Runtime', 'value': f"{total_runtime:.0f}", 'unit': 'min'},
{'label': 'Peak Pressure (PT130)', 'value': f"{max_pressure:.0f}", 'unit': 'bar'},
{'label': 'Total Dispensed', 'value': f"{total_dispensed:.1f}", 'unit': 'kg'},
]
metric_row(metrics)
st.divider()
# Timeline chart
fig = create_cycle_timeline(cycles, selected_month)
st.plotly_chart(fig, use_container_width=True)
st.divider()
# Cycle table
st.subheader("Cycle Details")
table_data = []
for c in cycles:
display_start = c.get('start_time_et') or c['start_time']
display_end = c.get('end_time_et') or c['end_time']
table_data.append({
'Cycle': c['cycle_id'],
'Start (ET)': display_start.strftime('%b %d %H:%M'),
'End (ET)': display_end.strftime('%b %d %H:%M'),
'Duration (min)': f"{c['duration_minutes']:.1f}",
'Peak Speed (RPM)': f"{c.get('peak_speed', 0):.0f}",
'Peak Pressure (bar)': f"{c.get('peak_pressure', 0):.0f}" if c.get('peak_pressure') else 'N/A',
'Avg Flow (kg/min)': f"{c.get('avg_flow', 0):.2f}" if c.get('avg_flow') else 'N/A',
'Dispensed (kg)': f"{c.get('total_kg_dispensed', 0):.2f}" if c.get('total_kg_dispensed') else 'N/A',
'Pump Strokes': f"{c.get('total_pump_strokes', 0):.0f}" if c.get('total_pump_strokes') else 'N/A',
})
df_table = pd.DataFrame(table_data)
st.dataframe(df_table, use_container_width=True, hide_index=True)
# Store cycles in session state for other pages
st.session_state.detected_cycles = cycles
st.info("Select a cycle above, then navigate to **Cycle Detail** page for in-depth analysis.")