Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions cli/medperf/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@
cas_folder = "cas"
training_events_folder = "training_events"
certificates_folder = "certificates"
dashboards_folder = "dashboards"
assets_folder = "assets"
models_folder = "models"

Expand Down Expand Up @@ -177,6 +178,10 @@
"base": default_base_storage,
"name": certificates_folder,
},
"dashboards_folder": {
"base": default_base_storage,
"name": dashboards_folder,
},
"assets_folder": {
"base": default_base_storage,
"name": assets_folder,
Expand Down Expand Up @@ -209,6 +214,7 @@
"cas_folder",
"training_events_folder",
"certificates_folder",
"dashboards_folder",
"assets_folder",
"models_folder",
]
Expand Down
1 change: 1 addition & 0 deletions cli/medperf/dashboard/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"""Dashboard package for preparation monitoring."""
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@

initialize()

from .preparation_dashboard import t_app # noqa
from .preparation_dashboard import t_app # noqa
152 changes: 152 additions & 0 deletions cli/medperf/dashboard/assets/medperf-dashboard.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
@import url("https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap");

:root {
--medperf-green: #2e7d32;
--medperf-green-light: #4caf50;
--medperf-page-bg: #f1f8f4;
--medperf-card-bg: #ffffff;
--medperf-card-border: #c8e6c9;
--medperf-text: #111827;
--medperf-text-muted: #6b7280;
--medperf-radius: 16px;
--medperf-table-header-bg: #e8f5e9;
--medperf-table-header-fg: #1b5e20;
--medperf-table-cell-border: #e5e7eb;
}

html.dark {
--medperf-page-bg: #111827;
--medperf-card-bg: #1f2937;
--medperf-card-border: #374151;
--medperf-text: #f9fafb;
--medperf-text-muted: #9ca3af;
--medperf-table-header-bg: #14532d;
--medperf-table-header-fg: #bbf7d0;
--medperf-table-cell-border: #4b5563;
}

html,
body {
font-family: "Inter", ui-sans-serif, system-ui, sans-serif;
background-color: var(--medperf-page-bg) !important;
color: var(--medperf-text);
margin: 0;
}

body {
padding: 8px 12px 32px;
min-height: 100vh;
}

.medperf-dash-main {
max-width: 1280px;
margin: 0 auto;
}

.medperf-dash-title {
font-size: clamp(1.75rem, 4vw, 2.5rem);
font-weight: 700;
color: var(--medperf-green);
margin: 0.75rem 0 1.25rem;
letter-spacing: -0.02em;
}

html.dark .medperf-dash-title {
color: #4ade80;
}

.medperf-dash-section {
background: var(--medperf-card-bg);
border: 2px solid var(--medperf-card-border);
border-radius: var(--medperf-radius);
box-shadow:
0 10px 15px -3px rgba(0, 0, 0, 0.08),
0 4px 6px -2px rgba(0, 0, 0, 0.04);
padding: 1.5rem 1.25rem;
margin-top: 1.25rem;
margin-bottom: 0.25rem;
}

html.dark .medperf-dash-section {
box-shadow:
0 10px 15px -3px rgba(0, 0, 0, 0.25),
0 4px 6px -2px rgba(0, 0, 0, 0.15);
}

.medperf-dash-section h2 {
font-size: 1.5rem;
font-weight: 700;
color: var(--medperf-green);
text-align: center;
margin-bottom: 1.25rem;
}

html.dark .medperf-dash-section h2 {
color: #4ade80;
}

.medperf-dash-alert {
border-radius: var(--medperf-radius) !important;
border: 2px solid #fcd34d !important;
background: linear-gradient(135deg, #fffbeb 0%, #fef3c7 100%) !important;
max-width: 42rem;
margin-left: auto;
margin-right: auto;
}

html.dark .medperf-dash-alert {
border-color: #b45309 !important;
background: linear-gradient(135deg, #451a03 0%, #78350f 100%) !important;
color: #fde68a !important;
}

html.dark .medperf-dash-alert .alert-heading {
color: #fef3c7 !important;
}

.medperf-dash-table-wrap {
border-radius: 12px;
overflow: hidden;
border: 1px solid var(--medperf-card-border);
}

html.dark .dash-table-container .dash-spreadsheet-inner table {
background-color: var(--medperf-card-bg) !important;
}

html.dark .dash-table-container .dash-cell {
background-color: var(--medperf-card-bg) !important;
color: var(--medperf-text) !important;
border-color: var(--medperf-table-cell-border) !important;
}

html.dark .dash-table-container .dash-header {
background-color: var(--medperf-table-header-bg) !important;
color: var(--medperf-table-header-fg) !important;
border-color: var(--medperf-card-border) !important;
}

html.dark .dash-table-container .dash-filter--case {
background-color: var(--medperf-card-bg) !important;
color: var(--medperf-text) !important;
}

html.dark .dash-table-container .previous-next-container button {
color: var(--medperf-text) !important;
}

html.dark .medperf-dash-alert .lead {
color: inherit !important;
}

.modebar {
opacity: 0.7;
}

.modebar:hover {
opacity: 1;
}

html.dark .modebar-btn {
color: #e5e7eb !important;
}
109 changes: 109 additions & 0 deletions cli/medperf/dashboard/assets/zzz_medperf_theme_sync.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
/**
* Keeps Dash dashboard theme in sync with MedPerf WebUI:
* - Same localStorage key and prefers-color-scheme fallback as base.html
* - postMessage from parent (dashboard_wrapper) when user toggles theme
* - Plotly relayout after theme change (graphs render async)
*/
(function () {
function medperfDashIsDark() {
try {
var v = localStorage.getItem("medperf-dark");
return (
v === "1" ||
(v === null &&
window.matchMedia &&
window.matchMedia("(prefers-color-scheme: dark)").matches)
);
} catch (e) {
return false;
}
}

function medperfPlotlyRelayout() {
if (typeof window.Plotly === "undefined") return;
var dark = document.documentElement.classList.contains("dark");
var plots = document.querySelectorAll(".js-plotly-plot");
var i;
var gd;
for (i = 0; i < plots.length; i++) {
gd = plots[i];
try {
if (dark) {
window.Plotly.relayout(gd, {
paper_bgcolor: "rgba(0,0,0,0)",
plot_bgcolor: "#111827",
font: {
color: "#e5e7eb",
family: "Inter, ui-sans-serif, system-ui, sans-serif",
},
title: { font: { color: "#f9fafb" } },
legend: {
bgcolor: "rgba(31,41,55,0.95)",
bordercolor: "#374151",
font: { color: "#e5e7eb" },
},
"xaxis.gridcolor": "#374151",
"xaxis.linecolor": "#6b7280",
"xaxis.zerolinecolor": "#4b5563",
"xaxis.tickfont.color": "#d1d5db",
"yaxis.gridcolor": "#374151",
"yaxis.linecolor": "#6b7280",
"yaxis.zerolinecolor": "#4b5563",
"yaxis.tickfont.color": "#d1d5db",
});
} else {
window.Plotly.relayout(gd, {
paper_bgcolor: "rgba(0,0,0,0)",
plot_bgcolor: "#f9fafb",
font: {
color: "#374151",
family: "Inter, ui-sans-serif, system-ui, sans-serif",
},
title: { font: { color: "#1f2937" } },
legend: {
bgcolor: "rgba(249,250,251,0.95)",
bordercolor: "#e5e7eb",
font: { color: "#374151" },
},
"xaxis.gridcolor": "#e5e7eb",
"xaxis.linecolor": "#9ca3af",
"xaxis.zerolinecolor": "#d1d5db",
"xaxis.tickfont.color": "#4b5563",
"yaxis.gridcolor": "#e5e7eb",
"yaxis.linecolor": "#9ca3af",
"yaxis.zerolinecolor": "#d1d5db",
"yaxis.tickfont.color": "#4b5563",
});
}
} catch (e) {
/* ignore per-figure errors */
}
}
}

function medperfDashApplyTheme(dark) {
document.documentElement.classList.toggle("dark", !!dark);
medperfPlotlyRelayout();
setTimeout(medperfPlotlyRelayout, 200);
setTimeout(medperfPlotlyRelayout, 800);
setTimeout(medperfPlotlyRelayout, 2000);
}

window.addEventListener("message", function (ev) {
if (!ev.data || ev.data.type !== "medperf-theme") return;
medperfDashApplyTheme(!!ev.data.dark);
});

window.addEventListener("storage", function (ev) {
if (ev.key !== "medperf-dark") return;
medperfDashApplyTheme(ev.newValue === "1");
});

if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", function () {
medperfDashApplyTheme(medperfDashIsDark());
});
} else {
medperfDashApplyTheme(medperfDashIsDark());
}
})();
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,17 @@
import datetime
import numpy as np

from medperf.entities.benchmark import Benchmark
from medperf.entities.dataset import Dataset
from medperf import config

from .utils import get_institution_from_email, get_reports_path, stage_id2name
from .utils import get_institution_from_email, stage_id2name


def get_dsets(mlcube_id):
dsets = Dataset.all(filters={"mlcube": mlcube_id})
def get_dsets(benchmark_id):
bmk = Benchmark.get(benchmark_id)
data_preparator = bmk.data_preparation_mlcube
dsets = Dataset.all(filters={"data_preparator": data_preparator})
dsets = [dset.todict() for dset in dsets]
for dset in dsets:
user_id = dset["owner"]
Expand Down Expand Up @@ -86,16 +89,19 @@ def write_sites(dsets_df, institutions_df, full_path):
f.write("\n".join(sites))


def get_data(mlcube_id, stages_path, institutions_path, out_path):
dsets = get_dsets(mlcube_id)
full_path = get_reports_path(out_path, mlcube_id)
os.makedirs(full_path, exist_ok=True)
def get_data(benchmark_id, stages_path, institutions_path, out_path):
dsets = get_dsets(benchmark_id)
if not dsets:
return

os.makedirs(out_path, exist_ok=True)
Comment thread Fixed
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed

institutions_df = pd.read_csv(institutions_path)
user2institution = {u: i for i, u in institutions_df.values.tolist()}
stages_df = pd.read_csv(stages_path)
stages_df.set_index("Status Code", inplace=True)

dsets_df = build_dset_df(dsets, user2institution, stages_df)
write_dsets_df(dsets_df, full_path)
write_sites(dsets_df, institutions_df, full_path)
write_dsets_df(dsets_df, out_path)
write_sites(dsets_df, institutions_df, out_path)
return True
Loading
Loading