HumanPlusX / src /ui /chart_components.py
FajarHidaa's picture
Upload 22 files
a91d88d verified
Raw History Blame Contribute Delete
3.99 kB
"""
Human+ Chart Components
Streamlit rendering wrappers untuk semua Plotly charts.
Import dari sini di halaman mana saja.
"""
import streamlit as st
from core.chart_builder import (
build_gauge_chart,
build_biomarker_bar_chart,
build_status_donut,
get_gauge_preset,
)
# Plotly config — hide toolbar, dark bg, responsive
_PLOTLY_CONFIG = {
"displayModeBar": False,
"responsive": True,
"staticPlot": False,
}
# Max gauge charts to show (avoid information overload)
_MAX_GAUGES = 6
def render_gauge_grid(biomarkers: list[dict]):
"""
Render a responsive 2-column grid of gauge charts
for the most important biomarkers (those with presets + non-neutral status).
"""
# Filter to biomarkers that have gauge presets AND are non-neutral
gaugeables = [
b for b in biomarkers
if b.get("raw_value") is not None
and b.get("status") != "neutral"
and get_gauge_preset(b["name"]) is not None
]
# Sort danger → warning → optimal, cap at max
priority = {"danger": 0, "warning": 1, "optimal": 2}
gaugeables = sorted(gaugeables, key=lambda b: priority.get(b["status"], 3))
gaugeables = gaugeables[:_MAX_GAUGES]
if not gaugeables:
st.markdown("""
<div style="
text-align:center; padding:20px;
font-family:'DM Sans',sans-serif;
font-size:0.8rem; color:#4a4a6a
">Tidak ada marker dengan gauge preset yang tersedia.</div>
""", unsafe_allow_html=True)
return
# Render in 2-column grid
pairs = [gaugeables[i:i+2] for i in range(0, len(gaugeables), 2)]
for pair in pairs:
cols = st.columns(len(pair))
for col, b in zip(cols, pair):
with col:
preset = get_gauge_preset(b["name"])
if preset is None:
continue
range_lo, range_hi, opt_lo, opt_hi = preset
try:
fig = build_gauge_chart(
name=b["name"],
value=float(b["raw_value"]),
unit=b.get("unit", ""),
status=b["status"],
range_lo=range_lo,
range_hi=range_hi,
optimal_lo=opt_lo,
optimal_hi=opt_hi,
height=210,
)
st.plotly_chart(
fig,
use_container_width=True,
config=_PLOTLY_CONFIG,
key=f"gauge_{b['name'].replace(' ', '_')}",
)
except Exception as e:
# Graceful fallback — don't crash the whole page
st.caption(f"⚠️ Chart tidak tersedia: {b['name']}")
def render_biomarker_bar_chart(biomarkers: list[dict]):
"""
Render horizontal bar chart untuk semua biomarker.
Color-coded by status sesuai Human+ brand.
"""
try:
fig = build_biomarker_bar_chart(biomarkers, height=350)
if fig is None:
st.caption("Tidak ada data numerik untuk ditampilkan.")
return
st.plotly_chart(
fig,
use_container_width=True,
config=_PLOTLY_CONFIG,
key="biomarker_bar",
)
except Exception:
st.caption("⚠️ Chart tidak tersedia saat ini.")
def render_status_donut(optimal: int, warning: int, danger: int):
"""
Render donut pie chart untuk summary status biomarker.
Small chart cocok untuk sidebar atau kolom kecil.
"""
try:
fig = build_status_donut(optimal, warning, danger)
if fig is None:
return
st.plotly_chart(
fig,
use_container_width=True,
config=_PLOTLY_CONFIG,
key="status_donut",
)
except Exception:
pass # Non-critical — skip silently