Upload folder using huggingface_hub
Browse files- home.py +45 -9
- modules/db.py +27 -1
- plot_helper.py +21 -1
- ui.py +1 -1
home.py
CHANGED
|
@@ -26,6 +26,7 @@ from plot_helper import (
|
|
| 26 |
MAX_CHARTS_IN_PAGE,
|
| 27 |
NUM_COLS,
|
| 28 |
build_trend_figure,
|
|
|
|
| 29 |
reset_trends,
|
| 30 |
update_trends,
|
| 31 |
)
|
|
@@ -278,7 +279,7 @@ def format_patient_info(patient):
|
|
| 278 |
|
| 279 |
async def load_patient_bundle(
|
| 280 |
patient_id: str,
|
| 281 |
-
)
|
| 282 |
"""
|
| 283 |
Given patient_id, return:
|
| 284 |
- reports_df
|
|
@@ -286,7 +287,9 @@ async def load_patient_bundle(
|
|
| 286 |
- meta dict with patient basics
|
| 287 |
- final_reports list
|
| 288 |
- run_stats list
|
| 289 |
-
- vitals
|
|
|
|
|
|
|
| 290 |
"""
|
| 291 |
if not patient_id:
|
| 292 |
return pd.DataFrame(), [], {}, []
|
|
@@ -302,6 +305,7 @@ async def load_patient_bundle(
|
|
| 302 |
run_stats = await db.get_run_stats_by_patient(patient_id)
|
| 303 |
latest_vitals = await render_latest_vitals_card_layout(patient_id)
|
| 304 |
vitals_history = await db.get_vitals_by_patient(patient_id)
|
|
|
|
| 305 |
|
| 306 |
reports_df = flatten_reports_v2(reports)
|
| 307 |
test_names = trends_index(trends)
|
|
@@ -322,6 +326,7 @@ async def load_patient_bundle(
|
|
| 322 |
run_stats,
|
| 323 |
vitals_history,
|
| 324 |
latest_vitals,
|
|
|
|
| 325 |
)
|
| 326 |
|
| 327 |
|
|
@@ -605,18 +610,39 @@ def build_home_page(
|
|
| 605 |
label="Unit", placeholder="e.g., bpm, cm"
|
| 606 |
)
|
| 607 |
|
| 608 |
-
save_vitals_btn = gr.Button(
|
|
|
|
|
|
|
| 609 |
save_vitals_status = gr.Markdown()
|
| 610 |
# Latest Vitals
|
| 611 |
with gr.Row():
|
| 612 |
latest_vitals_cards = [
|
| 613 |
gr.Label(visible=False) for _ in range(20)
|
| 614 |
]
|
| 615 |
-
|
| 616 |
-
|
| 617 |
-
|
| 618 |
-
|
| 619 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 620 |
with gr.Tab("🧪 Tests", id=2):
|
| 621 |
reports_df = gr.DataFrame(
|
| 622 |
headers=[
|
|
@@ -794,6 +820,7 @@ def build_home_page(
|
|
| 794 |
run_stats,
|
| 795 |
vitals_history,
|
| 796 |
latest_vitals,
|
|
|
|
| 797 |
) = await load_patient_bundle(patient_id)
|
| 798 |
(*trends, page, page_info, prev_btn, next_btn) = await update_trends(
|
| 799 |
patient_id=patient_id, page=0, num_cols=NUM_COLS
|
|
@@ -815,6 +842,7 @@ def build_home_page(
|
|
| 815 |
value=flatten_vitals(vitals_history),
|
| 816 |
),
|
| 817 |
*latest_vitals,
|
|
|
|
| 818 |
*trends,
|
| 819 |
page,
|
| 820 |
page_info,
|
|
@@ -841,6 +869,7 @@ def build_home_page(
|
|
| 841 |
value=[],
|
| 842 |
),
|
| 843 |
gr.update(),
|
|
|
|
| 844 |
*trends,
|
| 845 |
page,
|
| 846 |
page_info,
|
|
@@ -895,6 +924,7 @@ def build_home_page(
|
|
| 895 |
run_stats_df,
|
| 896 |
vitals_df,
|
| 897 |
*latest_vitals_cards,
|
|
|
|
| 898 |
*chart_outputs,
|
| 899 |
current_page,
|
| 900 |
page_info,
|
|
@@ -990,7 +1020,12 @@ def build_home_page(
|
|
| 990 |
vitals_custom_value,
|
| 991 |
vitals_custom_unit,
|
| 992 |
],
|
| 993 |
-
outputs=[
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 994 |
)
|
| 995 |
|
| 996 |
# open modal and set states
|
|
@@ -1019,6 +1054,7 @@ def build_home_page(
|
|
| 1019 |
run_stats_df,
|
| 1020 |
vitals_df,
|
| 1021 |
*latest_vitals_cards,
|
|
|
|
| 1022 |
*chart_outputs,
|
| 1023 |
current_page,
|
| 1024 |
page_info,
|
|
|
|
| 26 |
MAX_CHARTS_IN_PAGE,
|
| 27 |
NUM_COLS,
|
| 28 |
build_trend_figure,
|
| 29 |
+
render_vitals_plot_layout,
|
| 30 |
reset_trends,
|
| 31 |
update_trends,
|
| 32 |
)
|
|
|
|
| 279 |
|
| 280 |
async def load_patient_bundle(
|
| 281 |
patient_id: str,
|
| 282 |
+
):
|
| 283 |
"""
|
| 284 |
Given patient_id, return:
|
| 285 |
- reports_df
|
|
|
|
| 287 |
- meta dict with patient basics
|
| 288 |
- final_reports list
|
| 289 |
- run_stats list
|
| 290 |
+
- vitals history
|
| 291 |
+
- latest vitals
|
| 292 |
+
- vitals plots
|
| 293 |
"""
|
| 294 |
if not patient_id:
|
| 295 |
return pd.DataFrame(), [], {}, []
|
|
|
|
| 305 |
run_stats = await db.get_run_stats_by_patient(patient_id)
|
| 306 |
latest_vitals = await render_latest_vitals_card_layout(patient_id)
|
| 307 |
vitals_history = await db.get_vitals_by_patient(patient_id)
|
| 308 |
+
vitals_plots = await render_vitals_plot_layout(patient_id)
|
| 309 |
|
| 310 |
reports_df = flatten_reports_v2(reports)
|
| 311 |
test_names = trends_index(trends)
|
|
|
|
| 326 |
run_stats,
|
| 327 |
vitals_history,
|
| 328 |
latest_vitals,
|
| 329 |
+
vitals_plots,
|
| 330 |
)
|
| 331 |
|
| 332 |
|
|
|
|
| 610 |
label="Unit", placeholder="e.g., bpm, cm"
|
| 611 |
)
|
| 612 |
|
| 613 |
+
save_vitals_btn = gr.Button(
|
| 614 |
+
"Save Readings", variant="huggingface", scale=0
|
| 615 |
+
)
|
| 616 |
save_vitals_status = gr.Markdown()
|
| 617 |
# Latest Vitals
|
| 618 |
with gr.Row():
|
| 619 |
latest_vitals_cards = [
|
| 620 |
gr.Label(visible=False) for _ in range(20)
|
| 621 |
]
|
| 622 |
+
with gr.Row():
|
| 623 |
+
# Historical Vitals
|
| 624 |
+
with gr.Column(scale=1):
|
| 625 |
+
vitals_df = gr.DataFrame(
|
| 626 |
+
headers=[
|
| 627 |
+
"Date",
|
| 628 |
+
"Name",
|
| 629 |
+
"Value",
|
| 630 |
+
"Unit",
|
| 631 |
+
"AI Status",
|
| 632 |
+
],
|
| 633 |
+
interactive=False,
|
| 634 |
+
)
|
| 635 |
+
with gr.Column(scale=2):
|
| 636 |
+
vitals_plots = []
|
| 637 |
+
rows = math.ceil(
|
| 638 |
+
20 / 2
|
| 639 |
+
) # default num_cols_per_row columns per row
|
| 640 |
+
for r in range(rows):
|
| 641 |
+
with gr.Row():
|
| 642 |
+
for c in range(
|
| 643 |
+
2
|
| 644 |
+
): # num_cols_per_row cols per row
|
| 645 |
+
vitals_plots.append(gr.Plot(visible=True))
|
| 646 |
with gr.Tab("🧪 Tests", id=2):
|
| 647 |
reports_df = gr.DataFrame(
|
| 648 |
headers=[
|
|
|
|
| 820 |
run_stats,
|
| 821 |
vitals_history,
|
| 822 |
latest_vitals,
|
| 823 |
+
vitals_plots,
|
| 824 |
) = await load_patient_bundle(patient_id)
|
| 825 |
(*trends, page, page_info, prev_btn, next_btn) = await update_trends(
|
| 826 |
patient_id=patient_id, page=0, num_cols=NUM_COLS
|
|
|
|
| 842 |
value=flatten_vitals(vitals_history),
|
| 843 |
),
|
| 844 |
*latest_vitals,
|
| 845 |
+
*vitals_plots,
|
| 846 |
*trends,
|
| 847 |
page,
|
| 848 |
page_info,
|
|
|
|
| 869 |
value=[],
|
| 870 |
),
|
| 871 |
gr.update(),
|
| 872 |
+
gr.update(),
|
| 873 |
*trends,
|
| 874 |
page,
|
| 875 |
page_info,
|
|
|
|
| 924 |
run_stats_df,
|
| 925 |
vitals_df,
|
| 926 |
*latest_vitals_cards,
|
| 927 |
+
*vitals_plots,
|
| 928 |
*chart_outputs,
|
| 929 |
current_page,
|
| 930 |
page_info,
|
|
|
|
| 1020 |
vitals_custom_value,
|
| 1021 |
vitals_custom_unit,
|
| 1022 |
],
|
| 1023 |
+
outputs=[
|
| 1024 |
+
save_vitals_status,
|
| 1025 |
+
vitals_df,
|
| 1026 |
+
*latest_vitals_cards,
|
| 1027 |
+
*vitals_plots,
|
| 1028 |
+
],
|
| 1029 |
)
|
| 1030 |
|
| 1031 |
# open modal and set states
|
|
|
|
| 1054 |
run_stats_df,
|
| 1055 |
vitals_df,
|
| 1056 |
*latest_vitals_cards,
|
| 1057 |
+
*vitals_plots,
|
| 1058 |
*chart_outputs,
|
| 1059 |
current_page,
|
| 1060 |
page_info,
|
modules/db.py
CHANGED
|
@@ -10,7 +10,7 @@ from motor.motor_asyncio import AsyncIOMotorGridFSBucket
|
|
| 10 |
from motor.motor_asyncio import AsyncIOMotorClient, AsyncIOMotorGridFSBucket
|
| 11 |
import os
|
| 12 |
from dotenv import load_dotenv
|
| 13 |
-
|
| 14 |
|
| 15 |
class SheamiDB:
|
| 16 |
def __init__(self, uri: str = None, db_name: str = "sheami"):
|
|
@@ -741,3 +741,29 @@ class SheamiDB:
|
|
| 741 |
else:
|
| 742 |
vitals = {}
|
| 743 |
return vitals
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 10 |
from motor.motor_asyncio import AsyncIOMotorClient, AsyncIOMotorGridFSBucket
|
| 11 |
import os
|
| 12 |
from dotenv import load_dotenv
|
| 13 |
+
import pandas as pd
|
| 14 |
|
| 15 |
class SheamiDB:
|
| 16 |
def __init__(self, uri: str = None, db_name: str = "sheami"):
|
|
|
|
| 741 |
else:
|
| 742 |
vitals = {}
|
| 743 |
return vitals
|
| 744 |
+
|
| 745 |
+
async def get_vitals_trends_by_patient(self,patient_id):
|
| 746 |
+
docs = await self.vitals.aggregate([
|
| 747 |
+
{"$match": {"patient_id": ObjectId(patient_id)}},
|
| 748 |
+
{"$unwind": "$readings"},
|
| 749 |
+
{"$project": {
|
| 750 |
+
"date": 1,
|
| 751 |
+
"name": "$readings.name",
|
| 752 |
+
"value": "$readings.value",
|
| 753 |
+
"unit": "$readings.unit"
|
| 754 |
+
}},
|
| 755 |
+
{"$sort": {"date": 1}}
|
| 756 |
+
]).to_list(length=None)
|
| 757 |
+
|
| 758 |
+
df = pd.DataFrame(docs)
|
| 759 |
+
trend_docs = []
|
| 760 |
+
if "name" in df:
|
| 761 |
+
for vital_name, group in df.groupby("name"):
|
| 762 |
+
trend_docs.append({
|
| 763 |
+
"test_name": vital_name,
|
| 764 |
+
"trend_data": group[["date", "value"]].to_dict("records"),
|
| 765 |
+
"unit": group["unit"].iloc[0], # assume consistent unit
|
| 766 |
+
# TODO: optional: enrich with AI-based reference ranges
|
| 767 |
+
"test_reference_range": {}
|
| 768 |
+
})
|
| 769 |
+
return trend_docs
|
plot_helper.py
CHANGED
|
@@ -7,6 +7,7 @@ import numpy as np
|
|
| 7 |
import pandas as pd
|
| 8 |
from common import get_db
|
| 9 |
from ui import disable_component
|
|
|
|
| 10 |
|
| 11 |
MAX_CHARTS_IN_PAGE = 40
|
| 12 |
NUM_COLS = 4
|
|
@@ -108,7 +109,7 @@ def build_trend_figure(trend_doc: Dict[str, Any]) -> Figure:
|
|
| 108 |
title=f"{trend_doc.get('test_name','')}",
|
| 109 |
)
|
| 110 |
fig.update_yaxes(autorange=True)
|
| 111 |
-
fig.update_xaxes(autorange=True)
|
| 112 |
return sanitize_plotly_figure(fig)
|
| 113 |
|
| 114 |
|
|
@@ -216,3 +217,22 @@ def next_page(page, figures_len):
|
|
| 216 |
|
| 217 |
def prev_page(page):
|
| 218 |
return max(page - 1, 0)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 7 |
import pandas as pd
|
| 8 |
from common import get_db
|
| 9 |
from ui import disable_component
|
| 10 |
+
import plotly.express as px
|
| 11 |
|
| 12 |
MAX_CHARTS_IN_PAGE = 40
|
| 13 |
NUM_COLS = 4
|
|
|
|
| 109 |
title=f"{trend_doc.get('test_name','')}",
|
| 110 |
)
|
| 111 |
fig.update_yaxes(autorange=True)
|
| 112 |
+
fig.update_xaxes(autorange=True)
|
| 113 |
return sanitize_plotly_figure(fig)
|
| 114 |
|
| 115 |
|
|
|
|
| 217 |
|
| 218 |
def prev_page(page):
|
| 219 |
return max(page - 1, 0)
|
| 220 |
+
|
| 221 |
+
|
| 222 |
+
async def render_vitals_plot_layout(patient_id):
|
| 223 |
+
docs = await get_db().get_vitals_trends_by_patient(patient_id)
|
| 224 |
+
figures = [build_trend_figure(doc) for doc in docs if doc]
|
| 225 |
+
# Pad/truncate to exactly 20 charts
|
| 226 |
+
if len(figures) > 20:
|
| 227 |
+
figures = figures[:20]
|
| 228 |
+
elif len(figures) < 20:
|
| 229 |
+
while len(figures) < 20:
|
| 230 |
+
empty_fig = Figure()
|
| 231 |
+
empty_fig.update_layout(
|
| 232 |
+
title="No Data",
|
| 233 |
+
xaxis=dict(visible=False),
|
| 234 |
+
yaxis=dict(visible=False),
|
| 235 |
+
margin=dict(l=30, r=20, t=40, b=30),
|
| 236 |
+
)
|
| 237 |
+
figures.append(empty_fig)
|
| 238 |
+
return [gr.Plot(value=fig, label=fig.layout.title.text) for fig in figures]
|
ui.py
CHANGED
|
@@ -905,4 +905,4 @@ async def render_latest_vitals_card_layout(patient_id: str):
|
|
| 905 |
while len(cards) < 20:
|
| 906 |
cards.append(gr.Label(value="-", label="", visible=True))
|
| 907 |
|
| 908 |
-
return cards
|
|
|
|
| 905 |
while len(cards) < 20:
|
| 906 |
cards.append(gr.Label(value="-", label="", visible=True))
|
| 907 |
|
| 908 |
+
return cards
|