Spaces:
Running on Zero
Running on Zero
Upload app.py
Browse files
app.py
CHANGED
|
@@ -926,6 +926,69 @@ def compute_summary(r1, r2, r3, r4):
|
|
| 926 |
_block(CONC_PRESETS[0][1]), _block(CONC_PRESETS[1][1]))
|
| 927 |
|
| 928 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 929 |
# ---------------------------------------------------------------------------
|
| 930 |
# Training data export — feature extraction per cell
|
| 931 |
# ---------------------------------------------------------------------------
|
|
@@ -1859,6 +1922,26 @@ with gr.Blocks(
|
|
| 1859 |
summary_conc_2x, summary_conc_10x]
|
| 1860 |
)
|
| 1861 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1862 |
|
| 1863 |
|
| 1864 |
if __name__ == "__main__":
|
|
|
|
| 926 |
_block(CONC_PRESETS[0][1]), _block(CONC_PRESETS[1][1]))
|
| 927 |
|
| 928 |
|
| 929 |
+
def export_summary_csv(r1, r2, r3, r4):
|
| 930 |
+
"""One rectangular table: a row per tab plus a Mean row, concentrations
|
| 931 |
+
included on every row. Rectangular rather than sectioned so it loads
|
| 932 |
+
straight into pandas/Excel without hand-editing.
|
| 933 |
+
|
| 934 |
+
Returns (path_or_None, status_message).
|
| 935 |
+
"""
|
| 936 |
+
results = [r1, r2, r3, r4]
|
| 937 |
+
valid = [(i + 1, r) for i, r in enumerate(results)
|
| 938 |
+
if r is not None and r.get("cell_count") is not None]
|
| 939 |
+
if not valid:
|
| 940 |
+
return None, "No data to export — run segmentation in at least one tab first."
|
| 941 |
+
|
| 942 |
+
def _avg(key):
|
| 943 |
+
vals = [r.get(key) for _, r in valid if r.get(key) is not None]
|
| 944 |
+
return (sum(vals) / len(vals)) if vals else 0.0
|
| 945 |
+
|
| 946 |
+
means = {k: _avg(k) for k in
|
| 947 |
+
("cell_count", "live", "dead", "viab_percent", "confluency")}
|
| 948 |
+
|
| 949 |
+
header = ["Tab", "Total cells", "Live cells", "Dead cells",
|
| 950 |
+
"Viability (%)", "Confluency (%)"]
|
| 951 |
+
for name, mult in CONC_PRESETS:
|
| 952 |
+
header += [f"Live conc {name} [x{mult}] (cells/mL)",
|
| 953 |
+
f"Dead conc {name} [x{mult}] (cells/mL)",
|
| 954 |
+
f"Total conc {name} [x{mult}] (cells/mL)"]
|
| 955 |
+
header += ["Tabs averaged", "Note"]
|
| 956 |
+
|
| 957 |
+
def _row(label, total, live, dead, viab, conf, n_avg="", note=""):
|
| 958 |
+
row = [label,
|
| 959 |
+
f"{total:.0f}" if label != "Mean" else f"{total:.2f}",
|
| 960 |
+
f"{live:.0f}" if label != "Mean" else f"{live:.2f}",
|
| 961 |
+
f"{dead:.0f}" if label != "Mean" else f"{dead:.2f}",
|
| 962 |
+
f"{viab:.1f}", f"{conf:.1f}"]
|
| 963 |
+
for _, mult in CONC_PRESETS:
|
| 964 |
+
row += [f"{live * mult:.0f}", f"{dead * mult:.0f}", f"{total * mult:.0f}"]
|
| 965 |
+
row += [n_avg, note]
|
| 966 |
+
return row
|
| 967 |
+
|
| 968 |
+
rows = [_row(str(tab),
|
| 969 |
+
r.get("cell_count") or 0.0, r.get("live") or 0.0,
|
| 970 |
+
r.get("dead") or 0.0, r.get("viab_percent") or 0.0,
|
| 971 |
+
r.get("confluency") or 0.0)
|
| 972 |
+
for tab, r in valid]
|
| 973 |
+
|
| 974 |
+
n = len(valid)
|
| 975 |
+
note = ("" if n == 4 else
|
| 976 |
+
"Fewer than 4 squares averaged - not the standard hemocytometer convention")
|
| 977 |
+
rows.append(_row("Mean", means["cell_count"], means["live"], means["dead"],
|
| 978 |
+
means["viab_percent"], means["confluency"], str(n), note))
|
| 979 |
+
|
| 980 |
+
tmp = tempfile.NamedTemporaryFile(mode="w", suffix=".csv", delete=False, newline="")
|
| 981 |
+
w = csv.writer(tmp)
|
| 982 |
+
w.writerow(header)
|
| 983 |
+
w.writerows(rows)
|
| 984 |
+
tmp.close()
|
| 985 |
+
|
| 986 |
+
msg = f"Exported {n} tab{'s' if n > 1 else ''} + mean row."
|
| 987 |
+
if note:
|
| 988 |
+
msg += " ⚠ " + note
|
| 989 |
+
return tmp.name, msg
|
| 990 |
+
|
| 991 |
+
|
| 992 |
# ---------------------------------------------------------------------------
|
| 993 |
# Training data export — feature extraction per cell
|
| 994 |
# ---------------------------------------------------------------------------
|
|
|
|
| 1922 |
summary_conc_2x, summary_conc_10x]
|
| 1923 |
)
|
| 1924 |
|
| 1925 |
+
gr.Markdown("### Export")
|
| 1926 |
+
with gr.Row():
|
| 1927 |
+
summary_export_btn = gr.Button("⬇️ Export summary CSV",
|
| 1928 |
+
variant="secondary")
|
| 1929 |
+
summary_export_info = gr.Textbox(label="Export status", lines=2,
|
| 1930 |
+
interactive=False)
|
| 1931 |
+
summary_export_file = gr.File(label="Download summary CSV", visible=False)
|
| 1932 |
+
|
| 1933 |
+
def on_export_summary(*tab_results):
|
| 1934 |
+
path, msg = export_summary_csv(*tab_results)
|
| 1935 |
+
if path is None:
|
| 1936 |
+
return gr.update(visible=False), msg
|
| 1937 |
+
return gr.update(value=path, visible=True), msg
|
| 1938 |
+
|
| 1939 |
+
summary_export_btn.click(
|
| 1940 |
+
fn=on_export_summary,
|
| 1941 |
+
inputs=result_states,
|
| 1942 |
+
outputs=[summary_export_file, summary_export_info]
|
| 1943 |
+
)
|
| 1944 |
+
|
| 1945 |
|
| 1946 |
|
| 1947 |
if __name__ == "__main__":
|