Upload folder using huggingface_hub
Browse files- home.py +4 -1
- modules/db.py +2 -2
- plot_helper.py +18 -6
home.py
CHANGED
|
@@ -514,7 +514,10 @@ def build_home_page():
|
|
| 514 |
gr.Column()
|
| 515 |
test_names_state = gr.State([])
|
| 516 |
num_cols = gr.State(4)
|
| 517 |
-
|
|
|
|
|
|
|
|
|
|
| 518 |
chart_outputs = []
|
| 519 |
|
| 520 |
# Compute how many plots per column (ceil division)
|
|
|
|
| 514 |
gr.Column()
|
| 515 |
test_names_state = gr.State([])
|
| 516 |
num_cols = gr.State(4)
|
| 517 |
+
with gr.Row():
|
| 518 |
+
gr.Column()
|
| 519 |
+
refresh_btn = gr.Button("🔄 Refresh", scale=0)
|
| 520 |
+
gr.Column()
|
| 521 |
chart_outputs = []
|
| 522 |
|
| 523 |
# Compute how many plots per column (ceil division)
|
modules/db.py
CHANGED
|
@@ -82,7 +82,7 @@ class SheamiDB:
|
|
| 82 |
return patient
|
| 83 |
|
| 84 |
async def get_patients_by_user(self, user_id: str) -> list:
|
| 85 |
-
cursor = self.patients.find({"user_id": ObjectId(user_id)})
|
| 86 |
patients = await cursor.to_list(
|
| 87 |
length=None
|
| 88 |
) # length=None returns all documents
|
|
@@ -150,7 +150,7 @@ class SheamiDB:
|
|
| 150 |
async def get_trends_by_patient(
|
| 151 |
self, patient_id: str, fields: list[str] = None, serializable=False
|
| 152 |
) -> list:
|
| 153 |
-
cursor = self.trends.find({"patient_id": ObjectId(patient_id)})
|
| 154 |
trends = await cursor.to_list(length=None)
|
| 155 |
if fields:
|
| 156 |
trends = [
|
|
|
|
| 82 |
return patient
|
| 83 |
|
| 84 |
async def get_patients_by_user(self, user_id: str) -> list:
|
| 85 |
+
cursor = self.patients.find({"user_id": ObjectId(user_id)}).sort("name")
|
| 86 |
patients = await cursor.to_list(
|
| 87 |
length=None
|
| 88 |
) # length=None returns all documents
|
|
|
|
| 150 |
async def get_trends_by_patient(
|
| 151 |
self, patient_id: str, fields: list[str] = None, serializable=False
|
| 152 |
) -> list:
|
| 153 |
+
cursor = self.trends.find({"patient_id": ObjectId(patient_id)}).sort("test_name")
|
| 154 |
trends = await cursor.to_list(length=None)
|
| 155 |
if fields:
|
| 156 |
trends = [
|
plot_helper.py
CHANGED
|
@@ -10,6 +10,7 @@ from common import get_db
|
|
| 10 |
MAX_CHARTS_IN_PAGE = 50
|
| 11 |
NUM_COLS = 4
|
| 12 |
|
|
|
|
| 13 |
def coerce_to_number(val):
|
| 14 |
"""Try converting to int/float, else return original string."""
|
| 15 |
if val is None:
|
|
@@ -26,6 +27,7 @@ def coerce_to_number(val):
|
|
| 26 |
except (ValueError, TypeError):
|
| 27 |
return val # fallback to original (string, unit, etc.)
|
| 28 |
|
|
|
|
| 29 |
def build_trend_figure(trend_doc: Dict[str, Any]) -> Figure:
|
| 30 |
"""Make a Plotly line chart for a single test's trend_data with optional reference ranges."""
|
| 31 |
points = trend_doc.get("trend_data", [])
|
|
@@ -59,8 +61,10 @@ def build_trend_figure(trend_doc: Dict[str, Any]) -> Figure:
|
|
| 59 |
if ref_min is not None and ref_max is not None:
|
| 60 |
fig.add_shape(
|
| 61 |
type="rect",
|
| 62 |
-
x0=min(dates),
|
| 63 |
-
|
|
|
|
|
|
|
| 64 |
fillcolor="rgba(0,200,0,0.1)", # light green
|
| 65 |
line=dict(width=0),
|
| 66 |
layer="below",
|
|
@@ -100,10 +104,11 @@ def build_trend_figure(trend_doc: Dict[str, Any]) -> Figure:
|
|
| 100 |
margin=dict(l=30, r=20, t=40, b=30),
|
| 101 |
xaxis_title="Date",
|
| 102 |
yaxis_title="Value",
|
| 103 |
-
title=f"{trend_doc.get('test_name','')}
|
| 104 |
)
|
| 105 |
return sanitize_plotly_figure(fig)
|
| 106 |
|
|
|
|
| 107 |
async def load_all_trend_figures(patient_id: str):
|
| 108 |
"""Fetch all test trend docs and return list of Plot figures."""
|
| 109 |
if not patient_id:
|
|
@@ -114,6 +119,7 @@ async def load_all_trend_figures(patient_id: str):
|
|
| 114 |
figures = [build_trend_figure(doc) for doc in docs if doc]
|
| 115 |
return figures
|
| 116 |
|
|
|
|
| 117 |
async def update_trends(patient_id, page, num_cols):
|
| 118 |
figures = await load_all_trend_figures(patient_id)
|
| 119 |
total_pages = (len(figures) - 1) // MAX_CHARTS_IN_PAGE + 1
|
|
@@ -125,20 +131,24 @@ async def update_trends(patient_id, page, num_cols):
|
|
| 125 |
outputs = []
|
| 126 |
for i in range(MAX_CHARTS_IN_PAGE):
|
| 127 |
if i < len(page_figures):
|
| 128 |
-
|
|
|
|
|
|
|
| 129 |
else:
|
| 130 |
-
outputs.append(gr.update(visible=False))
|
| 131 |
|
| 132 |
# also return updated page + page info for a label
|
| 133 |
return outputs + [page, f"Page {page+1} / {total_pages}"]
|
| 134 |
|
|
|
|
| 135 |
def _to_jsonable_dt(x):
|
| 136 |
if isinstance(x, pd.Timestamp):
|
| 137 |
-
return x.to_pydatetime()
|
| 138 |
if isinstance(x, np.datetime64):
|
| 139 |
return pd.to_datetime(x).to_pydatetime()
|
| 140 |
return x
|
| 141 |
|
|
|
|
| 142 |
def sanitize_plotly_figure(fig):
|
| 143 |
# traces (x/xbins/…)
|
| 144 |
for tr in fig.data:
|
|
@@ -169,9 +179,11 @@ def sanitize_plotly_figure(fig):
|
|
| 169 |
|
| 170 |
return fig
|
| 171 |
|
|
|
|
| 172 |
def next_page(page, figures_len):
|
| 173 |
total_pages = (figures_len - 1) // MAX_CHARTS_IN_PAGE + 1
|
| 174 |
return min(page + 1, total_pages - 1)
|
| 175 |
|
|
|
|
| 176 |
def prev_page(page):
|
| 177 |
return max(page - 1, 0)
|
|
|
|
| 10 |
MAX_CHARTS_IN_PAGE = 50
|
| 11 |
NUM_COLS = 4
|
| 12 |
|
| 13 |
+
|
| 14 |
def coerce_to_number(val):
|
| 15 |
"""Try converting to int/float, else return original string."""
|
| 16 |
if val is None:
|
|
|
|
| 27 |
except (ValueError, TypeError):
|
| 28 |
return val # fallback to original (string, unit, etc.)
|
| 29 |
|
| 30 |
+
|
| 31 |
def build_trend_figure(trend_doc: Dict[str, Any]) -> Figure:
|
| 32 |
"""Make a Plotly line chart for a single test's trend_data with optional reference ranges."""
|
| 33 |
points = trend_doc.get("trend_data", [])
|
|
|
|
| 61 |
if ref_min is not None and ref_max is not None:
|
| 62 |
fig.add_shape(
|
| 63 |
type="rect",
|
| 64 |
+
x0=min(dates),
|
| 65 |
+
x1=max(dates),
|
| 66 |
+
y0=ref_min,
|
| 67 |
+
y1=ref_max,
|
| 68 |
fillcolor="rgba(0,200,0,0.1)", # light green
|
| 69 |
line=dict(width=0),
|
| 70 |
layer="below",
|
|
|
|
| 104 |
margin=dict(l=30, r=20, t=40, b=30),
|
| 105 |
xaxis_title="Date",
|
| 106 |
yaxis_title="Value",
|
| 107 |
+
title=f"{trend_doc.get('test_name','')}",
|
| 108 |
)
|
| 109 |
return sanitize_plotly_figure(fig)
|
| 110 |
|
| 111 |
+
|
| 112 |
async def load_all_trend_figures(patient_id: str):
|
| 113 |
"""Fetch all test trend docs and return list of Plot figures."""
|
| 114 |
if not patient_id:
|
|
|
|
| 119 |
figures = [build_trend_figure(doc) for doc in docs if doc]
|
| 120 |
return figures
|
| 121 |
|
| 122 |
+
|
| 123 |
async def update_trends(patient_id, page, num_cols):
|
| 124 |
figures = await load_all_trend_figures(patient_id)
|
| 125 |
total_pages = (len(figures) - 1) // MAX_CHARTS_IN_PAGE + 1
|
|
|
|
| 131 |
outputs = []
|
| 132 |
for i in range(MAX_CHARTS_IN_PAGE):
|
| 133 |
if i < len(page_figures):
|
| 134 |
+
title = page_figures[i].layout.title.text
|
| 135 |
+
page_figures[i].update_layout(title="")
|
| 136 |
+
outputs.append(gr.update(value=page_figures[i], visible=True, label=title))
|
| 137 |
else:
|
| 138 |
+
outputs.append(gr.update(visible=False, label="", value=None))
|
| 139 |
|
| 140 |
# also return updated page + page info for a label
|
| 141 |
return outputs + [page, f"Page {page+1} / {total_pages}"]
|
| 142 |
|
| 143 |
+
|
| 144 |
def _to_jsonable_dt(x):
|
| 145 |
if isinstance(x, pd.Timestamp):
|
| 146 |
+
return x.to_pydatetime() # or x.isoformat()
|
| 147 |
if isinstance(x, np.datetime64):
|
| 148 |
return pd.to_datetime(x).to_pydatetime()
|
| 149 |
return x
|
| 150 |
|
| 151 |
+
|
| 152 |
def sanitize_plotly_figure(fig):
|
| 153 |
# traces (x/xbins/…)
|
| 154 |
for tr in fig.data:
|
|
|
|
| 179 |
|
| 180 |
return fig
|
| 181 |
|
| 182 |
+
|
| 183 |
def next_page(page, figures_len):
|
| 184 |
total_pages = (figures_len - 1) // MAX_CHARTS_IN_PAGE + 1
|
| 185 |
return min(page + 1, total_pages - 1)
|
| 186 |
|
| 187 |
+
|
| 188 |
def prev_page(page):
|
| 189 |
return max(page - 1, 0)
|