import pandas as pd import plotly.graph_objects as go from ui_common import ( PALETTE, THRESHOLD_LINES, add_basal_area_trace, add_bolus_marker_traces, add_carb_bars_trace, add_target_line_trace, add_threshold_lines, build_tir_stacked_bar_figure, hex_to_rgba, ) def test_hex_to_rgba_converts_correctly(): assert hex_to_rgba("#2a78d6", 0.3) == "rgba(42,120,214,0.3)" def test_hex_to_rgba_handles_hash_optional(): assert hex_to_rgba("2a78d6", 0.3) == "rgba(42,120,214,0.3)" def test_add_threshold_lines_adds_one_line_per_clinical_threshold(): fig = go.Figure() add_threshold_lines(fig, PALETTE["light"]) assert len(fig.layout.shapes) == len(THRESHOLD_LINES) def test_add_threshold_lines_targets_subplot_row_when_given(): fig = go.Figure() fig.add_trace(go.Scatter(x=[0], y=[0])) add_threshold_lines(fig, PALETTE["light"], row=1) assert len(fig.layout.shapes) == len(THRESHOLD_LINES) def test_build_tir_stacked_bar_figure_has_one_bar_trace_per_band(): tir = {"very_low": 1.0, "low": 3.0, "in_range": 80.0, "high": 12.0, "very_high": 4.0} fig = build_tir_stacked_bar_figure(tir, PALETTE["light"]) assert len(fig.data) == 5 assert [trace.y[0] for trace in fig.data] == [1.0, 3.0, 80.0, 12.0, 4.0] assert fig.layout.barmode == "stack" def test_add_basal_area_trace_adds_one_filled_step_trace(): basal = pd.DataFrame( {"timestamp": pd.to_datetime(["2026-01-01 00:00", "2026-01-01 06:00"]), "rate": [0.8, 1.2]} ) fig = go.Figure() add_basal_area_trace(fig, basal, PALETTE["light"]) assert len(fig.data) == 1 assert fig.data[0].name == "Basaal" assert fig.data[0].fill == "tozeroy" assert fig.data[0].line.shape == "hv" def test_add_basal_area_trace_skips_empty_input(): fig = go.Figure() add_basal_area_trace(fig, pd.DataFrame(columns=["timestamp", "rate"]), PALETTE["light"]) assert len(fig.data) == 0 def test_add_carb_bars_trace_adds_one_bar_trace(): carbs = pd.DataFrame({"timestamp": pd.to_datetime(["2026-01-01 08:00"]), "carbs": [40.0]}) fig = go.Figure() add_carb_bars_trace(fig, carbs, PALETTE["light"]) assert len(fig.data) == 1 assert fig.data[0].name == "Koolhydraten" assert isinstance(fig.data[0], go.Bar) def test_add_bolus_marker_traces_adds_one_trace_per_category_present(): bolus = pd.DataFrame( { "timestamp": pd.to_datetime(["2026-01-01 08:00", "2026-01-01 12:00"]), "insulin": [4.0, 0.3], "category": ["food", "smb"], } ) fig = go.Figure() add_bolus_marker_traces(fig, bolus, PALETTE["light"], lane_y=2.0) names = {trace.name for trace in fig.data} assert names == {"Bolus (voeding)", "SMB"} def test_add_bolus_marker_traces_labels_food_and_correction_but_not_smb(): bolus = pd.DataFrame( { "timestamp": pd.to_datetime(["2026-01-01 08:00", "2026-01-01 12:00"]), "insulin": [4.0, 0.3], "category": ["food", "smb"], } ) fig = go.Figure() add_bolus_marker_traces(fig, bolus, PALETTE["light"], lane_y=2.0) by_name = {trace.name: trace for trace in fig.data} assert by_name["Bolus (voeding)"].mode == "markers+text" assert by_name["SMB"].mode == "markers" def test_add_bolus_marker_traces_skips_empty_input(): fig = go.Figure() add_bolus_marker_traces(fig, pd.DataFrame(columns=["timestamp", "insulin", "category"]), PALETTE["light"], 2.0) assert len(fig.data) == 0 def test_add_target_line_trace_adds_one_line_trace(): target = pd.DataFrame({"timestamp": pd.to_datetime(["2026-01-01 00:00"]), "target": [6.5]}) fig = go.Figure() add_target_line_trace(fig, target, PALETTE["light"]) assert len(fig.data) == 1 assert fig.data[0].name == "Streefwaarde (pomp)" def test_add_target_line_trace_skips_empty_input(): fig = go.Figure() add_target_line_trace(fig, pd.DataFrame(columns=["timestamp", "target"]), PALETTE["light"]) assert len(fig.data) == 0 def test_overlay_traces_support_an_hour_based_x_column_for_dagoverzicht(): """Dagoverzicht plot elke dag op een gedeelde 0-24u-as ('hour') i.p.v. echte datums; de hover mag daar dan ook geen datumformattering tonen.""" basal = pd.DataFrame({"hour": [0.0, 6.0], "rate": [0.8, 1.2]}) carbs = pd.DataFrame({"hour": [8.0], "carbs": [40.0]}) bolus = pd.DataFrame({"hour": [8.0], "insulin": [4.0], "category": ["food"]}) target = pd.DataFrame({"hour": [0.0], "target": [6.5]}) fig = go.Figure() add_basal_area_trace(fig, basal, PALETTE["light"], x_col="hour") add_carb_bars_trace(fig, carbs, PALETTE["light"], x_col="hour") add_bolus_marker_traces(fig, bolus, PALETTE["light"], lane_y=2.0, x_col="hour") add_target_line_trace(fig, target, PALETTE["light"], x_col="hour") assert len(fig.data) == 4 bolus_trace = next(t for t in fig.data if t.name == "Bolus (voeding)") assert list(bolus_trace.x) == [8.0] assert "%d-%m" not in bolus_trace.hovertemplate