|
|
@@ -335,7 +335,7 @@ with tab1:
|
|
|
st.divider()
|
|
|
|
|
|
st.write("### 📈 Manager's Dashboard")
|
|
|
- m_col1, m_col2, m_col3 = st.columns(3)
|
|
|
+ m_col1, m_col2, m_col3, m_col4 = st.columns(4)
|
|
|
with m_col1:
|
|
|
st.metric("Total Bunches", data.get('total_count', 0))
|
|
|
with m_col2:
|
|
|
@@ -343,10 +343,15 @@ with tab1:
|
|
|
with m_col3:
|
|
|
abnormal = data['industrial_summary'].get('Abnormal', 0)
|
|
|
st.metric("Abnormal Alerts", abnormal, delta=-abnormal, delta_color="inverse")
|
|
|
+ with m_col4:
|
|
|
+ st.metric("Inference Speed", f"{data.get('inference_ms', 0):.1f} ms")
|
|
|
|
|
|
col1, col2 = st.columns([1.5, 1]) # Keep original col structure for summary below
|
|
|
|
|
|
- with col2:
|
|
|
+ with col1:
|
|
|
+ with st.expander("🛠️ Technical Evidence: Raw Output Tensor", expanded=False):
|
|
|
+ st.write("First 5 detections from raw output tensor:")
|
|
|
+ st.json(data.get('raw_array_sample', []))
|
|
|
with st.container(border=True):
|
|
|
st.write("### 🏷️ Detection Results")
|
|
|
if not data['detections']:
|
|
|
@@ -638,6 +643,9 @@ with tab3:
|
|
|
# --- Tab 4: History Vault ---
|
|
|
with tab4:
|
|
|
st.subheader("📜 Local History Vault")
|
|
|
+ if "selected_history_id" not in st.session_state:
|
|
|
+ st.session_state.selected_history_id = None
|
|
|
+
|
|
|
try:
|
|
|
res = requests.get(f"{API_BASE_URL}/get_history")
|
|
|
if res.status_code == 200:
|
|
|
@@ -645,32 +653,63 @@ with tab4:
|
|
|
if not history_data:
|
|
|
st.info("No saved records found.")
|
|
|
else:
|
|
|
- # Selection table
|
|
|
- df_history = pd.DataFrame(history_data)[['id', 'filename', 'timestamp']]
|
|
|
- selected_id = st.selectbox("Select a record to review:", df_history['id'])
|
|
|
-
|
|
|
- if selected_id:
|
|
|
- record = next(item for item in history_data if item["id"] == selected_id)
|
|
|
- detections = json.loads(record['detections'])
|
|
|
+ if st.session_state.selected_history_id is None:
|
|
|
+ # ListView Mode
|
|
|
+ st.write("### 📋 Record List")
|
|
|
+ df_history = pd.DataFrame(history_data)[['id', 'filename', 'timestamp', 'inference_ms']]
|
|
|
+ st.dataframe(df_history, hide_index=True, use_container_width=True)
|
|
|
|
|
|
- # Display Interactive Hover View
|
|
|
- if os.path.exists(record['archive_path']):
|
|
|
- with open(record['archive_path'], "rb") as f:
|
|
|
- hist_img = Image.open(f).convert("RGB")
|
|
|
- display_interactive_results(hist_img, detections, key=f"hist_{record['id']}")
|
|
|
+ id_to_select = st.number_input("Enter Record ID to view details:", min_value=int(df_history['id'].min()), max_value=int(df_history['id'].max()), step=1)
|
|
|
+ if st.button("Deep Dive Analysis", type="primary"):
|
|
|
+ st.session_state.selected_history_id = id_to_select
|
|
|
+ st.rerun()
|
|
|
+ else:
|
|
|
+ # Detail View Mode
|
|
|
+ record = next((item for item in history_data if item["id"] == st.session_state.selected_history_id), None)
|
|
|
+ if not record:
|
|
|
+ st.error("Record not found.")
|
|
|
+ if st.button("Back to List"):
|
|
|
+ st.session_state.selected_history_id = None
|
|
|
+ st.rerun()
|
|
|
+ else:
|
|
|
+ if st.button("⬅️ Back to History List"):
|
|
|
+ st.session_state.selected_history_id = None
|
|
|
+ st.rerun()
|
|
|
|
|
|
- st.write("### 📈 Archived Summary")
|
|
|
+ st.divider()
|
|
|
+ st.write(f"## 🔍 Deep Dive: Record #{record['id']} ({record['filename']})")
|
|
|
+ detections = json.loads(record['detections'])
|
|
|
summary = json.loads(record['summary'])
|
|
|
- s_col1, s_col2, s_col3 = st.columns(3)
|
|
|
- with s_col1:
|
|
|
+
|
|
|
+ # Metrics Row
|
|
|
+ h_col1, h_col2, h_col3, h_col4 = st.columns(4)
|
|
|
+ with h_col1:
|
|
|
st.metric("Total Bunches", sum(summary.values()))
|
|
|
- with s_col2:
|
|
|
+ with h_col2:
|
|
|
st.metric("Healthy (Ripe)", summary.get('Ripe', 0))
|
|
|
- with s_col3:
|
|
|
- abnormal = summary.get('Abnormal', 0)
|
|
|
- st.metric("Abnormal Alerts", abnormal)
|
|
|
- else:
|
|
|
- st.error(f"Archive file not found: {record['archive_path']}")
|
|
|
+ with h_col3:
|
|
|
+ st.metric("Abnormal Alerts", summary.get('Abnormal', 0))
|
|
|
+ with h_col4:
|
|
|
+ st.metric("Inference Speed", f"{record.get('inference_ms', 0) or 0:.1f} ms")
|
|
|
+
|
|
|
+ # Image View
|
|
|
+ if os.path.exists(record['archive_path']):
|
|
|
+ with open(record['archive_path'], "rb") as f:
|
|
|
+ hist_img = Image.open(f).convert("RGB")
|
|
|
+ display_interactive_results(hist_img, detections, key=f"hist_{record['id']}")
|
|
|
+ else:
|
|
|
+ st.error(f"Archive file not found: {record['archive_path']}")
|
|
|
+
|
|
|
+ # Technical Evidence Expander
|
|
|
+ with st.expander("🛠️ Technical Evidence: Raw Output Tensor"):
|
|
|
+ raw_data = record.get('raw_array_sample')
|
|
|
+ if raw_data:
|
|
|
+ try:
|
|
|
+ st.json(json.loads(raw_data))
|
|
|
+ except:
|
|
|
+ st.text(raw_data)
|
|
|
+ else:
|
|
|
+ st.info("No raw tensor data available for this record.")
|
|
|
else:
|
|
|
st.error(f"Failed to fetch history: {res.text}")
|
|
|
except Exception as e:
|