TEZv commited on
Commit
ae71706
·
verified ·
1 Parent(s): b617193

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +145 -95
app.py CHANGED
@@ -11,7 +11,7 @@ from datetime import datetime
11
  from pathlib import Path
12
  import plotly.graph_objects as go
13
  import plotly.express as px
14
- import tabulate # для коректної роботи to_markdown()
15
 
16
  # ========== Діагностичний друк ==========
17
  print("Gradio version:", gr.__version__)
@@ -43,22 +43,27 @@ def log_entry(tab, inputs, result, note=""):
43
  except Exception as e:
44
  print(f"Log error: {e}")
45
 
46
- def load_journal():
 
47
  try:
48
  if not LOG_PATH.exists() or LOG_PATH.stat().st_size == 0:
49
  return "No entries yet."
50
  df = pd.read_csv(LOG_PATH)
51
  if df.empty:
52
  return "No entries yet."
53
- return df.tail(20).to_markdown(index=False)
 
 
 
 
54
  except Exception as e:
55
  print(f"Journal load error: {e}")
56
  return "Error loading journal."
57
 
58
- def save_note(note, tab):
59
  if note.strip():
60
- log_entry(tab, "manual note", note, note)
61
- return load_journal()
62
 
63
  def clear_journal():
64
  try:
@@ -169,6 +174,17 @@ PROTEINS = ["albumin","apolipoprotein","fibrinogen","vitronectin",
169
  "clusterin","igm","iga","igg","complement","transferrin",
170
  "alpha-2-macroglobulin"]
171
 
 
 
 
 
 
 
 
 
 
 
 
172
  # ---------- S1-F RARE ----------
173
  DIPG_VARIANTS = [
174
  {"Variant":"H3K27M (H3F3A)","Freq_pct":78,"Pathway":"PRC2 inhibition → global H3K27me3 loss","Drug_status":"ONC201 (clinical)","Circadian_gene":"BMAL1 suppressed"},
@@ -229,20 +245,20 @@ def safe_img_from_fig(fig):
229
 
230
  def predict_mirna(gene):
231
  df = pd.DataFrame(MIRNA_DB.get(gene, []))
232
- log_entry("S1-B · R1a · miRNA", gene, f"{len(df)} miRNAs")
233
  return df
234
 
235
  def predict_sirna(cancer):
236
  df = pd.DataFrame(SIRNA_DB.get(cancer, []))
237
- log_entry("S1-B · R2a · siRNA", cancer, f"{len(df)} targets")
238
  return df
239
 
240
  def get_lncrna():
241
- log_entry("S1-B · R3a · lncRNA", "load", "ceRNA")
242
  return pd.DataFrame(CERNA)
243
 
244
  def get_aso():
245
- log_entry("S1-B · R3b · ASO", "load", "ASO")
246
  return pd.DataFrame(ASO)
247
 
248
  def predict_drug(pocket):
@@ -259,10 +275,10 @@ def predict_drug(pocket):
259
  ax.set_title(f"Top compounds — {pocket}", color=TXT, fontsize=10)
260
  plt.tight_layout()
261
  img = safe_img_from_fig(fig)
262
- log_entry("S1-C · R1a · FGFR3", pocket, f"Top: {df.iloc[0]['Compound'] if len(df) else 'none'}")
263
  return df, img
264
  except Exception as e:
265
- log_entry("S1-C · R1a · FGFR3", pocket, f"Error: {str(e)}")
266
  return pd.DataFrame(), None
267
 
268
  def predict_variant(hgvs, sift, polyphen, gnomad):
@@ -279,10 +295,10 @@ def predict_variant(hgvs, sift, polyphen, gnomad):
279
  conf = "High" if (sift < 0.01 or sift > 0.9) else "Moderate"
280
  colour = RED if "Pathogenic" in cls else GRN
281
  icon = "⚠️ WARNING" if "Pathogenic" in cls else "✅ OK"
282
- log_entry("S1-A · R1a · OpenVariant", hgvs or f"SIFT={sift}", f"{cls} score={score}")
283
  return (
284
  f"<div style=\'background:{CARD};padding:16px;border-radius:8px;font-family:sans-serif;color:{TXT}\'>"
285
- f"<p style=\'font-size:11px;color:{DIM};margin:0 0 8px\'>S1-A · R1a · OpenVariant</p>"
286
  f"<h3 style=\'color:{colour};margin:0 0 8px\'>{icon} {cls}</h3>"
287
  f"<p>Score: <b>{score:.3f}</b> &nbsp;|&nbsp; Confidence: <b>{conf}</b></p>"
288
  f"<div style=\'background:{BORDER};border-radius:4px;height:14px\'>"
@@ -301,7 +317,7 @@ def predict_corona(size, zeta, peg, lipid):
301
  if size < 100: score += 1
302
  dominant = ["ApoE","Albumin","Fibrinogen","Vitronectin","ApoA-I"][min(score, 4)]
303
  efficacy = "High" if score >= 4 else "Medium" if score >= 2 else "Low"
304
- log_entry("S1-D · R1a · Corona", f"size={size},peg={peg}", f"dominant={dominant}")
305
  return f"**Dominant corona protein:** {dominant}\n\n**Predicted efficacy:** {efficacy}\n\n**Score:** {score}/6"
306
  except Exception as e:
307
  return f"Error: {str(e)}"
@@ -325,10 +341,10 @@ def predict_cancer(c1,c2,c3,c4,c5,c6,c7,c8,c9,c10):
325
  ax.set_title("Protein contributions", color=TXT, fontsize=10)
326
  plt.tight_layout()
327
  img = safe_img_from_fig(fig)
328
- log_entry("S1-E · R1a · Liquid Biopsy", f"CTHRC1={c1},FHL2={c2}", f"{label} {prob:.2f}")
329
  html_out = (
330
  f"<div style=\'background:{CARD};padding:14px;border-radius:8px;font-family:sans-serif;\'>"
331
- f"<p style=\'font-size:11px;color:{DIM};margin:0 0 6px\'>S1-E · R1a · Liquid Biopsy</p>"
332
  f"<span style=\'color:{colour};font-size:24px;font-weight:bold\'>{label}</span><br>"
333
  f"<span style=\'color:{TXT};font-size:14px\'>Probability: {prob:.2f}</span></div>"
334
  )
@@ -354,7 +370,7 @@ def predict_flow(size, zeta, peg, charge, flow_rate):
354
  ax.set_title("Vroman Effect — flow vs static", color=TXT, fontsize=9)
355
  plt.tight_layout()
356
  img = safe_img_from_fig(fig)
357
- log_entry("S1-D · R2a · Flow Corona", f"flow={flow_rate}", f"CSI={csi}")
358
  return f"**Corona Shift Index: {csi}** — {stability}", img
359
  except Exception as e:
360
  return f"Error: {str(e)}", None
@@ -376,7 +392,7 @@ def predict_bbb(smiles, pka, zeta):
376
  ax.tick_params(colors=TXT)
377
  plt.tight_layout()
378
  img = safe_img_from_fig(fig)
379
- log_entry("S1-D · R3a · LNP Brain", f"pka={pka},zeta={zeta}", f"ApoE={apoe_pct:.1f}%")
380
  return f"**Predicted ApoE:** {apoe_pct:.1f}% — {tier}\n\n**BBB Probability:** {bbb_prob:.2f}", img
381
  except Exception as e:
382
  return f"Error: {str(e)}", None
@@ -402,7 +418,7 @@ def extract_corona(text):
402
  if not out["zeta_mv"]: flags.append("zeta_mv not found")
403
  if not out["corona_proteins"]: flags.append("no proteins detected")
404
  summary = "All key fields extracted" if not flags else " | ".join(flags)
405
- log_entry("S1-D · R4a · AutoCorona NLP", text[:80], f"proteins={len(out['corona_proteins'])}")
406
  return json.dumps(out, indent=2), summary
407
  except Exception as e:
408
  return json.dumps({"error": str(e)}), "Extraction error"
@@ -410,7 +426,7 @@ def extract_corona(text):
410
  def dipg_variants(sort_by):
411
  df = pd.DataFrame(DIPG_VARIANTS).sort_values(
412
  "Freq_pct" if sort_by == "Frequency" else "Drug_status", ascending=False)
413
- log_entry("S1-F · R1a · DIPG", sort_by, f"{len(df)} variants")
414
  return df
415
 
416
  def dipg_csf(peg, size):
@@ -428,14 +444,14 @@ def dipg_csf(peg, size):
428
  ax.set_title("DIPG — CSF LNP formulations (ApoE%)", color=TXT, fontsize=9)
429
  plt.tight_layout()
430
  img = safe_img_from_fig(fig)
431
- log_entry("S1-F · R1a · DIPG CSF", f"peg={peg},size={size}", "formulation ranking")
432
  return df[["Formulation","Size_nm","Zeta_mV","ApoE_pct","BBB_est","Priority"]], img
433
  except Exception as e:
434
  return pd.DataFrame(), None
435
 
436
  def uvm_variants():
437
  df = pd.DataFrame(UVM_VARIANTS)
438
- log_entry("S1-F · R2a · UVM", "load", f"{len(df)} variants")
439
  return df
440
 
441
  def uvm_vitreous():
@@ -451,7 +467,7 @@ def uvm_vitreous():
451
  ax.set_title("UVM — LNP retention in vitreous humor", color=TXT, fontsize=9)
452
  plt.tight_layout()
453
  img = safe_img_from_fig(fig)
454
- log_entry("S1-F · R2a · UVM Vitreous", "load", "vitreous LNP ranking")
455
  return df, img
456
  except Exception as e:
457
  return pd.DataFrame(), None
@@ -475,7 +491,7 @@ def paml_ferroptosis(variant):
475
  ax.set_title(f"pAML · {row['Variant'][:20]}", color=TXT, fontsize=9)
476
  plt.tight_layout()
477
  img = safe_img_from_fig(fig)
478
- log_entry("S1-F · R3a · pAML", variant, f"ferr={ferr_score:.2f}")
479
  _v = row["Variant"]
480
  _p = row["Pathway"]
481
  _d = row["Drug_status"]
@@ -483,7 +499,7 @@ def paml_ferroptosis(variant):
483
  _fs = f"{ferr_score:.2f}"
484
  summary = (
485
  f"<div style='background:{CARD};padding:14px;border-radius:8px;font-family:sans-serif;'>"
486
- f"<p style='color:{DIM};font-size:11px;margin:0 0 6px'>S1-F · R3a · pAML</p>"
487
  f"<b style='color:{ACC2};font-size:15px'>{_v}</b><br>"
488
  f"<p style='color:{TXT};margin:6px 0'><b>Pathway:</b> {_p}</p>"
489
  f"<p style='color:{TXT};margin:0'><b>Drug:</b> {_d}</p>"
@@ -597,7 +613,12 @@ def plot_corona():
597
  css = f"""
598
  body, .gradio-container {{ background: {BG} !important; color: {TXT} !important; }}
599
 
600
- /* Вкладки верхнього рівня (категорії) */
 
 
 
 
 
601
  .tabs-outer .tab-nav button {{
602
  color: {TXT} !important;
603
  background: {CARD} !important;
@@ -605,6 +626,11 @@ body, .gradio-container {{ background: {BG} !important; color: {TXT} !important;
605
  font-weight: 600 !important;
606
  padding: 8px 16px !important;
607
  border-radius: 6px 6px 0 0 !important;
 
 
 
 
 
608
  }}
609
  .tabs-outer .tab-nav button.selected {{
610
  border-bottom: 3px solid {ACC} !important;
@@ -676,14 +702,14 @@ body, .gradio-container {{ background: {BG} !important; color: {TXT} !important;
676
  margin-left: 6px;
677
  }}
678
 
679
- /* Журнал */
680
- .journal {{
681
  background: {CARD};
682
  border: 1px solid {BORDER};
683
  border-radius: 8px;
684
  padding: 14px;
685
  }}
686
- .journal h3 {{
687
  color: {ACC};
688
  margin-top: 0;
689
  }}
@@ -701,30 +727,30 @@ MAP_HTML = f"""
701
  <span style="color:{DIM};font-size:11px;margin-left:12px">Science Sphere — sub-direction 1</span>
702
  <br><br>
703
  <span style="color:{ACC2};font-weight:600">S1-A · PHYLO-GENOMICS</span> — What breaks in DNA<br>
704
- &nbsp;&nbsp;&nbsp;├─ <b>S1-A · R1a</b> OpenVariant <span style="color:{GRN}"> AUC=0.939 ✅</span><br>
705
- &nbsp;&nbsp;&nbsp;└─ <b>S1-A · R1b</b> Somatic classifier <span style="color:#f59e0b"> 🔶 In progress</span><br><br>
706
  <span style="color:{ACC2};font-weight:600">S1-B · PHYLO-RNA</span> — How to silence it via RNA<br>
707
- &nbsp;&nbsp;&nbsp;├─ <b>S1-B · R1a</b> miRNA silencing <span style="color:{GRN}"> ✅</span><br>
708
- &nbsp;&nbsp;&nbsp;├─ <b>S1-B · R2a</b> siRNA synthetic lethal <span style="color:{GRN}"> ✅</span><br>
709
- &nbsp;&nbsp;&nbsp;├─ <b>S1-B · R3a</b> lncRNA-TREM2 ceRNA <span style="color:{GRN}"> ✅</span><br>
710
- &nbsp;&nbsp;&nbsp;└─ <b>S1-B · R3b</b> ASO designer <span style="color:{GRN}"> ✅</span><br><br>
711
  <span style="color:{ACC2};font-weight:600">S1-C · PHYLO-DRUG</span> — Which molecule treats it<br>
712
- &nbsp;&nbsp;&nbsp;├─ <b>S1-C · R1a</b> FGFR3 RNA-directed compounds <span style="color:{GRN}"> ✅</span><br>
713
- &nbsp;&nbsp;&nbsp;├─ <b>S1-C · R1b</b> Synthetic lethal drug mapping <span style="color:#f59e0b"> 🔶</span><br>
714
- &nbsp;&nbsp;&nbsp;└─ <b>S1-C · R2a</b> m6A × Ferroptosis × Circadian <span style="color:{DIM}"> 🔴 Frontier</span><br><br>
715
  <span style="color:{ACC2};font-weight:600">S1-D · PHYLO-LNP</span> — How to deliver the drug<br>
716
- &nbsp;&nbsp;&nbsp;├─ <b>S1-D · R1a</b> LNP corona (serum) <span style="color:{GRN}"> AUC=0.791 ✅</span><br>
717
- &nbsp;&nbsp;&nbsp;├─ <b>S1-D · R2a</b> Flow corona — Vroman effect <span style="color:{GRN}"> ✅</span><br>
718
- &nbsp;&nbsp;&nbsp;├─ <b>S1-D · R3a</b> LNP brain / BBB / ApoE <span style="color:{GRN}"> ✅</span><br>
719
- &nbsp;&nbsp;&nbsp;├─ <b>S1-D · R4a</b> AutoCorona NLP <span style="color:{GRN}"> F1=0.71 ✅</span><br>
720
- &nbsp;&nbsp;&nbsp;└─ <b>S1-D · R5a</b> CSF · Vitreous · Bone Marrow <span style="color:{DIM}"> 🔴 0 prior studies</span><br><br>
721
  <span style="color:{ACC2};font-weight:600">S1-E · PHYLO-BIOMARKERS</span> — Detect without biopsy<br>
722
- &nbsp;&nbsp;&nbsp;├─ <b>S1-E · R1a</b> Liquid Biopsy classifier <span style="color:{GRN}"> AUC=0.992* ✅</span><br>
723
- &nbsp;&nbsp;&nbsp;└─ <b>S1-E · R1b</b> Protein panel validator <span style="color:#f59e0b"> 🔶</span><br><br>
724
  <span style="color:{ACC2};font-weight:600">S1-F · PHYLO-RARE</span> — Where almost nobody has looked yet<br>
725
- &nbsp;&nbsp;&nbsp;├─ <b>S1-F · R1a</b> DIPG toolkit (H3K27M + CSF LNP + Circadian) <span style="color:#f59e0b"> 🔶</span><br>
726
- &nbsp;&nbsp;&nbsp;├─ <b>S1-F · R2a</b> UVM toolkit (GNAQ/GNA11 + vitreous + m6A) <span style="color:#f59e0b"> 🔶</span><br>
727
- &nbsp;&nbsp;&nbsp;└─ <b>S1-F · R3a</b> pAML toolkit (FLT3-ITD + BM niche + ferroptosis) <span style="color:#f59e0b"> 🔶</span><br><br>
728
  <span style="color:{ACC2};font-weight:600">S1-G · PHYLO-SIM</span> — 3D Models & Simulations<br>
729
  &nbsp;&nbsp;&nbsp;├─ <b>Nanoparticle</b> Interactive 3D model <span style="color:{GRN}"> ✅</span><br>
730
  &nbsp;&nbsp;&nbsp;├─ <b>DNA Helix</b> Double helix visualization <span style="color:{GRN}"> ✅</span><br>
@@ -750,7 +776,7 @@ with gr.Blocks(css=css, title="K R&D Lab · S1 Biomedical") as demo:
750
  gr.HTML(MAP_HTML)
751
 
752
  # === S1-A · PHYLO-GENOMICS ===
753
- with gr.TabItem("🧬 S1-A · PHYLO-GENOMICS"):
754
  gr.HTML(section_header(
755
  "S1-A", "PHYLO-GENOMICS", "— What breaks in DNA",
756
  "R1a OpenVariant ✅ · R1b Somatic classifier 🔶"
@@ -761,7 +787,7 @@ with gr.Blocks(css=css, title="K R&D Lab · S1 Biomedical") as demo:
761
  with gr.Tabs(elem_classes="sub-tabs"):
762
  # R1a · OpenVariant
763
  with gr.TabItem("R1a · OpenVariant"):
764
- gr.HTML(proj_badge("S1-A · R1a", "OpenVariant — SNV Pathogenicity Classifier", "AUC=0.939"))
765
  hgvs = gr.Textbox(label="HGVS notation", placeholder="BRCA1:p.R1699Q")
766
  gr.Markdown("**Or enter functional scores manually:**")
767
  with gr.Row():
@@ -776,11 +802,11 @@ with gr.Blocks(css=css, title="K R&D Lab · S1 Biomedical") as demo:
776
  b_v.click(predict_variant, [hgvs,sift,pp,gn], o_v)
777
  # R1b · Somatic Classifier (в розробці)
778
  with gr.TabItem("R1b · Somatic Classifier 🔶"):
779
- gr.HTML(proj_badge("S1-A · R1b", "Somatic Mutation Classifier", "🔶 In progress"))
780
  gr.Markdown("> This module is in active development. Coming in the next release.")
781
 
782
  # === S1-B · PHYLO-RNA ===
783
- with gr.TabItem("🔬 S1-B · PHYLO-RNA"):
784
  gr.HTML(section_header(
785
  "S1-B", "PHYLO-RNA", "— How to silence it via RNA",
786
  "R1a miRNA ✅ · R2a siRNA ✅ · R3a lncRNA ✅ · R3b ASO ✅"
@@ -790,7 +816,7 @@ with gr.Blocks(css=css, title="K R&D Lab · S1 Biomedical") as demo:
790
  with gr.TabItem("R1 · miRNA silencing"):
791
  with gr.Tabs(elem_classes="sub-tabs"):
792
  with gr.TabItem("R1a · BRCA2 miRNA"):
793
- gr.HTML(proj_badge("S1-B · R1a", "miRNA Silencing — BRCA1/2 · TP53"))
794
  g1 = gr.Dropdown(["BRCA2","BRCA1","TP53"], value="BRCA2", label="Gene")
795
  b1 = gr.Button("Find miRNAs", variant="primary")
796
  o1 = gr.Dataframe(label="Top 5 downregulated miRNAs")
@@ -800,7 +826,7 @@ with gr.Blocks(css=css, title="K R&D Lab · S1 Biomedical") as demo:
800
  with gr.TabItem("R2 · siRNA SL"):
801
  with gr.Tabs(elem_classes="sub-tabs"):
802
  with gr.TabItem("R2a · TP53 siRNA"):
803
- gr.HTML(proj_badge("S1-B · R2a", "siRNA Synthetic Lethal — TP53-null"))
804
  g2 = gr.Dropdown(["LUAD","BRCA","COAD"], value="LUAD", label="Cancer type")
805
  b2 = gr.Button("Find Targets", variant="primary")
806
  o2 = gr.Dataframe(label="Top 5 synthetic lethal targets")
@@ -810,18 +836,18 @@ with gr.Blocks(css=css, title="K R&D Lab · S1 Biomedical") as demo:
810
  with gr.TabItem("R3 · lncRNA + ASO"):
811
  with gr.Tabs(elem_classes="sub-tabs"):
812
  with gr.TabItem("R3a · lncRNA-TREM2"):
813
- gr.HTML(proj_badge("S1-B · R3a", "lncRNA-TREM2 ceRNA Network"))
814
  b3a = gr.Button("Load ceRNA", variant="primary")
815
  o3a = gr.Dataframe(label="ceRNA Network (R3a)")
816
  b3a.click(lambda: pd.DataFrame(CERNA), [], o3a)
817
  with gr.TabItem("R3b · ASO Designer"):
818
- gr.HTML(proj_badge("S1-B · R3b", "ASO Designer"))
819
  b3b = gr.Button("Load ASO Candidates", variant="primary")
820
  o3b = gr.Dataframe(label="ASO Candidates (R3b)")
821
  b3b.click(lambda: pd.DataFrame(ASO), [], o3b)
822
 
823
  # === S1-C · PHYLO-DRUG ===
824
- with gr.TabItem("💊 S1-C · PHYLO-DRUG"):
825
  gr.HTML(section_header(
826
  "S1-C", "PHYLO-DRUG", "— Which molecule treats it",
827
  "R1a FGFR3 ✅ · R1b SL drug mapping 🔶 · R2a Frontier 🔴⭐"
@@ -831,7 +857,7 @@ with gr.Blocks(css=css, title="K R&D Lab · S1 Biomedical") as demo:
831
  with gr.TabItem("R1 · RNA-directed drug"):
832
  with gr.Tabs(elem_classes="sub-tabs"):
833
  with gr.TabItem("R1a · FGFR3 RNA Drug"):
834
- gr.HTML(proj_badge("S1-C · R1a", "FGFR3 RNA-Directed Drug Discovery", "top score 0.793"))
835
  g4 = gr.Radio(["P1 (hairpin loop)","P10 (G-quadruplex)"],
836
  value="P1 (hairpin loop)", label="Target pocket")
837
  b4_drug = gr.Button("Screen Compounds", variant="primary")
@@ -840,13 +866,13 @@ with gr.Blocks(css=css, title="K R&D Lab · S1 Biomedical") as demo:
840
  gr.Examples([["P1 (hairpin loop)"],["P10 (G-quadruplex)"]], inputs=[g4])
841
  b4_drug.click(predict_drug, [g4], [o4t, o4p])
842
  with gr.TabItem("R1b · SL Drug Mapping 🔶"):
843
- gr.HTML(proj_badge("S1-C · R1b", "Synthetic Lethal Drug Mapping", "🔶 In progress"))
844
  gr.Markdown("> In development. Coming soon.")
845
  # R2 · Frontier
846
  with gr.TabItem("R2 · Frontier"):
847
  with gr.Tabs(elem_classes="sub-tabs"):
848
  with gr.TabItem("R2a · m6A×Ferroptosis×Circadian 🔴⭐"):
849
- gr.HTML(proj_badge("S1-C · R2a", "m6A × Ferroptosis × Circadian", "🔴 Frontier"))
850
  gr.Markdown(
851
  "> **Research gap:** This triple intersection has never been studied as an integrated system.\n\n"
852
  "> **Planned datasets:** TCGA-PAAD · GEO m6A atlases · Circadian gene panels\n\n"
@@ -854,7 +880,7 @@ with gr.Blocks(css=css, title="K R&D Lab · S1 Biomedical") as demo:
854
  )
855
 
856
  # === S1-D · PHYLO-LNP ===
857
- with gr.TabItem("🧪 S1-D · PHYLO-LNP"):
858
  gr.HTML(section_header(
859
  "S1-D", "PHYLO-LNP", "— How to deliver the drug",
860
  "R1a Corona ✅ · R2a Flow ✅ · R3a Brain ✅ · R4a NLP ✅ · R5a CSF/BM 🔴⭐"
@@ -864,7 +890,7 @@ with gr.Blocks(css=css, title="K R&D Lab · S1 Biomedical") as demo:
864
  with gr.TabItem("R1 · Serum corona"):
865
  with gr.Tabs(elem_classes="sub-tabs"):
866
  with gr.TabItem("R1a · LNP Corona ML"):
867
- gr.HTML(proj_badge("S1-D · R1a", "LNP Protein Corona (Serum)", "AUC=0.791"))
868
  with gr.Row():
869
  sz = gr.Slider(50,300,value=100,step=1,label="Size (nm)")
870
  zt = gr.Slider(-40,10,value=-5,step=1,label="Zeta (mV)")
@@ -879,7 +905,7 @@ with gr.Blocks(css=css, title="K R&D Lab · S1 Biomedical") as demo:
879
  with gr.TabItem("R2 · Flow corona"):
880
  with gr.Tabs(elem_classes="sub-tabs"):
881
  with gr.TabItem("R2a · Flow Corona"):
882
- gr.HTML(proj_badge("S1-D · R2a", "Flow Corona — Vroman Effect"))
883
  with gr.Row():
884
  s8 = gr.Slider(50,300,value=100,step=1,label="Size (nm)")
885
  z8 = gr.Slider(-40,10,value=-5,step=1,label="Zeta (mV)")
@@ -896,7 +922,7 @@ with gr.Blocks(css=css, title="K R&D Lab · S1 Biomedical") as demo:
896
  with gr.TabItem("R3 · Brain BBB"):
897
  with gr.Tabs(elem_classes="sub-tabs"):
898
  with gr.TabItem("R3a · LNP Brain"):
899
- gr.HTML(proj_badge("S1-D · R3a", "LNP Brain Delivery"))
900
  smi = gr.Textbox(label="Ionizable lipid SMILES",
901
  value="CC(C)CC(=O)OCC(COC(=O)CC(C)C)OC(=O)CC(C)C")
902
  with gr.Row():
@@ -911,7 +937,7 @@ with gr.Blocks(css=css, title="K R&D Lab · S1 Biomedical") as demo:
911
  with gr.TabItem("R4 · NLP"):
912
  with gr.Tabs(elem_classes="sub-tabs"):
913
  with gr.TabItem("R4a · AutoCorona NLP"):
914
- gr.HTML(proj_badge("S1-D · R4a", "AutoCorona NLP", "F1=0.71"))
915
  txt = gr.Textbox(lines=5,label="Paper abstract",placeholder="Paste abstract here...")
916
  b10 = gr.Button("Extract Data", variant="primary")
917
  o10j = gr.Code(label="Extracted JSON", language="json")
@@ -926,7 +952,7 @@ with gr.Blocks(css=css, title="K R&D Lab · S1 Biomedical") as demo:
926
  with gr.TabItem("R5 · Exotic fluids 🔴⭐"):
927
  with gr.Tabs(elem_classes="sub-tabs"):
928
  with gr.TabItem("R5a · CSF/Vitreous/BM"):
929
- gr.HTML(proj_badge("S1-D · R5a", "LNP Corona in CSF · Vitreous · Bone Marrow", "🔴 0 prior studies"))
930
  gr.Markdown(
931
  "> **Research gap:** Protein corona has only been characterized in serum/plasma. "
932
  "CSF, vitreous humor, and bone marrow interstitial fluid remain completely unstudied.\n\n"
@@ -935,7 +961,7 @@ with gr.Blocks(css=css, title="K R&D Lab · S1 Biomedical") as demo:
935
  )
936
 
937
  # === S1-E · PHYLO-BIOMARKERS ===
938
- with gr.TabItem("🩸 S1-E · PHYLO-BIOMARKERS"):
939
  gr.HTML(section_header(
940
  "S1-E", "PHYLO-BIOMARKERS", "— Detect without biopsy",
941
  "R1a Liquid Biopsy ✅ · R1b Protein validator 🔶"
@@ -944,7 +970,7 @@ with gr.Blocks(css=css, title="K R&D Lab · S1 Biomedical") as demo:
944
  with gr.TabItem("R1 · Liquid biopsy"):
945
  with gr.Tabs(elem_classes="sub-tabs"):
946
  with gr.TabItem("R1a · Liquid Biopsy"):
947
- gr.HTML(proj_badge("S1-E · R1a", "Liquid Biopsy Classifier", "AUC=0.992*"))
948
  with gr.Row():
949
  p1=gr.Slider(-3,3,value=0,step=0.1,label="CTHRC1")
950
  p2=gr.Slider(-3,3,value=0,step=0.1,label="FHL2")
@@ -964,11 +990,11 @@ with gr.Blocks(css=css, title="K R&D Lab · S1 Biomedical") as demo:
964
  inputs=[p1,p2,p3,p4,p5,p6,p7,p8,p9,p10])
965
  b7.click(predict_cancer, [p1,p2,p3,p4,p5,p6,p7,p8,p9,p10], [o7t,o7p])
966
  with gr.TabItem("R1b · Protein Validator 🔶"):
967
- gr.HTML(proj_badge("S1-E · R1b", "Protein Panel Validator", "🔶 In progress"))
968
  gr.Markdown("> Coming next — validates R1a results against GEO plasma proteomics datasets.")
969
 
970
  # === S1-F · PHYLO-RARE ===
971
- with gr.TabItem("🧠 S1-F · PHYLO-RARE"):
972
  gr.HTML(section_header(
973
  "S1-F", "PHYLO-RARE", "— Where almost nobody has looked yet",
974
  "<b style='color:#ef4444'>⚠️ <300 cases/yr · <5% survival · 0–1 prior studies per gap</b><br>"
@@ -979,7 +1005,7 @@ with gr.Blocks(css=css, title="K R&D Lab · S1 Biomedical") as demo:
979
  with gr.TabItem("R1 · DIPG"):
980
  with gr.Tabs(elem_classes="sub-tabs"):
981
  with gr.TabItem("R1a · DIPG Toolkit"):
982
- gr.HTML(proj_badge("S1-F · R1a", "DIPG Toolkit", "PBTA · GSE126319"))
983
  gr.Markdown(
984
  "> **Why DIPG?** Diffuse Intrinsic Pontine Glioma — median survival 9–11 months. "
985
  "H3K27M oncohistone in **78%** cases. "
@@ -1013,7 +1039,7 @@ with gr.Blocks(css=css, title="K R&D Lab · S1 Biomedical") as demo:
1013
  with gr.TabItem("R2 · UVM"):
1014
  with gr.Tabs(elem_classes="sub-tabs"):
1015
  with gr.TabItem("R2a · UVM Toolkit"):
1016
- gr.HTML(proj_badge("S1-F · R2a", "UVM Toolkit", "TCGA-UVM n=80"))
1017
  gr.Markdown(
1018
  "> **Why UVM?** Uveal Melanoma — metastatic 5-yr survival **15%**. "
1019
  "GNAQ/GNA11 mutations in 78% cases. "
@@ -1043,7 +1069,7 @@ with gr.Blocks(css=css, title="K R&D Lab · S1 Biomedical") as demo:
1043
  with gr.TabItem("R3 · pAML"):
1044
  with gr.Tabs(elem_classes="sub-tabs"):
1045
  with gr.TabItem("R3a · pAML Toolkit"):
1046
- gr.HTML(proj_badge("S1-F · R3a", "pAML Toolkit", "TARGET-AML n≈197"))
1047
  gr.Markdown(
1048
  "> **Why pAML?** Pediatric AML — relapse OS **<30%**. "
1049
  "FLT3-ITD in 25% cases. "
@@ -1077,7 +1103,7 @@ with gr.Blocks(css=css, title="K R&D Lab · S1 Biomedical") as demo:
1077
  )
1078
 
1079
  # === S1-G · 3D Lab ===
1080
- with gr.TabItem("🧊 S1-G · 3D Lab"):
1081
  gr.HTML(section_header(
1082
  "S1-G", "PHYLO-SIM", "— 3D Models & Simulations",
1083
  "Interactive visualizations for learning"
@@ -1110,29 +1136,53 @@ with gr.Blocks(css=css, title="K R&D Lab · S1 Biomedical") as demo:
1110
  ## 🧪 Guided Investigations
1111
  > 🟢 Beginner → 🟡 Intermediate → 🔴 Advanced
1112
 
1113
- **S1-A · R1a** OpenVariant – try BRCA1:p.R1699Q vs R1699W
1114
- **S1-D · R1a** Corona – vary PEG% and observe dominant protein
1115
- **S1-D · R2a** Flow Corona – compare flow 0 vs 40 cm/s
1116
- **S1-B · R2a** siRNA – count "Novel" targets across cancer types
1117
- **S1-E · R1a** Liquid Biopsy – find minimal signal for CANCER
1118
- **S1-G · 3D Lab** – explore nanoparticle, DNA, and protein corona models
1119
  """)
1120
 
1121
- # Права колонка з журналом
1122
- with gr.Column(scale=1, min_width=300):
1123
- with gr.Group(elem_classes="journal"):
1124
- gr.Markdown("## 📓 Lab Journal")
1125
- note_input = gr.Textbox(label="📝 Observation", placeholder="Your observation...", lines=2)
1126
- note_tab = gr.Textbox(label="Project code (e.g. S1-A·R1a)", value="General", visible=False)
1127
- with gr.Row():
1128
- save_btn = gr.Button("💾 Save", size="sm", variant="primary")
1129
- refresh_btn = gr.Button("🔄 Refresh", size="sm")
1130
- clear_btn = gr.Button("🗑️ Clear", size="sm")
1131
- journal_display = gr.Markdown(value=load_journal())
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1132
 
1133
- save_btn.click(save_note, [note_input, note_tab], journal_display)
1134
- refresh_btn.click(load_journal, [], journal_display)
1135
- clear_btn.click(clear_journal, [], journal_display)
1136
 
1137
  gr.Markdown(
1138
  "---\n**K R&D Lab** · MIT License · "
 
11
  from pathlib import Path
12
  import plotly.graph_objects as go
13
  import plotly.express as px
14
+ import tabulate
15
 
16
  # ========== Діагностичний друк ==========
17
  print("Gradio version:", gr.__version__)
 
43
  except Exception as e:
44
  print(f"Log error: {e}")
45
 
46
+ def load_journal(category=None):
47
+ """Load journal entries, optionally filtered by category. Returns markdown string."""
48
  try:
49
  if not LOG_PATH.exists() or LOG_PATH.stat().st_size == 0:
50
  return "No entries yet."
51
  df = pd.read_csv(LOG_PATH)
52
  if df.empty:
53
  return "No entries yet."
54
+ if category and category != "All":
55
+ df = df[df["tab"] == category]
56
+ if df.empty:
57
+ return "No entries for this category."
58
+ return df.tail(50).to_markdown(index=False)
59
  except Exception as e:
60
  print(f"Journal load error: {e}")
61
  return "Error loading journal."
62
 
63
+ def save_note(note, category):
64
  if note.strip():
65
+ log_entry(category, "manual note", note, note)
66
+ return "Note saved."
67
 
68
  def clear_journal():
69
  try:
 
174
  "clusterin","igm","iga","igg","complement","transferrin",
175
  "alpha-2-macroglobulin"]
176
 
177
+ # ========== Список кодів проєктів для випадаючих списків ==========
178
+ PROJECT_CODES = [
179
+ "S1-A·R1a", "S1-A·R1b",
180
+ "S1-B·R1a", "S1-B·R2a", "S1-B·R3a", "S1-B·R3b",
181
+ "S1-C·R1a", "S1-C·R1b", "S1-C·R2a",
182
+ "S1-D·R1a", "S1-D·R2a", "S1-D·R3a", "S1-D·R4a", "S1-D·R5a",
183
+ "S1-E·R1a", "S1-E·R1b",
184
+ "S1-F·R1a", "S1-F·R2a", "S1-F·R3a",
185
+ "S1-G·General"
186
+ ]
187
+
188
  # ---------- S1-F RARE ----------
189
  DIPG_VARIANTS = [
190
  {"Variant":"H3K27M (H3F3A)","Freq_pct":78,"Pathway":"PRC2 inhibition → global H3K27me3 loss","Drug_status":"ONC201 (clinical)","Circadian_gene":"BMAL1 suppressed"},
 
245
 
246
  def predict_mirna(gene):
247
  df = pd.DataFrame(MIRNA_DB.get(gene, []))
248
+ log_entry("S1-B·R1a", gene, f"{len(df)} miRNAs")
249
  return df
250
 
251
  def predict_sirna(cancer):
252
  df = pd.DataFrame(SIRNA_DB.get(cancer, []))
253
+ log_entry("S1-B·R2a", cancer, f"{len(df)} targets")
254
  return df
255
 
256
  def get_lncrna():
257
+ log_entry("S1-B·R3a", "load", "ceRNA")
258
  return pd.DataFrame(CERNA)
259
 
260
  def get_aso():
261
+ log_entry("S1-B·R3b", "load", "ASO")
262
  return pd.DataFrame(ASO)
263
 
264
  def predict_drug(pocket):
 
275
  ax.set_title(f"Top compounds — {pocket}", color=TXT, fontsize=10)
276
  plt.tight_layout()
277
  img = safe_img_from_fig(fig)
278
+ log_entry("S1-C·R1a", pocket, f"Top: {df.iloc[0]['Compound'] if len(df) else 'none'}")
279
  return df, img
280
  except Exception as e:
281
+ log_entry("S1-C·R1a", pocket, f"Error: {str(e)}")
282
  return pd.DataFrame(), None
283
 
284
  def predict_variant(hgvs, sift, polyphen, gnomad):
 
295
  conf = "High" if (sift < 0.01 or sift > 0.9) else "Moderate"
296
  colour = RED if "Pathogenic" in cls else GRN
297
  icon = "⚠️ WARNING" if "Pathogenic" in cls else "✅ OK"
298
+ log_entry("S1-A·R1a", hgvs or f"SIFT={sift}", f"{cls} score={score}")
299
  return (
300
  f"<div style=\'background:{CARD};padding:16px;border-radius:8px;font-family:sans-serif;color:{TXT}\'>"
301
+ f"<p style=\'font-size:11px;color:{DIM};margin:0 0 8px\'>S1-A·R1a · OpenVariant</p>"
302
  f"<h3 style=\'color:{colour};margin:0 0 8px\'>{icon} {cls}</h3>"
303
  f"<p>Score: <b>{score:.3f}</b> &nbsp;|&nbsp; Confidence: <b>{conf}</b></p>"
304
  f"<div style=\'background:{BORDER};border-radius:4px;height:14px\'>"
 
317
  if size < 100: score += 1
318
  dominant = ["ApoE","Albumin","Fibrinogen","Vitronectin","ApoA-I"][min(score, 4)]
319
  efficacy = "High" if score >= 4 else "Medium" if score >= 2 else "Low"
320
+ log_entry("S1-D·R1a", f"size={size},peg={peg}", f"dominant={dominant}")
321
  return f"**Dominant corona protein:** {dominant}\n\n**Predicted efficacy:** {efficacy}\n\n**Score:** {score}/6"
322
  except Exception as e:
323
  return f"Error: {str(e)}"
 
341
  ax.set_title("Protein contributions", color=TXT, fontsize=10)
342
  plt.tight_layout()
343
  img = safe_img_from_fig(fig)
344
+ log_entry("S1-E·R1a", f"CTHRC1={c1},FHL2={c2}", f"{label} {prob:.2f}")
345
  html_out = (
346
  f"<div style=\'background:{CARD};padding:14px;border-radius:8px;font-family:sans-serif;\'>"
347
+ f"<p style=\'font-size:11px;color:{DIM};margin:0 0 6px\'>S1-E·R1a · Liquid Biopsy</p>"
348
  f"<span style=\'color:{colour};font-size:24px;font-weight:bold\'>{label}</span><br>"
349
  f"<span style=\'color:{TXT};font-size:14px\'>Probability: {prob:.2f}</span></div>"
350
  )
 
370
  ax.set_title("Vroman Effect — flow vs static", color=TXT, fontsize=9)
371
  plt.tight_layout()
372
  img = safe_img_from_fig(fig)
373
+ log_entry("S1-D·R2a", f"flow={flow_rate}", f"CSI={csi}")
374
  return f"**Corona Shift Index: {csi}** — {stability}", img
375
  except Exception as e:
376
  return f"Error: {str(e)}", None
 
392
  ax.tick_params(colors=TXT)
393
  plt.tight_layout()
394
  img = safe_img_from_fig(fig)
395
+ log_entry("S1-D·R3a", f"pka={pka},zeta={zeta}", f"ApoE={apoe_pct:.1f}%")
396
  return f"**Predicted ApoE:** {apoe_pct:.1f}% — {tier}\n\n**BBB Probability:** {bbb_prob:.2f}", img
397
  except Exception as e:
398
  return f"Error: {str(e)}", None
 
418
  if not out["zeta_mv"]: flags.append("zeta_mv not found")
419
  if not out["corona_proteins"]: flags.append("no proteins detected")
420
  summary = "All key fields extracted" if not flags else " | ".join(flags)
421
+ log_entry("S1-D·R4a", text[:80], f"proteins={len(out['corona_proteins'])}")
422
  return json.dumps(out, indent=2), summary
423
  except Exception as e:
424
  return json.dumps({"error": str(e)}), "Extraction error"
 
426
  def dipg_variants(sort_by):
427
  df = pd.DataFrame(DIPG_VARIANTS).sort_values(
428
  "Freq_pct" if sort_by == "Frequency" else "Drug_status", ascending=False)
429
+ log_entry("S1-F·R1a", sort_by, f"{len(df)} variants")
430
  return df
431
 
432
  def dipg_csf(peg, size):
 
444
  ax.set_title("DIPG — CSF LNP formulations (ApoE%)", color=TXT, fontsize=9)
445
  plt.tight_layout()
446
  img = safe_img_from_fig(fig)
447
+ log_entry("S1-F·R1a", f"peg={peg},size={size}", "formulation ranking")
448
  return df[["Formulation","Size_nm","Zeta_mV","ApoE_pct","BBB_est","Priority"]], img
449
  except Exception as e:
450
  return pd.DataFrame(), None
451
 
452
  def uvm_variants():
453
  df = pd.DataFrame(UVM_VARIANTS)
454
+ log_entry("S1-F·R2a", "load", f"{len(df)} variants")
455
  return df
456
 
457
  def uvm_vitreous():
 
467
  ax.set_title("UVM — LNP retention in vitreous humor", color=TXT, fontsize=9)
468
  plt.tight_layout()
469
  img = safe_img_from_fig(fig)
470
+ log_entry("S1-F·R2a", "load", "vitreous LNP ranking")
471
  return df, img
472
  except Exception as e:
473
  return pd.DataFrame(), None
 
491
  ax.set_title(f"pAML · {row['Variant'][:20]}", color=TXT, fontsize=9)
492
  plt.tight_layout()
493
  img = safe_img_from_fig(fig)
494
+ log_entry("S1-F·R3a", variant, f"ferr={ferr_score:.2f}")
495
  _v = row["Variant"]
496
  _p = row["Pathway"]
497
  _d = row["Drug_status"]
 
499
  _fs = f"{ferr_score:.2f}"
500
  summary = (
501
  f"<div style='background:{CARD};padding:14px;border-radius:8px;font-family:sans-serif;'>"
502
+ f"<p style='color:{DIM};font-size:11px;margin:0 0 6px'>S1-F·R3a · pAML</p>"
503
  f"<b style='color:{ACC2};font-size:15px'>{_v}</b><br>"
504
  f"<p style='color:{TXT};margin:6px 0'><b>Pathway:</b> {_p}</p>"
505
  f"<p style='color:{TXT};margin:0'><b>Drug:</b> {_d}</p>"
 
613
  css = f"""
614
  body, .gradio-container {{ background: {BG} !important; color: {TXT} !important; }}
615
 
616
+ /* Вкладки верхнього рівня (категорії) з переносом на новий ряд */
617
+ .tabs-outer .tab-nav {{
618
+ display: flex;
619
+ flex-wrap: wrap;
620
+ gap: 2px;
621
+ }}
622
  .tabs-outer .tab-nav button {{
623
  color: {TXT} !important;
624
  background: {CARD} !important;
 
626
  font-weight: 600 !important;
627
  padding: 8px 16px !important;
628
  border-radius: 6px 6px 0 0 !important;
629
+ border: 1px solid {BORDER};
630
+ border-bottom: none;
631
+ margin-right: 2px;
632
+ margin-bottom: 2px;
633
+ white-space: nowrap;
634
  }}
635
  .tabs-outer .tab-nav button.selected {{
636
  border-bottom: 3px solid {ACC} !important;
 
702
  margin-left: 6px;
703
  }}
704
 
705
+ /* Бічна панель з журналом (тільки введення) */
706
+ .sidebar-journal {{
707
  background: {CARD};
708
  border: 1px solid {BORDER};
709
  border-radius: 8px;
710
  padding: 14px;
711
  }}
712
+ .sidebar-journal h3 {{
713
  color: {ACC};
714
  margin-top: 0;
715
  }}
 
727
  <span style="color:{DIM};font-size:11px;margin-left:12px">Science Sphere — sub-direction 1</span>
728
  <br><br>
729
  <span style="color:{ACC2};font-weight:600">S1-A · PHYLO-GENOMICS</span> — What breaks in DNA<br>
730
+ &nbsp;&nbsp;&nbsp;├─ <b>S1-A·R1a</b> OpenVariant <span style="color:{GRN}"> AUC=0.939 ✅</span><br>
731
+ &nbsp;&nbsp;&nbsp;└─ <b>S1-A·R1b</b> Somatic classifier <span style="color:#f59e0b"> 🔶 In progress</span><br><br>
732
  <span style="color:{ACC2};font-weight:600">S1-B · PHYLO-RNA</span> — How to silence it via RNA<br>
733
+ &nbsp;&nbsp;&nbsp;├─ <b>S1-B·R1a</b> miRNA silencing <span style="color:{GRN}"> ✅</span><br>
734
+ &nbsp;&nbsp;&nbsp;├─ <b>S1-B·R2a</b> siRNA synthetic lethal <span style="color:{GRN}"> ✅</span><br>
735
+ &nbsp;&nbsp;&nbsp;├─ <b>S1-B·R3a</b> lncRNA-TREM2 ceRNA <span style="color:{GRN}"> ✅</span><br>
736
+ &nbsp;&nbsp;&nbsp;└─ <b>S1-B·R3b</b> ASO designer <span style="color:{GRN}"> ✅</span><br><br>
737
  <span style="color:{ACC2};font-weight:600">S1-C · PHYLO-DRUG</span> — Which molecule treats it<br>
738
+ &nbsp;&nbsp;&nbsp;├─ <b>S1-C·R1a</b> FGFR3 RNA-directed compounds <span style="color:{GRN}"> ✅</span><br>
739
+ &nbsp;&nbsp;&nbsp;├─ <b>S1-C·R1b</b> Synthetic lethal drug mapping <span style="color:#f59e0b"> 🔶</span><br>
740
+ &nbsp;&nbsp;&nbsp;└─ <b>S1-C·R2a</b> m6A × Ferroptosis × Circadian <span style="color:{DIM}"> 🔴 Frontier</span><br><br>
741
  <span style="color:{ACC2};font-weight:600">S1-D · PHYLO-LNP</span> — How to deliver the drug<br>
742
+ &nbsp;&nbsp;&nbsp;├─ <b>S1-D·R1a</b> LNP corona (serum) <span style="color:{GRN}"> AUC=0.791 ✅</span><br>
743
+ &nbsp;&nbsp;&nbsp;├─ <b>S1-D·R2a</b> Flow corona — Vroman effect <span style="color:{GRN}"> ✅</span><br>
744
+ &nbsp;&nbsp;&nbsp;├─ <b>S1-D·R3a</b> LNP brain / BBB / ApoE <span style="color:{GRN}"> ✅</span><br>
745
+ &nbsp;&nbsp;&nbsp;├─ <b>S1-D·R4a</b> AutoCorona NLP <span style="color:{GRN}"> F1=0.71 ✅</span><br>
746
+ &nbsp;&nbsp;&nbsp;└─ <b>S1-D·R5a</b> CSF · Vitreous · Bone Marrow <span style="color:{DIM}"> 🔴 0 prior studies</span><br><br>
747
  <span style="color:{ACC2};font-weight:600">S1-E · PHYLO-BIOMARKERS</span> — Detect without biopsy<br>
748
+ &nbsp;&nbsp;&nbsp;├─ <b>S1-E·R1a</b> Liquid Biopsy classifier <span style="color:{GRN}"> AUC=0.992* ✅</span><br>
749
+ &nbsp;&nbsp;&nbsp;└─ <b>S1-E·R1b</b> Protein panel validator <span style="color:#f59e0b"> 🔶</span><br><br>
750
  <span style="color:{ACC2};font-weight:600">S1-F · PHYLO-RARE</span> — Where almost nobody has looked yet<br>
751
+ &nbsp;&nbsp;&nbsp;├─ <b>S1-F·R1a</b> DIPG toolkit (H3K27M + CSF LNP + Circadian) <span style="color:#f59e0b"> 🔶</span><br>
752
+ &nbsp;&nbsp;&nbsp;├─ <b>S1-F·R2a</b> UVM toolkit (GNAQ/GNA11 + vitreous + m6A) <span style="color:#f59e0b"> 🔶</span><br>
753
+ &nbsp;&nbsp;&nbsp;└─ <b>S1-F·R3a</b> pAML toolkit (FLT3-ITD + BM niche + ferroptosis) <span style="color:#f59e0b"> 🔶</span><br><br>
754
  <span style="color:{ACC2};font-weight:600">S1-G · PHYLO-SIM</span> — 3D Models & Simulations<br>
755
  &nbsp;&nbsp;&nbsp;├─ <b>Nanoparticle</b> Interactive 3D model <span style="color:{GRN}"> ✅</span><br>
756
  &nbsp;&nbsp;&nbsp;├─ <b>DNA Helix</b> Double helix visualization <span style="color:{GRN}"> ✅</span><br>
 
776
  gr.HTML(MAP_HTML)
777
 
778
  # === S1-A · PHYLO-GENOMICS ===
779
+ with gr.TabItem("🧬 S1-A"):
780
  gr.HTML(section_header(
781
  "S1-A", "PHYLO-GENOMICS", "— What breaks in DNA",
782
  "R1a OpenVariant ✅ · R1b Somatic classifier 🔶"
 
787
  with gr.Tabs(elem_classes="sub-tabs"):
788
  # R1a · OpenVariant
789
  with gr.TabItem("R1a · OpenVariant"):
790
+ gr.HTML(proj_badge("S1-A·R1a", "OpenVariant — SNV Pathogenicity Classifier", "AUC=0.939"))
791
  hgvs = gr.Textbox(label="HGVS notation", placeholder="BRCA1:p.R1699Q")
792
  gr.Markdown("**Or enter functional scores manually:**")
793
  with gr.Row():
 
802
  b_v.click(predict_variant, [hgvs,sift,pp,gn], o_v)
803
  # R1b · Somatic Classifier (в розробці)
804
  with gr.TabItem("R1b · Somatic Classifier 🔶"):
805
+ gr.HTML(proj_badge("S1-A·R1b", "Somatic Mutation Classifier", "🔶 In progress"))
806
  gr.Markdown("> This module is in active development. Coming in the next release.")
807
 
808
  # === S1-B · PHYLO-RNA ===
809
+ with gr.TabItem("🔬 S1-B"):
810
  gr.HTML(section_header(
811
  "S1-B", "PHYLO-RNA", "— How to silence it via RNA",
812
  "R1a miRNA ✅ · R2a siRNA ✅ · R3a lncRNA ✅ · R3b ASO ✅"
 
816
  with gr.TabItem("R1 · miRNA silencing"):
817
  with gr.Tabs(elem_classes="sub-tabs"):
818
  with gr.TabItem("R1a · BRCA2 miRNA"):
819
+ gr.HTML(proj_badge("S1-B·R1a", "miRNA Silencing — BRCA1/2 · TP53"))
820
  g1 = gr.Dropdown(["BRCA2","BRCA1","TP53"], value="BRCA2", label="Gene")
821
  b1 = gr.Button("Find miRNAs", variant="primary")
822
  o1 = gr.Dataframe(label="Top 5 downregulated miRNAs")
 
826
  with gr.TabItem("R2 · siRNA SL"):
827
  with gr.Tabs(elem_classes="sub-tabs"):
828
  with gr.TabItem("R2a · TP53 siRNA"):
829
+ gr.HTML(proj_badge("S1-B·R2a", "siRNA Synthetic Lethal — TP53-null"))
830
  g2 = gr.Dropdown(["LUAD","BRCA","COAD"], value="LUAD", label="Cancer type")
831
  b2 = gr.Button("Find Targets", variant="primary")
832
  o2 = gr.Dataframe(label="Top 5 synthetic lethal targets")
 
836
  with gr.TabItem("R3 · lncRNA + ASO"):
837
  with gr.Tabs(elem_classes="sub-tabs"):
838
  with gr.TabItem("R3a · lncRNA-TREM2"):
839
+ gr.HTML(proj_badge("S1-B·R3a", "lncRNA-TREM2 ceRNA Network"))
840
  b3a = gr.Button("Load ceRNA", variant="primary")
841
  o3a = gr.Dataframe(label="ceRNA Network (R3a)")
842
  b3a.click(lambda: pd.DataFrame(CERNA), [], o3a)
843
  with gr.TabItem("R3b · ASO Designer"):
844
+ gr.HTML(proj_badge("S1-B·R3b", "ASO Designer"))
845
  b3b = gr.Button("Load ASO Candidates", variant="primary")
846
  o3b = gr.Dataframe(label="ASO Candidates (R3b)")
847
  b3b.click(lambda: pd.DataFrame(ASO), [], o3b)
848
 
849
  # === S1-C · PHYLO-DRUG ===
850
+ with gr.TabItem("💊 S1-C"):
851
  gr.HTML(section_header(
852
  "S1-C", "PHYLO-DRUG", "— Which molecule treats it",
853
  "R1a FGFR3 ✅ · R1b SL drug mapping 🔶 · R2a Frontier 🔴⭐"
 
857
  with gr.TabItem("R1 · RNA-directed drug"):
858
  with gr.Tabs(elem_classes="sub-tabs"):
859
  with gr.TabItem("R1a · FGFR3 RNA Drug"):
860
+ gr.HTML(proj_badge("S1-C·R1a", "FGFR3 RNA-Directed Drug Discovery", "top score 0.793"))
861
  g4 = gr.Radio(["P1 (hairpin loop)","P10 (G-quadruplex)"],
862
  value="P1 (hairpin loop)", label="Target pocket")
863
  b4_drug = gr.Button("Screen Compounds", variant="primary")
 
866
  gr.Examples([["P1 (hairpin loop)"],["P10 (G-quadruplex)"]], inputs=[g4])
867
  b4_drug.click(predict_drug, [g4], [o4t, o4p])
868
  with gr.TabItem("R1b · SL Drug Mapping 🔶"):
869
+ gr.HTML(proj_badge("S1-C·R1b", "Synthetic Lethal Drug Mapping", "🔶 In progress"))
870
  gr.Markdown("> In development. Coming soon.")
871
  # R2 · Frontier
872
  with gr.TabItem("R2 · Frontier"):
873
  with gr.Tabs(elem_classes="sub-tabs"):
874
  with gr.TabItem("R2a · m6A×Ferroptosis×Circadian 🔴⭐"):
875
+ gr.HTML(proj_badge("S1-C·R2a", "m6A × Ferroptosis × Circadian", "🔴 Frontier"))
876
  gr.Markdown(
877
  "> **Research gap:** This triple intersection has never been studied as an integrated system.\n\n"
878
  "> **Planned datasets:** TCGA-PAAD · GEO m6A atlases · Circadian gene panels\n\n"
 
880
  )
881
 
882
  # === S1-D · PHYLO-LNP ===
883
+ with gr.TabItem("🧪 S1-D"):
884
  gr.HTML(section_header(
885
  "S1-D", "PHYLO-LNP", "— How to deliver the drug",
886
  "R1a Corona ✅ · R2a Flow ✅ · R3a Brain ✅ · R4a NLP ✅ · R5a CSF/BM 🔴⭐"
 
890
  with gr.TabItem("R1 · Serum corona"):
891
  with gr.Tabs(elem_classes="sub-tabs"):
892
  with gr.TabItem("R1a · LNP Corona ML"):
893
+ gr.HTML(proj_badge("S1-D·R1a", "LNP Protein Corona (Serum)", "AUC=0.791"))
894
  with gr.Row():
895
  sz = gr.Slider(50,300,value=100,step=1,label="Size (nm)")
896
  zt = gr.Slider(-40,10,value=-5,step=1,label="Zeta (mV)")
 
905
  with gr.TabItem("R2 · Flow corona"):
906
  with gr.Tabs(elem_classes="sub-tabs"):
907
  with gr.TabItem("R2a · Flow Corona"):
908
+ gr.HTML(proj_badge("S1-D·R2a", "Flow Corona — Vroman Effect"))
909
  with gr.Row():
910
  s8 = gr.Slider(50,300,value=100,step=1,label="Size (nm)")
911
  z8 = gr.Slider(-40,10,value=-5,step=1,label="Zeta (mV)")
 
922
  with gr.TabItem("R3 · Brain BBB"):
923
  with gr.Tabs(elem_classes="sub-tabs"):
924
  with gr.TabItem("R3a · LNP Brain"):
925
+ gr.HTML(proj_badge("S1-D·R3a", "LNP Brain Delivery"))
926
  smi = gr.Textbox(label="Ionizable lipid SMILES",
927
  value="CC(C)CC(=O)OCC(COC(=O)CC(C)C)OC(=O)CC(C)C")
928
  with gr.Row():
 
937
  with gr.TabItem("R4 · NLP"):
938
  with gr.Tabs(elem_classes="sub-tabs"):
939
  with gr.TabItem("R4a · AutoCorona NLP"):
940
+ gr.HTML(proj_badge("S1-D·R4a", "AutoCorona NLP", "F1=0.71"))
941
  txt = gr.Textbox(lines=5,label="Paper abstract",placeholder="Paste abstract here...")
942
  b10 = gr.Button("Extract Data", variant="primary")
943
  o10j = gr.Code(label="Extracted JSON", language="json")
 
952
  with gr.TabItem("R5 · Exotic fluids 🔴⭐"):
953
  with gr.Tabs(elem_classes="sub-tabs"):
954
  with gr.TabItem("R5a · CSF/Vitreous/BM"):
955
+ gr.HTML(proj_badge("S1-D·R5a", "LNP Corona in CSF · Vitreous · Bone Marrow", "🔴 0 prior studies"))
956
  gr.Markdown(
957
  "> **Research gap:** Protein corona has only been characterized in serum/plasma. "
958
  "CSF, vitreous humor, and bone marrow interstitial fluid remain completely unstudied.\n\n"
 
961
  )
962
 
963
  # === S1-E · PHYLO-BIOMARKERS ===
964
+ with gr.TabItem("🩸 S1-E"):
965
  gr.HTML(section_header(
966
  "S1-E", "PHYLO-BIOMARKERS", "— Detect without biopsy",
967
  "R1a Liquid Biopsy ✅ · R1b Protein validator 🔶"
 
970
  with gr.TabItem("R1 · Liquid biopsy"):
971
  with gr.Tabs(elem_classes="sub-tabs"):
972
  with gr.TabItem("R1a · Liquid Biopsy"):
973
+ gr.HTML(proj_badge("S1-E·R1a", "Liquid Biopsy Classifier", "AUC=0.992*"))
974
  with gr.Row():
975
  p1=gr.Slider(-3,3,value=0,step=0.1,label="CTHRC1")
976
  p2=gr.Slider(-3,3,value=0,step=0.1,label="FHL2")
 
990
  inputs=[p1,p2,p3,p4,p5,p6,p7,p8,p9,p10])
991
  b7.click(predict_cancer, [p1,p2,p3,p4,p5,p6,p7,p8,p9,p10], [o7t,o7p])
992
  with gr.TabItem("R1b · Protein Validator 🔶"):
993
+ gr.HTML(proj_badge("S1-E·R1b", "Protein Panel Validator", "🔶 In progress"))
994
  gr.Markdown("> Coming next — validates R1a results against GEO plasma proteomics datasets.")
995
 
996
  # === S1-F · PHYLO-RARE ===
997
+ with gr.TabItem("🧠 S1-F"):
998
  gr.HTML(section_header(
999
  "S1-F", "PHYLO-RARE", "— Where almost nobody has looked yet",
1000
  "<b style='color:#ef4444'>⚠️ <300 cases/yr · <5% survival · 0–1 prior studies per gap</b><br>"
 
1005
  with gr.TabItem("R1 · DIPG"):
1006
  with gr.Tabs(elem_classes="sub-tabs"):
1007
  with gr.TabItem("R1a · DIPG Toolkit"):
1008
+ gr.HTML(proj_badge("S1-F·R1a", "DIPG Toolkit", "PBTA · GSE126319"))
1009
  gr.Markdown(
1010
  "> **Why DIPG?** Diffuse Intrinsic Pontine Glioma — median survival 9–11 months. "
1011
  "H3K27M oncohistone in **78%** cases. "
 
1039
  with gr.TabItem("R2 · UVM"):
1040
  with gr.Tabs(elem_classes="sub-tabs"):
1041
  with gr.TabItem("R2a · UVM Toolkit"):
1042
+ gr.HTML(proj_badge("S1-F·R2a", "UVM Toolkit", "TCGA-UVM n=80"))
1043
  gr.Markdown(
1044
  "> **Why UVM?** Uveal Melanoma — metastatic 5-yr survival **15%**. "
1045
  "GNAQ/GNA11 mutations in 78% cases. "
 
1069
  with gr.TabItem("R3 · pAML"):
1070
  with gr.Tabs(elem_classes="sub-tabs"):
1071
  with gr.TabItem("R3a · pAML Toolkit"):
1072
+ gr.HTML(proj_badge("S1-F·R3a", "pAML Toolkit", "TARGET-AML n≈197"))
1073
  gr.Markdown(
1074
  "> **Why pAML?** Pediatric AML — relapse OS **<30%**. "
1075
  "FLT3-ITD in 25% cases. "
 
1103
  )
1104
 
1105
  # === S1-G · 3D Lab ===
1106
+ with gr.TabItem("🧊 S1-G"):
1107
  gr.HTML(section_header(
1108
  "S1-G", "PHYLO-SIM", "— 3D Models & Simulations",
1109
  "Interactive visualizations for learning"
 
1136
  ## 🧪 Guided Investigations
1137
  > 🟢 Beginner → 🟡 Intermediate → 🔴 Advanced
1138
 
1139
+ **S1-A·R1a** OpenVariant – try BRCA1:p.R1699Q vs R1699W
1140
+ **S1-D·R1a** Corona – vary PEG% and observe dominant protein
1141
+ **S1-D·R2a** Flow Corona – compare flow 0 vs 40 cm/s
1142
+ **S1-B·R2a** siRNA – count "Novel" targets across cancer types
1143
+ **S1-E·R1a** Liquid Biopsy – find minimal signal for CANCER
1144
+ **S1-G·3D Lab** – explore nanoparticle, DNA, and protein corona models
1145
  """)
1146
 
1147
+ # === Journal (окрема вкладка) ===
1148
+ with gr.TabItem("📓 Journal"):
1149
+ gr.Markdown("## Lab Journal — Full History")
1150
+ with gr.Row():
1151
+ filter_dropdown = gr.Dropdown(
1152
+ choices=["All"] + PROJECT_CODES,
1153
+ value="All",
1154
+ label="Filter by project code"
1155
+ )
1156
+ refresh_btn = gr.Button("🔄 Refresh", size="sm")
1157
+ clear_btn = gr.Button("🗑️ Clear Journal", size="sm")
1158
+ journal_display = gr.Markdown(value=load_journal())
1159
+
1160
+ refresh_btn.click(load_journal, inputs=[filter_dropdown], outputs=journal_display)
1161
+ clear_btn.click(clear_journal, [], journal_display).then(
1162
+ load_journal, inputs=[filter_dropdown], outputs=journal_display
1163
+ )
1164
+ filter_dropdown.change(load_journal, inputs=[filter_dropdown], outputs=journal_display)
1165
+
1166
+ # Права колонка – швидке введення нотаток (без відображення)
1167
+ with gr.Column(scale=1, min_width=260):
1168
+ with gr.Group(elem_classes="sidebar-journal"):
1169
+ gr.Markdown("## 📝 Quick Note")
1170
+ note_dropdown = gr.Dropdown(
1171
+ choices=PROJECT_CODES,
1172
+ value=PROJECT_CODES[0],
1173
+ label="Project code"
1174
+ )
1175
+ note_input = gr.Textbox(label="Your observation", placeholder="Type here...", lines=3)
1176
+ save_btn = gr.Button("💾 Save Note", variant="primary", size="sm")
1177
+ save_status = gr.Markdown("")
1178
+
1179
+ def quick_save(note, code):
1180
+ if note.strip():
1181
+ log_entry(code, "quick note", note, note)
1182
+ return "✅ Note saved."
1183
+ return "⚠️ Note is empty."
1184
 
1185
+ save_btn.click(quick_save, [note_input, note_dropdown], save_status)
 
 
1186
 
1187
  gr.Markdown(
1188
  "---\n**K R&D Lab** · MIT License · "