TEZv commited on
Commit
d162838
·
verified ·
1 Parent(s): e2679cd

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +513 -444
app.py CHANGED
@@ -9,6 +9,8 @@ from io import BytesIO
9
  from PIL import Image
10
  from datetime import datetime
11
  from pathlib import Path
 
 
12
 
13
  # ========== Діагностичний друк ==========
14
  print("Gradio version:", gr.__version__)
@@ -43,13 +45,25 @@ def load_journal():
43
  try:
44
  if not LOG_PATH.exists():
45
  return pd.DataFrame(columns=["timestamp","tab","inputs","result","note"])
46
- return pd.read_csv(LOG_PATH)
 
 
 
 
47
  except Exception:
48
- return pd.DataFrame(columns=["timestamp","tab","inputs","result","note"])
49
 
50
- def save_note(note, tab, last_result):
51
- log_entry(tab, "", last_result, note)
52
- return "✅ Saved!", load_journal()
 
 
 
 
 
 
 
 
53
 
54
  # ========== БАЗИ ДАНИХ ==========
55
  MIRNA_DB = {
@@ -152,7 +166,65 @@ PROTEINS = ["albumin","apolipoprotein","fibrinogen","vitronectin",
152
  "clusterin","igm","iga","igg","complement","transferrin",
153
  "alpha-2-macroglobulin"]
154
 
155
- # ========== ФУНКЦІЇ ПРЕДИКЦІЇ ==========
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
156
  def predict_mirna(gene):
157
  df = pd.DataFrame(MIRNA_DB.get(gene, []))
158
  log_entry("S1-B · R1a · miRNA", gene, f"{len(df)} miRNAs")
@@ -172,17 +244,24 @@ def get_aso():
172
  return pd.DataFrame(ASO)
173
 
174
  def predict_drug(pocket):
175
- df = pd.DataFrame(FGFR3.get(pocket, []))
176
- fig, ax = plt.subplots(figsize=(6, 4), facecolor=CARD)
177
- ax.set_facecolor(CARD)
178
- ax.barh(df["Compound"], df["Final_score"], color=ACC)
179
- ax.set_xlabel("Final Score", color=TXT); ax.tick_params(colors=TXT)
180
- for sp in ax.spines.values(): sp.set_edgecolor(BORDER)
181
- ax.set_title(f"Top compounds — {pocket}", color=TXT, fontsize=10)
182
- plt.tight_layout()
183
- buf = BytesIO(); plt.savefig(buf, format="png", dpi=120, facecolor=CARD); plt.close(); buf.seek(0)
184
- log_entry("S1-C · R1a · FGFR3", pocket, f"Top: {df.iloc[0]['Compound'] if len(df) else 'none'}")
185
- return df, Image.open(buf)
 
 
 
 
 
 
 
186
 
187
  def predict_variant(hgvs, sift, polyphen, gnomad):
188
  hgvs = hgvs.strip()
@@ -211,147 +290,120 @@ def predict_variant(hgvs, sift, polyphen, gnomad):
211
  )
212
 
213
  def predict_corona(size, zeta, peg, lipid):
214
- score = 0
215
- if lipid == "Ionizable": score += 2
216
- elif lipid == "Cationic": score += 1
217
- if abs(zeta) < 10: score += 1
218
- if peg > 1.5: score += 2
219
- if size < 100: score += 1
220
- dominant = ["ApoE","Albumin","Fibrinogen","Vitronectin","ApoA-I"][min(score, 4)]
221
- efficacy = "High" if score >= 4 else "Medium" if score >= 2 else "Low"
222
- log_entry("S1-D · R1a · Corona", f"size={size},peg={peg}", f"dominant={dominant}")
223
- return f"**Dominant corona protein:** {dominant}\n\n**Predicted efficacy:** {efficacy}\n\n**Score:** {score}/6"
 
 
 
224
 
225
  def predict_cancer(c1,c2,c3,c4,c5,c6,c7,c8,c9,c10):
226
- vals = [c1,c2,c3,c4,c5,c6,c7,c8,c9,c10]
227
- names, weights = list(BM_W.keys()), list(BM_W.values())
228
- raw = sum(v*w for v,w in zip(vals, weights))
229
- prob = 1 / (1 + np.exp(-raw * 2))
230
- label = "CANCER" if prob > 0.5 else "HEALTHY"
231
- colour = RED if prob > 0.5 else GRN
232
- contribs = [v*w for v,w in zip(vals, weights)]
233
- fig, ax = plt.subplots(figsize=(6, 3.5), facecolor=CARD)
234
- ax.set_facecolor(CARD)
235
- ax.barh(names, contribs, color=[ACC if c > 0 else ACC2 for c in contribs])
236
- ax.axvline(0, color=TXT, linewidth=0.8)
237
- ax.set_xlabel("Contribution to cancer score", color=TXT)
238
- ax.tick_params(colors=TXT, labelsize=8)
239
- for sp in ax.spines.values(): sp.set_edgecolor(BORDER)
240
- ax.set_title("Protein contributions", color=TXT, fontsize=10)
241
- plt.tight_layout()
242
- buf = BytesIO(); plt.savefig(buf, format="png", dpi=120, facecolor=CARD); plt.close(); buf.seek(0)
243
- log_entry("S1-E · R1a · Liquid Biopsy", f"CTHRC1={c1},FHL2={c2}", f"{label} {prob:.2f}")
244
- return (
245
- f"<div style=\'background:{CARD};padding:14px;border-radius:8px;font-family:sans-serif;\'>"
246
- f"<p style=\'font-size:11px;color:{DIM};margin:0 0 6px\'>S1-E · R1a · Liquid Biopsy</p>"
247
- f"<span style=\'color:{colour};font-size:24px;font-weight:bold\'>{label}</span><br>"
248
- f"<span style=\'color:{TXT};font-size:14px\'>Probability: {prob:.2f}</span></div>"
249
- ), Image.open(buf)
 
 
 
 
250
 
251
  def predict_flow(size, zeta, peg, charge, flow_rate):
252
- csi = round(min((flow_rate/40)*0.6 + (peg/5)*0.2 + (1 if charge=="Cationic" else 0)*0.2, 1.0), 3)
253
- stability = "High remodeling" if csi > 0.6 else "Medium" if csi > 0.3 else "Stable"
254
- t = np.linspace(0, 60, 200)
255
- kf, ks = 0.03*(1+flow_rate/40), 0.038*(1+flow_rate/40)
256
- fig, ax = plt.subplots(figsize=(6, 3.5), facecolor=CARD)
257
- ax.set_facecolor(CARD)
258
- ax.plot(t, 60*np.exp(-0.03*t)+20, color="#60a5fa", ls="--", label="Albumin (static)")
259
- ax.plot(t, 60*np.exp(-kf*t)+10, color="#60a5fa", label="Albumin (flow)")
260
- ax.plot(t, 14*(1-np.exp(-0.038*t))+5, color=ACC, ls="--", label="ApoE (static)")
261
- ax.plot(t, 20*(1-np.exp(-ks*t))+5, color=ACC, label="ApoE (flow)")
262
- ax.set_xlabel("Time (min)", color=TXT); ax.set_ylabel("% Corona", color=TXT)
263
- ax.tick_params(colors=TXT); ax.legend(fontsize=7, labelcolor=TXT, facecolor=CARD)
264
- for sp in ax.spines.values(): sp.set_edgecolor(BORDER)
265
- ax.set_title("Vroman Effect — flow vs static", color=TXT, fontsize=9)
266
- plt.tight_layout()
267
- buf = BytesIO(); plt.savefig(buf, format="png", dpi=120, facecolor=CARD); plt.close(); buf.seek(0)
268
- log_entry("S1-D · R2a · Flow Corona", f"flow={flow_rate}", f"CSI={csi}")
269
- return f"**Corona Shift Index: {csi}** {stability}", Image.open(buf)
 
 
 
270
 
271
  def predict_bbb(smiles, pka, zeta):
272
- logp = smiles.count("C")*0.3 - smiles.count("O")*0.5 + 1.5
273
- apoe_pct = max(0, min(40, (7.0-pka)*8 + abs(zeta)*0.5 + logp*0.8))
274
- bbb_prob = min(0.95, apoe_pct/30)
275
- tier = "HIGH (>20%)" if apoe_pct > 20 else "MEDIUM (10-20%)" if apoe_pct > 10 else "LOW (<10%)"
276
- cats = ["ApoE%","BBB","logP","pKa fit","Zeta"]
277
- vals = [apoe_pct/40, bbb_prob, min(logp/5,1), (7-abs(pka-6.5))/7, (10-abs(zeta))/10]
278
- angles = np.linspace(0, 2*np.pi, len(cats), endpoint=False).tolist()
279
- v2, a2 = vals+[vals[0]], angles+[angles[0]]
280
- fig, ax = plt.subplots(figsize=(5, 4), subplot_kw={"polar":True}, facecolor=CARD)
281
- ax.set_facecolor(CARD)
282
- ax.plot(a2, v2, color=ACC, linewidth=2); ax.fill(a2, v2, color=ACC, alpha=0.2)
283
- ax.set_xticks(angles); ax.set_xticklabels(cats, color=TXT, fontsize=8)
284
- ax.tick_params(colors=TXT)
285
- plt.tight_layout()
286
- buf = BytesIO(); plt.savefig(buf, format="png", dpi=120, facecolor=CARD); plt.close(); buf.seek(0)
287
- log_entry("S1-D · R3a · LNP Brain", f"pka={pka},zeta={zeta}", f"ApoE={apoe_pct:.1f}%")
288
- return f"**Predicted ApoE:** {apoe_pct:.1f}% {tier}\n\n**BBB Probability:** {bbb_prob:.2f}", Image.open(buf)
 
 
 
289
 
290
  def extract_corona(text):
291
- out = {"nanoparticle_composition":"","size_nm":None,"zeta_mv":None,"PDI":None,
292
- "protein_source":"","corona_proteins":[],"confidence":{}}
293
- for pat, key in [(r"(\d+\.?\d*)\s*(?:nm|nanometer)","size_nm"),
294
- (r"([+-]?\d+\.?\d*)\s*mV","zeta_mv"),
295
- (r"PDI\s*[=:of]*\s*(\d+\.?\d*)","PDI")]:
296
- m = re.search(pat, text, re.I)
297
- if m: out[key] = float(m.group(1)); out["confidence"][key] = "HIGH"
298
- for src in ["human plasma","human serum","fetal bovine serum","FBS","PBS"]:
299
- if src.lower() in text.lower():
300
- out["protein_source"] = src; out["confidence"]["protein_source"] = "HIGH"; break
301
- out["corona_proteins"] = [{"name":p,"confidence":"MEDIUM"} for p in PROTEINS if p in text.lower()]
302
- for lip in ["DSPC","DOPE","MC3","DLin","cholesterol","PEG","DOTAP"]:
303
- if lip in text: out["nanoparticle_composition"] += lip + " "
304
- out["nanoparticle_composition"] = out["nanoparticle_composition"].strip()
305
- flags = []
306
- if not out["size_nm"]: flags.append("size_nm not found")
307
- if not out["zeta_mv"]: flags.append("zeta_mv not found")
308
- if not out["corona_proteins"]: flags.append("no proteins detected")
309
- summary = "All key fields extracted" if not flags else " | ".join(flags)
310
- log_entry("S1-D · R4a · AutoCorona NLP", text[:80], f"proteins={len(out['corona_proteins'])}")
311
- return json.dumps(out, indent=2), summary
312
-
313
- # ---------- S1-F RARE ----------
314
- DIPG_VARIANTS = [
315
- {"Variant":"H3K27M (H3F3A)","Freq_pct":78,"Pathway":"PRC2 inhibition → global H3K27me3 loss","Drug_status":"ONC201 (clinical)","Circadian_gene":"BMAL1 suppressed"},
316
- {"Variant":"ACVR1 p.R206H","Freq_pct":21,"Pathway":"BMP/SMAD hyperactivation","Drug_status":"LDN-193189 (preclinical)","Circadian_gene":"PER1 disrupted"},
317
- {"Variant":"PIK3CA p.H1047R","Freq_pct":15,"Pathway":"PI3K/AKT/mTOR","Drug_status":"Copanlisib (clinical)","Circadian_gene":"CRY1 altered"},
318
- {"Variant":"TP53 p.R248W","Freq_pct":14,"Pathway":"DNA damage response loss","Drug_status":"APR-246 (clinical)","Circadian_gene":"p53-CLOCK axis"},
319
- {"Variant":"PDGFRA amp","Freq_pct":13,"Pathway":"RTK/RAS signalling","Drug_status":"Avapritinib (clinical)","Circadian_gene":"REV-ERB altered"},
320
- ]
321
- DIPG_CSF_LNP = [
322
- {"Formulation":"MC3-DSPC-Chol-PEG","Size_nm":92,"Zeta_mV":-4.1,"CSF_protein":"Beta2-microglobulin","ApoE_pct":12.4,"BBB_est":0.41,"Priority":"HIGH"},
323
- {"Formulation":"DLin-KC2-DSPE-PEG","Size_nm":87,"Zeta_mV":-3.8,"CSF_protein":"Cystatin C","ApoE_pct":14.1,"BBB_est":0.47,"Priority":"HIGH"},
324
- {"Formulation":"C12-200-DOPE-PEG","Size_nm":103,"Zeta_mV":-5.2,"CSF_protein":"Albumin (low)","ApoE_pct":9.8,"BBB_est":0.33,"Priority":"MEDIUM"},
325
- {"Formulation":"DODAP-DSPC-Chol","Size_nm":118,"Zeta_mV":-2.1,"CSF_protein":"Transferrin","ApoE_pct":7.2,"BBB_est":0.24,"Priority":"LOW"},
326
- ]
327
-
328
- UVM_VARIANTS = [
329
- {"Variant":"GNAQ p.Q209L","Freq_pct":46,"Pathway":"PLCβ → PKC → MAPK","Drug_status":"Darovasertib (clinical)","m6A_writer":"METTL3 upregulated"},
330
- {"Variant":"GNA11 p.Q209L","Freq_pct":32,"Pathway":"PLCβ → PKC → MAPK","Drug_status":"Darovasertib (clinical)","m6A_writer":"WTAP upregulated"},
331
- {"Variant":"BAP1 loss","Freq_pct":47,"Pathway":"Chromatin remodeling → metastasis","Drug_status":"No approved (HDAC trials)","m6A_writer":"FTO overexpressed"},
332
- {"Variant":"SF3B1 p.R625H","Freq_pct":19,"Pathway":"Splicing alteration → neoepitopes","Drug_status":"H3B-8800 (clinical)","m6A_writer":"METTL14 altered"},
333
- {"Variant":"EIF1AX p.A113_splice","Freq_pct":14,"Pathway":"Translation initiation","Drug_status":"Novel — no drug","m6A_writer":"YTHDF2 suppressed"},
334
- ]
335
- UVM_VITREOUS_LNP = [
336
- {"Formulation":"SM-102-DSPC-Chol-PEG","Vitreal_protein":"Hyaluronan-binding","Size_nm":95,"Zeta_mV":-3.2,"Retention_h":18,"Priority":"HIGH"},
337
- {"Formulation":"Lipid-H-DOPE-PEG","Vitreal_protein":"Vitronectin dominant","Size_nm":88,"Zeta_mV":-4.0,"Retention_h":22,"Priority":"HIGH"},
338
- {"Formulation":"DOTAP-DSPC-PEG","Vitreal_protein":"Albumin wash-out","Size_nm":112,"Zeta_mV":+2.1,"Retention_h":6,"Priority":"LOW"},
339
- {"Formulation":"MC3-DPPC-Chol","Vitreal_protein":"Clusterin-rich","Size_nm":101,"Zeta_mV":-2.8,"Retention_h":14,"Priority":"MEDIUM"},
340
- ]
341
-
342
- PAML_VARIANTS = [
343
- {"Variant":"FLT3-ITD","Freq_pct":25,"Pathway":"RTK constitutive activation → JAK/STAT","Drug_status":"Midostaurin (approved)","Ferroptosis":"GPX4 suppressed"},
344
- {"Variant":"NPM1 c.860_863dupTCAG","Freq_pct":30,"Pathway":"Nuclear export deregulation","Drug_status":"APR-548 combo (clinical)","Ferroptosis":"SLC7A11 upregulated"},
345
- {"Variant":"DNMT3A p.R882H","Freq_pct":18,"Pathway":"Epigenetic dysregulation","Drug_status":"Azacitidine (approved)","Ferroptosis":"ACSL4 altered"},
346
- {"Variant":"CEBPA biallelic","Freq_pct":8,"Pathway":"Myeloid differentiation block","Drug_status":"Novel target","Ferroptosis":"NRF2 pathway"},
347
- {"Variant":"IDH1/2 mutation","Freq_pct":15,"Pathway":"2-HG oncometabolite → TET2 inhibition","Drug_status":"Enasidenib (approved)","Ferroptosis":"Iron metabolism disrupted"},
348
- ]
349
- PAML_BM_LNP = [
350
- {"Formulation":"ALC-0315-DSPC-Chol-PEG","BM_protein":"ApoE + Clusterin","Size_nm":98,"Zeta_mV":-3.5,"Marrow_uptake_pct":34,"Priority":"HIGH"},
351
- {"Formulation":"MC3-DOPE-Chol-PEG","BM_protein":"Fibronectin dominant","Size_nm":105,"Zeta_mV":-4.2,"Marrow_uptake_pct":28,"Priority":"HIGH"},
352
- {"Formulation":"DLin-MC3-DPPC","BM_protein":"Vitronectin-rich","Size_nm":91,"Zeta_mV":-2.9,"Marrow_uptake_pct":19,"Priority":"MEDIUM"},
353
- {"Formulation":"Cationic-DOTAP-Chol","BM_protein":"Opsonin-heavy","Size_nm":132,"Zeta_mV":+8.1,"Marrow_uptake_pct":8,"Priority":"LOW"},
354
- ]
355
 
356
  def dipg_variants(sort_by):
357
  df = pd.DataFrame(DIPG_VARIANTS).sort_values(
@@ -360,21 +412,24 @@ def dipg_variants(sort_by):
360
  return df
361
 
362
  def dipg_csf(peg, size):
363
- df = pd.DataFrame(DIPG_CSF_LNP)
364
- df["Score"] = df["ApoE_pct"]/40 + df["BBB_est"] - abs(df["Size_nm"]-size)/200
365
- df = df.sort_values("Score", ascending=False)
366
- fig, ax = plt.subplots(figsize=(6, 3), facecolor=CARD)
367
- ax.set_facecolor(CARD)
368
- colors = [GRN if p=="HIGH" else ACC if p=="MEDIUM" else RED for p in df["Priority"]]
369
- ax.barh(df["Formulation"], df["ApoE_pct"], color=colors)
370
- ax.set_xlabel("ApoE% in CSF corona", color=TXT)
371
- ax.tick_params(colors=TXT, labelsize=8)
372
- for sp in ax.spines.values(): sp.set_edgecolor(BORDER)
373
- ax.set_title("DIPG CSF LNP formulations (ApoE%)", color=TXT, fontsize=9)
374
- plt.tight_layout()
375
- buf = BytesIO(); plt.savefig(buf, format="png", dpi=120, facecolor=CARD); plt.close(); buf.seek(0)
376
- log_entry("S1-F · R1a · DIPG CSF", f"peg={peg},size={size}", "formulation ranking")
377
- return df[["Formulation","Size_nm","Zeta_mV","ApoE_pct","BBB_est","Priority"]], Image.open(buf)
 
 
 
378
 
379
  def uvm_variants():
380
  df = pd.DataFrame(UVM_VARIANTS)
@@ -382,56 +437,62 @@ def uvm_variants():
382
  return df
383
 
384
  def uvm_vitreous():
385
- df = pd.DataFrame(UVM_VITREOUS_LNP)
386
- fig, ax = plt.subplots(figsize=(6, 3), facecolor=CARD)
387
- ax.set_facecolor(CARD)
388
- colors = [GRN if p=="HIGH" else ACC if p=="MEDIUM" else RED for p in df["Priority"]]
389
- ax.barh(df["Formulation"], df["Retention_h"], color=colors)
390
- ax.set_xlabel("Vitreous retention (hours)", color=TXT)
391
- ax.tick_params(colors=TXT, labelsize=8)
392
- for sp in ax.spines.values(): sp.set_edgecolor(BORDER)
393
- ax.set_title("UVM LNP retention in vitreous humor", color=TXT, fontsize=9)
394
- plt.tight_layout()
395
- buf = BytesIO(); plt.savefig(buf, format="png", dpi=120, facecolor=CARD); plt.close(); buf.seek(0)
396
- log_entry("S1-F · R2a · UVM Vitreous", "load", "vitreous LNP ranking")
397
- return df, Image.open(buf)
 
 
 
398
 
399
  def paml_ferroptosis(variant):
400
- row = next((r for r in PAML_VARIANTS if variant in r["Variant"]), PAML_VARIANTS[0])
401
- ferr_map = {"GPX4 suppressed": 0.85, "SLC7A11 upregulated": 0.72,
402
- "ACSL4 altered": 0.61, "NRF2 pathway": 0.55, "Iron metabolism disrupted": 0.78}
403
- ferr_score = ferr_map.get(row["Ferroptosis"], 0.5)
404
- cats = ["Ferroptosis\nsensitivity", "Drug\navailable", "BM niche\ncoverage", "Data\nmaturity", "Target\nnovelty"]
405
- has_drug = 0.9 if row["Drug_status"] not in ["Novel target"] else 0.3
406
- vals = [ferr_score, has_drug, 0.6, 0.55, 1-has_drug+0.2]
407
- angles = np.linspace(0, 2*np.pi, len(cats), endpoint=False).tolist()
408
- v2, a2 = vals+[vals[0]], angles+[angles[0]]
409
- fig, ax = plt.subplots(figsize=(5, 4), subplot_kw={"polar":True}, facecolor=CARD)
410
- ax.set_facecolor(CARD)
411
- ax.plot(a2, v2, color=ACC2, linewidth=2); ax.fill(a2, v2, color=ACC2, alpha=0.2)
412
- ax.set_xticks(angles); ax.set_xticklabels(cats, color=TXT, fontsize=8)
413
- ax.tick_params(colors=TXT)
414
- ax.set_title(f"pAML · {row['Variant'][:20]}", color=TXT, fontsize=9)
415
- plt.tight_layout()
416
- buf = BytesIO(); plt.savefig(buf, format="png", dpi=120, facecolor=CARD); plt.close(); buf.seek(0)
417
- log_entry("S1-F · R3a · pAML", variant, f"ferr={ferr_score:.2f}")
418
- _v = row["Variant"]
419
- _p = row["Pathway"]
420
- _d = row["Drug_status"]
421
- _f = row["Ferroptosis"]
422
- _fs = f"{ferr_score:.2f}"
423
- summary = (
424
- f"<div style='background:{CARD};padding:14px;border-radius:8px;font-family:sans-serif;'>"
425
- f"<p style='color:{DIM};font-size:11px;margin:0 0 6px'>S1-F · R3a · pAML</p>"
426
- f"<b style='color:{ACC2};font-size:15px'>{_v}</b><br>"
427
- f"<p style='color:{TXT};margin:6px 0'><b>Pathway:</b> {_p}</p>"
428
- f"<p style='color:{TXT};margin:0'><b>Drug:</b> {_d}</p>"
429
- f"<p style='color:{TXT};margin:6px 0'><b>Ferroptosis link:</b> {_f}</p>"
430
- f"<p style='color:{TXT}'><b>Ferroptosis sensitivity score:</b> "
431
- f"<span style='color:{ACC};font-size:18px'>{_fs}</span></p>"
432
- f"<p style='font-size:11px;color:{DIM}'>Research only. Not clinical advice.</p></div>"
433
- )
434
- return summary, Image.open(buf)
 
 
 
435
 
436
  # ========== ДОПОМІЖНІ ФУНКЦІЇ ==========
437
  def section_header(code, name, tagline, projects_html):
@@ -457,6 +518,80 @@ def proj_badge(code, title, metric=""):
457
  f"</div>"
458
  )
459
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
460
  # ========== CSS ==========
461
  css = f"""
462
  body, .gradio-container {{ background: {BG} !important; color: {TXT} !important; }}
@@ -549,7 +684,7 @@ with gr.Blocks(css=css, title="K R&D Lab · S1 Biomedical") as demo:
549
  with gr.TabItem("🗺️ Lab Map"):
550
  gr.HTML(MAP_HTML)
551
 
552
- # === АКТИВНІ ВКЛАДКИ (працюють) ===
553
  with gr.TabItem("S1-A · R1a · OpenVariant"):
554
  gr.HTML(proj_badge("S1-A · R1a", "OpenVariant — SNV Pathogenicity Classifier", "AUC = 0.939"))
555
  hgvs = gr.Textbox(label="HGVS notation", placeholder="BRCA1:p.R1699Q")
@@ -565,22 +700,11 @@ with gr.Blocks(css=css, title="K R&D Lab · S1 Biomedical") as demo:
565
  ["BRCA2:p.D2723A",0.01,0.98,0.0]], inputs=[hgvs,sift,pp,gn], cache_examples=False)
566
  b_v.click(predict_variant, [hgvs,sift,pp,gn], o_v)
567
 
568
- # === ВСІ ІНШІ ВКЛАДКИ ЗАКОМЕНТОВАНІ ===
569
- # Розкоментуйте блок цілком, щоб перевірити, чи викликає помилку
570
-
571
- # =============================================================================
572
- # === START: S1-A · R1b · Somatic Classifier 🔶 ===============================
573
- # =============================================================================
574
  with gr.TabItem("S1-A · R1b · Somatic Classifier 🔶"):
575
  gr.HTML(proj_badge("S1-A · R1b", "Somatic Mutation Classifier", "🔶 In progress"))
576
  gr.Markdown("> This module is in active development. Coming in the next release.")
577
- # =============================================================================
578
- # === END: S1-A · R1b =========================================================
579
- # =============================================================================
580
 
581
- # =============================================================================
582
- # === START: S1-B · R1a · BRCA2 miRNA ========================================
583
- # =============================================================================
584
  with gr.TabItem("S1-B · R1a · BRCA2 miRNA"):
585
  gr.HTML(proj_badge("S1-B · R1a", "miRNA Silencing — BRCA1/2 · TP53"))
586
  g1 = gr.Dropdown(["BRCA2","BRCA1","TP53"], value="BRCA2", label="Gene")
@@ -588,13 +712,7 @@ with gr.Blocks(css=css, title="K R&D Lab · S1 Biomedical") as demo:
588
  o1 = gr.Dataframe(label="Top 5 downregulated miRNAs")
589
  gr.Examples([["BRCA2"],["BRCA1"],["TP53"]], inputs=[g1])
590
  b1.click(predict_mirna, [g1], o1)
591
- # =============================================================================
592
- # === END: S1-B · R1a =========================================================
593
- # =============================================================================
594
 
595
- # =============================================================================
596
- # === START: S1-B · R2a · TP53 siRNA =========================================
597
- # =============================================================================
598
  with gr.TabItem("S1-B · R2a · TP53 siRNA"):
599
  gr.HTML(proj_badge("S1-B · R2a", "siRNA Synthetic Lethal — TP53-null"))
600
  g2 = gr.Dropdown(["LUAD","BRCA","COAD"], value="LUAD", label="Cancer type")
@@ -602,73 +720,40 @@ with gr.Blocks(css=css, title="K R&D Lab · S1 Biomedical") as demo:
602
  o2 = gr.Dataframe(label="Top 5 synthetic lethal targets")
603
  gr.Examples([["LUAD"],["BRCA"],["COAD"]], inputs=[g2], cache_examples=False)
604
  b2.click(predict_sirna, [g2], o2)
605
- # =============================================================================
606
- # === END: S1-B · R2a =========================================================
607
- # =============================================================================
608
 
609
- # =============================================================================
610
- # === START: S1-B · R3a · lncRNA-TREM2 =======================================
611
- # =============================================================================
612
  with gr.TabItem("S1-B · R3a · lncRNA-TREM2"):
613
  gr.HTML(proj_badge("S1-B · R3a", "lncRNA-TREM2 ceRNA Network"))
614
  b3 = gr.Button("Load Network", variant="primary")
615
  o3 = gr.Dataframe(label="ceRNA Network")
616
  b3.click(get_lncrna, [], o3)
617
- # =============================================================================
618
- # === END: S1-B · R3a =========================================================
619
- # =============================================================================
620
 
621
- # =============================================================================
622
- # === START: S1-B · R3b · ASO Designer =======================================
623
- # =============================================================================
624
  with gr.TabItem("S1-B · R3b · ASO Designer"):
625
  gr.HTML(proj_badge("S1-B · R3b", "ASO Candidates"))
626
  b4 = gr.Button("Show ASOs", variant="primary")
627
  o4 = gr.Dataframe(label="ASO Candidates")
628
  b4.click(get_aso, [], o4)
629
- # =============================================================================
630
- # === END: S1-B · R3b =========================================================
631
- # =============================================================================
632
-
633
- # =============================================================================
634
- # === START: S1-C · R1a · FGFR3 RNA Drug ===================================== оце
635
- # =============================================================================
636
- # with gr.TabItem("S1-C · R1a · FGFR3 RNA Drug"):
637
- # gr.HTML(proj_badge("S1-C · R1a", "FGFR3 RNA-Directed Drug Discovery", "top score 0.793"))
638
- # g4 = gr.Radio(["P1 (hairpin loop)","P10 (G-quadruplex)"],
639
- # value="P1 (hairpin loop)", label="Target pocket")
640
- # b4 = gr.Button("Screen Compounds", variant="primary")
641
- # o4t = gr.Dataframe(label="Top 5 candidates")
642
- # o4p = gr.Image(label="Binding scores")
643
- # gr.Examples([["P1 (hairpin loop)"],["P10 (G-quadruplex)"]], inputs=[g4])
644
- # b4.click(predict_drug, [g4], [o4t, o4p])
645
- # =============================================================================
646
- # === END: S1-C · R1a =========================================================
647
- # =============================================================================
648
-
649
- # =============================================================================
650
- # === START: S1-C · R1b · SL Drug Mapping 🔶 =================================
651
- # =============================================================================
652
  with gr.TabItem("S1-C · R1b · SL Drug Mapping 🔶"):
653
  gr.HTML(proj_badge("S1-C · R1b", "Synthetic Lethal Drug Mapping", "🔶 In progress"))
654
  gr.Markdown("> Coming soon.")
655
- # =============================================================================
656
- # === END: S1-C · R1b =========================================================
657
- # =============================================================================
658
 
659
- # =============================================================================
660
- # === START: S1-C · R2a · Frontier 🔴 ========================================
661
- # =============================================================================
662
  with gr.TabItem("S1-C · R2a · Frontier 🔴"):
663
  gr.HTML(proj_badge("S1-C · R2a", "m6A × Ferroptosis × Circadian", "🔴 Frontier"))
664
  gr.Markdown("> Planned for Q3 2026")
665
- # =============================================================================
666
- # === END: S1-C · R2a =========================================================
667
- # =============================================================================
668
 
669
- # =============================================================================
670
- # === START: S1-D · R1a · LNP Corona =========================================
671
- # =============================================================================
672
  with gr.TabItem("S1-D · R1a · LNP Corona"):
673
  gr.HTML(proj_badge("S1-D · R1a", "LNP Protein Corona (Serum)", "AUC = 0.791"))
674
  with gr.Row():
@@ -681,183 +766,167 @@ with gr.Blocks(css=css, title="K R&D Lab · S1 Biomedical") as demo:
681
  o6 = gr.Markdown()
682
  gr.Examples([[100,-5,1.5,"Ionizable"],[80,5,0.5,"Cationic"]], inputs=[sz,zt,pg,lp])
683
  b6.click(predict_corona, [sz,zt,pg,lp], o6)
684
- # =============================================================================
685
- # === END: S1-D · R1a =========================================================
686
- # =============================================================================
687
-
688
- # =============================================================================
689
- # === START: S1-D · R2a · Flow Corona ========================================
690
- # =============================================================================
691
- # with gr.TabItem("S1-D · R2a · Flow Corona"):
692
- # gr.HTML(proj_badge("S1-D · R2a", "Flow Corona — Vroman Effect"))
693
- # with gr.Row():
694
- # s8 = gr.Slider(50,300,value=100,step=1,label="Size (nm)")
695
- # z8 = gr.Slider(-40,10,value=-5,step=1,label="Zeta (mV)")
696
- # pg8 = gr.Slider(0,5,value=1.5,step=0.1,label="PEG mol%")
697
- # with gr.Row():
698
- # ch8 = gr.Dropdown(["Ionizable","Cationic","Anionic","Neutral"],value="Ionizable",label="Charge")
699
- # fl8 = gr.Slider(0,40,value=20,step=1,label="Flow cm/s")
700
- # b8 = gr.Button("Model Vroman Effect", variant="primary")
701
- # o8t = gr.Markdown(); o8p = gr.Image(label="Kinetics")
702
- # gr.Examples([[100,-5,1.5,"Ionizable",40],[150,5,0.5,"Cationic",10]], inputs=[s8,z8,pg8,ch8,fl8])
703
- # b8.click(predict_flow, [s8,z8,pg8,ch8,fl8], [o8t,o8p])
704
- # =============================================================================
705
- # === END: S1-D · R2a =========================================================
706
- # =============================================================================
707
-
708
- # =============================================================================
709
- # === START: S1-D · R3a · LNP Brain ========================================== оце
710
- # =============================================================================
711
- # with gr.TabItem("S1-D · R3a · LNP Brain"):
712
- # gr.HTML(proj_badge("S1-D · R3a", "LNP Brain Delivery — ApoE% + BBB probability"))
713
- # smi = gr.Textbox(label="Ionizable lipid SMILES",
714
- # value="CC(C)CC(=O)OCC(COC(=O)CC(C)C)OC(=O)CC(C)C")
715
- # with gr.Row():
716
- # pk = gr.Slider(4,8,value=6.5,step=0.1,label="pKa")
717
- # zt9 = gr.Slider(-20,10,value=-3,step=1,label="Zeta (mV)")
718
- # b9 = gr.Button("Predict BBB Crossing", variant="primary")
719
- # o9t = gr.Markdown(); o9p = gr.Image(label="Radar profile")
720
- # gr.Examples([["CC(C)CC(=O)OCC(COC(=O)CC(C)C)OC(=O)CC(C)C",6.5,-3]], inputs=[smi,pk,zt9])
721
- # b9.click(predict_bbb, [smi,pk,zt9], [o9t,o9p])
722
- # =============================================================================
723
- # === END: S1-D · R3a =========================================================
724
- # =============================================================================
725
-
726
- # =============================================================================
727
- # === START: S1-D · R4a · AutoCorona NLP =====================================
728
- # =============================================================================
729
- # with gr.TabItem("S1-D · R4a · AutoCorona NLP"):
730
- # gr.HTML(proj_badge("S1-D · R4a", "AutoCorona NLP — from abstracts", "F1 = 0.71"))
731
- # txt = gr.Textbox(lines=5,label="Paper abstract",placeholder="Paste abstract here...")
732
- # b10 = gr.Button("Extract Data", variant="primary")
733
- # o10j = gr.Code(label="Extracted JSON", language="json")
734
- # o10f = gr.Textbox(label="Validation flags")
735
- # gr.Examples([[
736
- # "LNPs composed of MC3, DSPC, Cholesterol (50:10:40 mol%) with 1.5% PEG-DMG. "
737
- # "Hydrodynamic diameter was 98 nm, zeta potential -3.2 mV, PDI 0.12. "
738
- # "Incubated in human plasma. Corona: albumin, apolipoprotein E, fibrinogen."
739
- # ]], inputs=[txt])
740
- # b10.click(extract_corona, txt, [o10j, o10f])
741
- # =============================================================================
742
- # === END: S1-D · R4a =========================================================
743
- # =============================================================================
744
-
745
- # =============================================================================
746
- # === START: S1-D · R5a · CSF/BM 🔴 ==========================================
747
- # =============================================================================
748
  with gr.TabItem("S1-D · R5a · CSF/BM 🔴"):
749
  gr.HTML(proj_badge("S1-D · R5a", "LNP Corona in CSF · Vitreous · Bone Marrow", "🔴 0 prior studies"))
750
  gr.Markdown("> Planned for Q2–Q3 2026")
751
- # =============================================================================
752
- # === END: S1-D · R5a =========================================================
753
- # =============================================================================
754
-
755
- # =============================================================================
756
- # === START: S1-E · R1a · Liquid Biopsy ====================================== оце
757
- # =============================================================================
758
- # with gr.TabItem("S1-E · R1a · Liquid Biopsy"):
759
- # gr.HTML(proj_badge("S1-E · R1a", "Liquid Biopsy Classifier", "AUC = 0.992*"))
760
- # with gr.Row():
761
- # p1=gr.Slider(-3,3,value=0,step=0.1,label="CTHRC1")
762
- # p2=gr.Slider(-3,3,value=0,step=0.1,label="FHL2")
763
- # p3=gr.Slider(-3,3,value=0,step=0.1,label="LDHA")
764
- # p4=gr.Slider(-3,3,value=0,step=0.1,label="P4HA1")
765
- # p5=gr.Slider(-3,3,value=0,step=0.1,label="SERPINH1")
766
- # with gr.Row():
767
- # p6=gr.Slider(-3,3,value=0,step=0.1,label="ABCA8")
768
- # p7=gr.Slider(-3,3,value=0,step=0.1,label="CA4")
769
- # p8=gr.Slider(-3,3,value=0,step=0.1,label="CKB")
770
- # p9=gr.Slider(-3,3,value=0,step=0.1,label="NNMT")
771
- # p10=gr.Slider(-3,3,value=0,step=0.1,label="CACNA2D2")
772
- # b7=gr.Button("Classify", variant="primary")
773
- # o7t=gr.HTML(); o7p=gr.Image(label="Feature contributions")
774
- # gr.Examples([[2,2,1.5,1.8,1.6,-1,-1.2,-0.8,1.4,-1.1],[0]*10],
775
- # inputs=[p1,p2,p3,p4,p5,p6,p7,p8,p9,p10])
776
- # b7.click(predict_cancer, [p1,p2,p3,p4,p5,p6,p7,p8,p9,p10], [o7t,o7p])
777
- # =============================================================================
778
- # === END: S1-E · R1a =========================================================
779
- # =============================================================================
780
-
781
- # =============================================================================
782
- # === START: S1-E · R1b · Validator 🔶 =======================================
783
- # =============================================================================
784
  with gr.TabItem("S1-E · R1b · Validator 🔶"):
785
  gr.HTML(proj_badge("S1-E · R1b", "Protein Panel Validator", "🔶 In progress"))
786
  gr.Markdown("> Coming next.")
787
- # =============================================================================
788
- # === END: S1-E · R1b =========================================================
789
- # =============================================================================
790
-
791
- # =============================================================================
792
- # === START: S1-F · R1a · DIPG Toolkit ======================================= оце
793
- # =============================================================================
794
- # with gr.TabItem("S1-F · R1a · DIPG Toolkit"):
795
- # gr.HTML(proj_badge("S1-F · R1a", "DIPG: H3K27M + CSF LNP + Circadian", "PBTA"))
796
- # sort_d = gr.Radio(["Frequency", "Drug status"], value="Frequency", label="Sort by")
797
- # b_dv = gr.Button("Load DIPG Variants", variant="primary")
798
- # o_dv = gr.Dataframe(label="H3K27M co-mutations")
799
- # b_dv.click(dipg_variants, [sort_d], o_dv)
800
- # gr.Markdown("---")
801
- # with gr.Row():
802
- # d_peg = gr.Slider(0.5, 3.0, value=1.5, step=0.1, label="PEG mol%")
803
- # d_size = gr.Slider(60, 150, value=90, step=5, label="Target size (nm)")
804
- # b_dc = gr.Button("Rank CSF Formulations", variant="primary")
805
- # o_dct = gr.Dataframe(label="CSF LNP ranking")
806
- # o_dcp = gr.Image(label="ApoE% in CSF corona")
807
- # b_dc.click(dipg_csf, [d_peg, d_size], [o_dct, o_dcp])
808
- # =============================================================================
809
- # === END: S1-F · R1a =========================================================
810
- # =============================================================================
811
-
812
- # =============================================================================
813
- # === START: S1-F · R2a · UVM Toolkit ======================================== оце
814
- # =============================================================================
815
- # with gr.TabItem("S1-F · R2a · UVM Toolkit"):
816
- # gr.HTML(proj_badge("S1-F · R2a", "UVM: GNAQ/GNA11 + vitreous + m6A", "TCGA-UVM"))
817
- # b_uv = gr.Button("Load UVM Variants", variant="primary")
818
- # o_uv = gr.Dataframe(label="GNAQ/GNA11 map")
819
- # b_uv.click(uvm_variants, [], o_uv)
820
- # b_uw = gr.Button("Rank Vitreous Formulations", variant="primary")
821
- # o_uwt = gr.Dataframe(label="Vitreous LNP retention ranking")
822
- # o_uwp = gr.Image(label="Retention (hours)")
823
- # b_uw.click(uvm_vitreous, [], [o_uwt, o_uwp])
824
- # =============================================================================
825
- # === END: S1-F · R2a =========================================================
826
- # =============================================================================
827
-
828
- # =============================================================================
829
- # === START: S1-F · R3a · pAML Toolkit ======================================= оце
830
- # =============================================================================
831
- # with gr.TabItem("S1-F · R3a · pAML Toolkit"):
832
- # gr.HTML(proj_badge("S1-F · R3a", "pAML: FLT3-ITD + BM niche + ferroptosis", "TARGET-AML"))
833
- # var_sel = gr.Dropdown(
834
- # ["FLT3-ITD", "NPM1 c.860_863dupTCAG", "DNMT3A p.R882H",
835
- # "CEBPA biallelic", "IDH1/2 mutation"],
836
- # value="FLT3-ITD", label="Select variant"
837
- # )
838
- # b_pf = gr.Button("Analyze Ferroptosis Profile", variant="primary")
839
- # o_pft = gr.HTML()
840
- # o_pfp = gr.Image(label="Target radar")
841
- # b_pf.click(paml_ferroptosis, var_sel, [o_pft, o_pfp])
842
- # =============================================================================
843
- # === END: S1-F · R3a =========================================================
844
- # =============================================================================
845
-
846
- # 📓 Journal
 
 
 
 
 
 
 
 
 
 
 
847
  with gr.TabItem("📓 Journal"):
848
  gr.Markdown("### Lab Journal")
849
  with gr.Row():
850
  note_text = gr.Textbox(label="📝 Observation", placeholder="What did you discover?", lines=3)
851
  note_tab = gr.Textbox(label="Project code (e.g. S1-A·R1a)", value="General")
852
- note_last = gr.Textbox(visible=False)
853
- save_btn = gr.Button("💾 Save", variant="primary")
854
- save_msg = gr.Markdown()
855
- journal_df = gr.Dataframe(label="📋 Full History", value=load_journal(), interactive=False)
856
  refresh_btn = gr.Button("🔄 Refresh")
857
- refresh_btn.click(load_journal, [], journal_df)
858
- save_btn.click(save_note, [note_text,note_tab,note_last], [save_msg,journal_df])
 
 
 
859
 
860
- # 📚 Learning
861
  with gr.TabItem("📚 Learning"):
862
  gr.Markdown("""
863
  ## 🧪 Guided Investigations
@@ -868,7 +937,7 @@ with gr.Blocks(css=css, title="K R&D Lab · S1 Biomedical") as demo:
868
  **S1-D · R2a** Flow Corona – compare flow 0 vs 40 cm/s
869
  **S1-B · R2a** siRNA – count "Novel" targets across cancer types
870
  **S1-E · R1a** Liquid Biopsy – find minimal signal for CANCER
871
- ... (more cases)
872
  """)
873
 
874
  gr.Markdown(
 
9
  from PIL import Image
10
  from datetime import datetime
11
  from pathlib import Path
12
+ import plotly.graph_objects as go
13
+ import plotly.express as px
14
 
15
  # ========== Діагностичний друк ==========
16
  print("Gradio version:", gr.__version__)
 
45
  try:
46
  if not LOG_PATH.exists():
47
  return pd.DataFrame(columns=["timestamp","tab","inputs","result","note"])
48
+ df = pd.read_csv(LOG_PATH)
49
+ # Convert to markdown for nice display
50
+ if df.empty:
51
+ return "No entries yet."
52
+ return df.tail(20).to_markdown(index=False)
53
  except Exception:
54
+ return "Error loading journal."
55
 
56
+ def save_note(note, tab):
57
+ log_entry(tab, "manual note", note, note)
58
+ return load_journal()
59
+
60
+ def clear_journal():
61
+ try:
62
+ if LOG_PATH.exists():
63
+ LOG_PATH.unlink()
64
+ return "Journal cleared."
65
+ except Exception:
66
+ return "Error clearing journal."
67
 
68
  # ========== БАЗИ ДАНИХ ==========
69
  MIRNA_DB = {
 
166
  "clusterin","igm","iga","igg","complement","transferrin",
167
  "alpha-2-macroglobulin"]
168
 
169
+ # ---------- S1-F RARE ----------
170
+ DIPG_VARIANTS = [
171
+ {"Variant":"H3K27M (H3F3A)","Freq_pct":78,"Pathway":"PRC2 inhibition → global H3K27me3 loss","Drug_status":"ONC201 (clinical)","Circadian_gene":"BMAL1 suppressed"},
172
+ {"Variant":"ACVR1 p.R206H","Freq_pct":21,"Pathway":"BMP/SMAD hyperactivation","Drug_status":"LDN-193189 (preclinical)","Circadian_gene":"PER1 disrupted"},
173
+ {"Variant":"PIK3CA p.H1047R","Freq_pct":15,"Pathway":"PI3K/AKT/mTOR","Drug_status":"Copanlisib (clinical)","Circadian_gene":"CRY1 altered"},
174
+ {"Variant":"TP53 p.R248W","Freq_pct":14,"Pathway":"DNA damage response loss","Drug_status":"APR-246 (clinical)","Circadian_gene":"p53-CLOCK axis"},
175
+ {"Variant":"PDGFRA amp","Freq_pct":13,"Pathway":"RTK/RAS signalling","Drug_status":"Avapritinib (clinical)","Circadian_gene":"REV-ERB altered"},
176
+ ]
177
+ DIPG_CSF_LNP = [
178
+ {"Formulation":"MC3-DSPC-Chol-PEG","Size_nm":92,"Zeta_mV":-4.1,"CSF_protein":"Beta2-microglobulin","ApoE_pct":12.4,"BBB_est":0.41,"Priority":"HIGH"},
179
+ {"Formulation":"DLin-KC2-DSPE-PEG","Size_nm":87,"Zeta_mV":-3.8,"CSF_protein":"Cystatin C","ApoE_pct":14.1,"BBB_est":0.47,"Priority":"HIGH"},
180
+ {"Formulation":"C12-200-DOPE-PEG","Size_nm":103,"Zeta_mV":-5.2,"CSF_protein":"Albumin (low)","ApoE_pct":9.8,"BBB_est":0.33,"Priority":"MEDIUM"},
181
+ {"Formulation":"DODAP-DSPC-Chol","Size_nm":118,"Zeta_mV":-2.1,"CSF_protein":"Transferrin","ApoE_pct":7.2,"BBB_est":0.24,"Priority":"LOW"},
182
+ ]
183
+
184
+ UVM_VARIANTS = [
185
+ {"Variant":"GNAQ p.Q209L","Freq_pct":46,"Pathway":"PLCβ → PKC → MAPK","Drug_status":"Darovasertib (clinical)","m6A_writer":"METTL3 upregulated"},
186
+ {"Variant":"GNA11 p.Q209L","Freq_pct":32,"Pathway":"PLCβ → PKC → MAPK","Drug_status":"Darovasertib (clinical)","m6A_writer":"WTAP upregulated"},
187
+ {"Variant":"BAP1 loss","Freq_pct":47,"Pathway":"Chromatin remodeling → metastasis","Drug_status":"No approved (HDAC trials)","m6A_writer":"FTO overexpressed"},
188
+ {"Variant":"SF3B1 p.R625H","Freq_pct":19,"Pathway":"Splicing alteration → neoepitopes","Drug_status":"H3B-8800 (clinical)","m6A_writer":"METTL14 altered"},
189
+ {"Variant":"EIF1AX p.A113_splice","Freq_pct":14,"Pathway":"Translation initiation","Drug_status":"Novel — no drug","m6A_writer":"YTHDF2 suppressed"},
190
+ ]
191
+ UVM_VITREOUS_LNP = [
192
+ {"Formulation":"SM-102-DSPC-Chol-PEG","Vitreal_protein":"Hyaluronan-binding","Size_nm":95,"Zeta_mV":-3.2,"Retention_h":18,"Priority":"HIGH"},
193
+ {"Formulation":"Lipid-H-DOPE-PEG","Vitreal_protein":"Vitronectin dominant","Size_nm":88,"Zeta_mV":-4.0,"Retention_h":22,"Priority":"HIGH"},
194
+ {"Formulation":"DOTAP-DSPC-PEG","Vitreal_protein":"Albumin wash-out","Size_nm":112,"Zeta_mV":+2.1,"Retention_h":6,"Priority":"LOW"},
195
+ {"Formulation":"MC3-DPPC-Chol","Vitreal_protein":"Clusterin-rich","Size_nm":101,"Zeta_mV":-2.8,"Retention_h":14,"Priority":"MEDIUM"},
196
+ ]
197
+
198
+ PAML_VARIANTS = [
199
+ {"Variant":"FLT3-ITD","Freq_pct":25,"Pathway":"RTK constitutive activation → JAK/STAT","Drug_status":"Midostaurin (approved)","Ferroptosis":"GPX4 suppressed"},
200
+ {"Variant":"NPM1 c.860_863dupTCAG","Freq_pct":30,"Pathway":"Nuclear export deregulation","Drug_status":"APR-548 combo (clinical)","Ferroptosis":"SLC7A11 upregulated"},
201
+ {"Variant":"DNMT3A p.R882H","Freq_pct":18,"Pathway":"Epigenetic dysregulation","Drug_status":"Azacitidine (approved)","Ferroptosis":"ACSL4 altered"},
202
+ {"Variant":"CEBPA biallelic","Freq_pct":8,"Pathway":"Myeloid differentiation block","Drug_status":"Novel target","Ferroptosis":"NRF2 pathway"},
203
+ {"Variant":"IDH1/2 mutation","Freq_pct":15,"Pathway":"2-HG oncometabolite → TET2 inhibition","Drug_status":"Enasidenib (approved)","Ferroptosis":"Iron metabolism disrupted"},
204
+ ]
205
+ PAML_BM_LNP = [
206
+ {"Formulation":"ALC-0315-DSPC-Chol-PEG","BM_protein":"ApoE + Clusterin","Size_nm":98,"Zeta_mV":-3.5,"Marrow_uptake_pct":34,"Priority":"HIGH"},
207
+ {"Formulation":"MC3-DOPE-Chol-PEG","BM_protein":"Fibronectin dominant","Size_nm":105,"Zeta_mV":-4.2,"Marrow_uptake_pct":28,"Priority":"HIGH"},
208
+ {"Formulation":"DLin-MC3-DPPC","BM_protein":"Vitronectin-rich","Size_nm":91,"Zeta_mV":-2.9,"Marrow_uptake_pct":19,"Priority":"MEDIUM"},
209
+ {"Formulation":"Cationic-DOTAP-Chol","BM_protein":"Opsonin-heavy","Size_nm":132,"Zeta_mV":+8.1,"Marrow_uptake_pct":8,"Priority":"LOW"},
210
+ ]
211
+
212
+ # ========== ФУНКЦІЇ ПРЕДИКЦІЇ (з обробкою помилок) ==========
213
+
214
+ def safe_img_from_fig(fig):
215
+ """Convert matplotlib figure to PIL Image safely."""
216
+ try:
217
+ buf = BytesIO()
218
+ fig.savefig(buf, format="png", dpi=120, facecolor=CARD)
219
+ buf.seek(0)
220
+ img = Image.open(buf)
221
+ plt.close(fig)
222
+ return img
223
+ except Exception:
224
+ plt.close(fig)
225
+ # Return a blank image as fallback
226
+ return Image.new('RGB', (100, 100), color=CARD)
227
+
228
  def predict_mirna(gene):
229
  df = pd.DataFrame(MIRNA_DB.get(gene, []))
230
  log_entry("S1-B · R1a · miRNA", gene, f"{len(df)} miRNAs")
 
244
  return pd.DataFrame(ASO)
245
 
246
  def predict_drug(pocket):
247
+ try:
248
+ df = pd.DataFrame(FGFR3.get(pocket, []))
249
+ if df.empty:
250
+ return pd.DataFrame(), None
251
+ fig, ax = plt.subplots(figsize=(6, 4), facecolor=CARD)
252
+ ax.set_facecolor(CARD)
253
+ ax.barh(df["Compound"], df["Final_score"], color=ACC)
254
+ ax.set_xlabel("Final Score", color=TXT)
255
+ ax.tick_params(colors=TXT)
256
+ for sp in ax.spines.values(): sp.set_edgecolor(BORDER)
257
+ ax.set_title(f"Top compounds — {pocket}", color=TXT, fontsize=10)
258
+ plt.tight_layout()
259
+ img = safe_img_from_fig(fig)
260
+ log_entry("S1-C · R1a · FGFR3", pocket, f"Top: {df.iloc[0]['Compound'] if len(df) else 'none'}")
261
+ return df, img
262
+ except Exception as e:
263
+ log_entry("S1-C · R1a · FGFR3", pocket, f"Error: {str(e)}")
264
+ return pd.DataFrame(), None
265
 
266
  def predict_variant(hgvs, sift, polyphen, gnomad):
267
  hgvs = hgvs.strip()
 
290
  )
291
 
292
  def predict_corona(size, zeta, peg, lipid):
293
+ try:
294
+ score = 0
295
+ if lipid == "Ionizable": score += 2
296
+ elif lipid == "Cationic": score += 1
297
+ if abs(zeta) < 10: score += 1
298
+ if peg > 1.5: score += 2
299
+ if size < 100: score += 1
300
+ dominant = ["ApoE","Albumin","Fibrinogen","Vitronectin","ApoA-I"][min(score, 4)]
301
+ efficacy = "High" if score >= 4 else "Medium" if score >= 2 else "Low"
302
+ log_entry("S1-D · R1a · Corona", f"size={size},peg={peg}", f"dominant={dominant}")
303
+ return f"**Dominant corona protein:** {dominant}\n\n**Predicted efficacy:** {efficacy}\n\n**Score:** {score}/6"
304
+ except Exception as e:
305
+ return f"Error: {str(e)}"
306
 
307
  def predict_cancer(c1,c2,c3,c4,c5,c6,c7,c8,c9,c10):
308
+ try:
309
+ vals = [c1,c2,c3,c4,c5,c6,c7,c8,c9,c10]
310
+ names, weights = list(BM_W.keys()), list(BM_W.values())
311
+ raw = sum(v*w for v,w in zip(vals, weights))
312
+ prob = 1 / (1 + np.exp(-raw * 2))
313
+ label = "CANCER" if prob > 0.5 else "HEALTHY"
314
+ colour = RED if prob > 0.5 else GRN
315
+ contribs = [v*w for v,w in zip(vals, weights)]
316
+ fig, ax = plt.subplots(figsize=(6, 3.5), facecolor=CARD)
317
+ ax.set_facecolor(CARD)
318
+ ax.barh(names, contribs, color=[ACC if c > 0 else ACC2 for c in contribs])
319
+ ax.axvline(0, color=TXT, linewidth=0.8)
320
+ ax.set_xlabel("Contribution to cancer score", color=TXT)
321
+ ax.tick_params(colors=TXT, labelsize=8)
322
+ for sp in ax.spines.values(): sp.set_edgecolor(BORDER)
323
+ ax.set_title("Protein contributions", color=TXT, fontsize=10)
324
+ plt.tight_layout()
325
+ img = safe_img_from_fig(fig)
326
+ log_entry("S1-E · R1a · Liquid Biopsy", f"CTHRC1={c1},FHL2={c2}", f"{label} {prob:.2f}")
327
+ html_out = (
328
+ f"<div style=\'background:{CARD};padding:14px;border-radius:8px;font-family:sans-serif;\'>"
329
+ f"<p style=\'font-size:11px;color:{DIM};margin:0 0 6px\'>S1-E · R1a · Liquid Biopsy</p>"
330
+ f"<span style=\'color:{colour};font-size:24px;font-weight:bold\'>{label}</span><br>"
331
+ f"<span style=\'color:{TXT};font-size:14px\'>Probability: {prob:.2f}</span></div>"
332
+ )
333
+ return html_out, img
334
+ except Exception as e:
335
+ return f"<div style='color:{RED}'>Error: {str(e)}</div>", None
336
 
337
  def predict_flow(size, zeta, peg, charge, flow_rate):
338
+ try:
339
+ csi = round(min((flow_rate/40)*0.6 + (peg/5)*0.2 + (1 if charge=="Cationic" else 0)*0.2, 1.0), 3)
340
+ stability = "High remodeling" if csi > 0.6 else "Medium" if csi > 0.3 else "Stable"
341
+ t = np.linspace(0, 60, 200)
342
+ kf, ks = 0.03*(1+flow_rate/40), 0.038*(1+flow_rate/40)
343
+ fig, ax = plt.subplots(figsize=(6, 3.5), facecolor=CARD)
344
+ ax.set_facecolor(CARD)
345
+ ax.plot(t, 60*np.exp(-0.03*t)+20, color="#60a5fa", ls="--", label="Albumin (static)")
346
+ ax.plot(t, 60*np.exp(-kf*t)+10, color="#60a5fa", label="Albumin (flow)")
347
+ ax.plot(t, 14*(1-np.exp(-0.038*t))+5, color=ACC, ls="--", label="ApoE (static)")
348
+ ax.plot(t, 20*(1-np.exp(-ks*t))+5, color=ACC, label="ApoE (flow)")
349
+ ax.set_xlabel("Time (min)", color=TXT); ax.set_ylabel("% Corona", color=TXT)
350
+ ax.tick_params(colors=TXT); ax.legend(fontsize=7, labelcolor=TXT, facecolor=CARD)
351
+ for sp in ax.spines.values(): sp.set_edgecolor(BORDER)
352
+ ax.set_title("Vroman Effect — flow vs static", color=TXT, fontsize=9)
353
+ plt.tight_layout()
354
+ img = safe_img_from_fig(fig)
355
+ log_entry("S1-D · R2a · Flow Corona", f"flow={flow_rate}", f"CSI={csi}")
356
+ return f"**Corona Shift Index: {csi}** — {stability}", img
357
+ except Exception as e:
358
+ return f"Error: {str(e)}", None
359
 
360
  def predict_bbb(smiles, pka, zeta):
361
+ try:
362
+ logp = smiles.count("C")*0.3 - smiles.count("O")*0.5 + 1.5
363
+ apoe_pct = max(0, min(40, (7.0-pka)*8 + abs(zeta)*0.5 + logp*0.8))
364
+ bbb_prob = min(0.95, apoe_pct/30)
365
+ tier = "HIGH (>20%)" if apoe_pct > 20 else "MEDIUM (10-20%)" if apoe_pct > 10 else "LOW (<10%)"
366
+ cats = ["ApoE%","BBB","logP","pKa fit","Zeta"]
367
+ vals = [apoe_pct/40, bbb_prob, min(logp/5,1), (7-abs(pka-6.5))/7, (10-abs(zeta))/10]
368
+ angles = np.linspace(0, 2*np.pi, len(cats), endpoint=False).tolist()
369
+ v2, a2 = vals+[vals[0]], angles+[angles[0]]
370
+ fig, ax = plt.subplots(figsize=(5, 4), subplot_kw={"polar":True}, facecolor=CARD)
371
+ ax.set_facecolor(CARD)
372
+ ax.plot(a2, v2, color=ACC, linewidth=2); ax.fill(a2, v2, color=ACC, alpha=0.2)
373
+ ax.set_xticks(angles); ax.set_xticklabels(cats, color=TXT, fontsize=8)
374
+ ax.tick_params(colors=TXT)
375
+ plt.tight_layout()
376
+ img = safe_img_from_fig(fig)
377
+ log_entry("S1-D · R3a · LNP Brain", f"pka={pka},zeta={zeta}", f"ApoE={apoe_pct:.1f}%")
378
+ return f"**Predicted ApoE:** {apoe_pct:.1f}% — {tier}\n\n**BBB Probability:** {bbb_prob:.2f}", img
379
+ except Exception as e:
380
+ return f"Error: {str(e)}", None
381
 
382
  def extract_corona(text):
383
+ try:
384
+ out = {"nanoparticle_composition":"","size_nm":None,"zeta_mv":None,"PDI":None,
385
+ "protein_source":"","corona_proteins":[],"confidence":{}}
386
+ for pat, key in [(r"(\d+\.?\d*)\s*(?:nm|nanometer)","size_nm"),
387
+ (r"([+-]?\d+\.?\d*)\s*mV","zeta_mv"),
388
+ (r"PDI\s*[=:of]*\s*(\d+\.?\d*)","PDI")]:
389
+ m = re.search(pat, text, re.I)
390
+ if m: out[key] = float(m.group(1)); out["confidence"][key] = "HIGH"
391
+ for src in ["human plasma","human serum","fetal bovine serum","FBS","PBS"]:
392
+ if src.lower() in text.lower():
393
+ out["protein_source"] = src; out["confidence"]["protein_source"] = "HIGH"; break
394
+ out["corona_proteins"] = [{"name":p,"confidence":"MEDIUM"} for p in PROTEINS if p in text.lower()]
395
+ for lip in ["DSPC","DOPE","MC3","DLin","cholesterol","PEG","DOTAP"]:
396
+ if lip in text: out["nanoparticle_composition"] += lip + " "
397
+ out["nanoparticle_composition"] = out["nanoparticle_composition"].strip()
398
+ flags = []
399
+ if not out["size_nm"]: flags.append("size_nm not found")
400
+ if not out["zeta_mv"]: flags.append("zeta_mv not found")
401
+ if not out["corona_proteins"]: flags.append("no proteins detected")
402
+ summary = "All key fields extracted" if not flags else " | ".join(flags)
403
+ log_entry("S1-D · R4a · AutoCorona NLP", text[:80], f"proteins={len(out['corona_proteins'])}")
404
+ return json.dumps(out, indent=2), summary
405
+ except Exception as e:
406
+ return json.dumps({"error": str(e)}), "Extraction error"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
407
 
408
  def dipg_variants(sort_by):
409
  df = pd.DataFrame(DIPG_VARIANTS).sort_values(
 
412
  return df
413
 
414
  def dipg_csf(peg, size):
415
+ try:
416
+ df = pd.DataFrame(DIPG_CSF_LNP)
417
+ df["Score"] = df["ApoE_pct"]/40 + df["BBB_est"] - abs(df["Size_nm"]-size)/200
418
+ df = df.sort_values("Score", ascending=False)
419
+ fig, ax = plt.subplots(figsize=(6, 3), facecolor=CARD)
420
+ ax.set_facecolor(CARD)
421
+ colors = [GRN if p=="HIGH" else ACC if p=="MEDIUM" else RED for p in df["Priority"]]
422
+ ax.barh(df["Formulation"], df["ApoE_pct"], color=colors)
423
+ ax.set_xlabel("ApoE% in CSF corona", color=TXT)
424
+ ax.tick_params(colors=TXT, labelsize=8)
425
+ for sp in ax.spines.values(): sp.set_edgecolor(BORDER)
426
+ ax.set_title("DIPG — CSF LNP formulations (ApoE%)", color=TXT, fontsize=9)
427
+ plt.tight_layout()
428
+ img = safe_img_from_fig(fig)
429
+ log_entry("S1-F · R1a · DIPG CSF", f"peg={peg},size={size}", "formulation ranking")
430
+ return df[["Formulation","Size_nm","Zeta_mV","ApoE_pct","BBB_est","Priority"]], img
431
+ except Exception as e:
432
+ return pd.DataFrame(), None
433
 
434
  def uvm_variants():
435
  df = pd.DataFrame(UVM_VARIANTS)
 
437
  return df
438
 
439
  def uvm_vitreous():
440
+ try:
441
+ df = pd.DataFrame(UVM_VITREOUS_LNP)
442
+ fig, ax = plt.subplots(figsize=(6, 3), facecolor=CARD)
443
+ ax.set_facecolor(CARD)
444
+ colors = [GRN if p=="HIGH" else ACC if p=="MEDIUM" else RED for p in df["Priority"]]
445
+ ax.barh(df["Formulation"], df["Retention_h"], color=colors)
446
+ ax.set_xlabel("Vitreous retention (hours)", color=TXT)
447
+ ax.tick_params(colors=TXT, labelsize=8)
448
+ for sp in ax.spines.values(): sp.set_edgecolor(BORDER)
449
+ ax.set_title("UVM — LNP retention in vitreous humor", color=TXT, fontsize=9)
450
+ plt.tight_layout()
451
+ img = safe_img_from_fig(fig)
452
+ log_entry("S1-F · R2a · UVM Vitreous", "load", "vitreous LNP ranking")
453
+ return df, img
454
+ except Exception as e:
455
+ return pd.DataFrame(), None
456
 
457
  def paml_ferroptosis(variant):
458
+ try:
459
+ row = next((r for r in PAML_VARIANTS if variant in r["Variant"]), PAML_VARIANTS[0])
460
+ ferr_map = {"GPX4 suppressed": 0.85, "SLC7A11 upregulated": 0.72,
461
+ "ACSL4 altered": 0.61, "NRF2 pathway": 0.55, "Iron metabolism disrupted": 0.78}
462
+ ferr_score = ferr_map.get(row["Ferroptosis"], 0.5)
463
+ cats = ["Ferroptosis\nsensitivity", "Drug\navailable", "BM niche\ncoverage", "Data\nmaturity", "Target\nnovelty"]
464
+ has_drug = 0.9 if row["Drug_status"] not in ["Novel target"] else 0.3
465
+ vals = [ferr_score, has_drug, 0.6, 0.55, 1-has_drug+0.2]
466
+ angles = np.linspace(0, 2*np.pi, len(cats), endpoint=False).tolist()
467
+ v2, a2 = vals+[vals[0]], angles+[angles[0]]
468
+ fig, ax = plt.subplots(figsize=(5, 4), subplot_kw={"polar":True}, facecolor=CARD)
469
+ ax.set_facecolor(CARD)
470
+ ax.plot(a2, v2, color=ACC2, linewidth=2); ax.fill(a2, v2, color=ACC2, alpha=0.2)
471
+ ax.set_xticks(angles); ax.set_xticklabels(cats, color=TXT, fontsize=8)
472
+ ax.tick_params(colors=TXT)
473
+ ax.set_title(f"pAML · {row['Variant'][:20]}", color=TXT, fontsize=9)
474
+ plt.tight_layout()
475
+ img = safe_img_from_fig(fig)
476
+ log_entry("S1-F · R3a · pAML", variant, f"ferr={ferr_score:.2f}")
477
+ _v = row["Variant"]
478
+ _p = row["Pathway"]
479
+ _d = row["Drug_status"]
480
+ _f = row["Ferroptosis"]
481
+ _fs = f"{ferr_score:.2f}"
482
+ summary = (
483
+ f"<div style='background:{CARD};padding:14px;border-radius:8px;font-family:sans-serif;'>"
484
+ f"<p style='color:{DIM};font-size:11px;margin:0 0 6px'>S1-F · R3a · pAML</p>"
485
+ f"<b style='color:{ACC2};font-size:15px'>{_v}</b><br>"
486
+ f"<p style='color:{TXT};margin:6px 0'><b>Pathway:</b> {_p}</p>"
487
+ f"<p style='color:{TXT};margin:0'><b>Drug:</b> {_d}</p>"
488
+ f"<p style='color:{TXT};margin:6px 0'><b>Ferroptosis link:</b> {_f}</p>"
489
+ f"<p style='color:{TXT}'><b>Ferroptosis sensitivity score:</b> "
490
+ f"<span style='color:{ACC};font-size:18px'>{_fs}</span></p>"
491
+ f"<p style='font-size:11px;color:{DIM}'>Research only. Not clinical advice.</p></div>"
492
+ )
493
+ return summary, img
494
+ except Exception as e:
495
+ return f"<div style='color:{RED}'>Error: {str(e)}</div>", None
496
 
497
  # ========== ДОПОМІЖНІ ФУНКЦІЇ ==========
498
  def section_header(code, name, tagline, projects_html):
 
518
  f"</div>"
519
  )
520
 
521
+ # ========== 3D моделі (функції) ==========
522
+ def plot_nanoparticle(r, peg):
523
+ theta = np.linspace(0, 2*np.pi, 30)
524
+ phi = np.linspace(0, np.pi, 30)
525
+ theta, phi = np.meshgrid(theta, phi)
526
+ x = r * np.sin(phi) * np.cos(theta)
527
+ y = r * np.sin(phi) * np.sin(theta)
528
+ z = r * np.cos(phi)
529
+ fig = go.Figure(data=[go.Surface(x=x, y=y, z=z, colorscale='Blues', opacity=0.7)])
530
+ if peg > 0:
531
+ n_points = int(100 * peg)
532
+ u = np.random.uniform(0, 1, n_points)
533
+ v = np.random.uniform(0, 1, n_points)
534
+ theta_pts = 2 * np.pi * u
535
+ phi_pts = np.arccos(2*v - 1)
536
+ x_pts = (r + 0.5) * np.sin(phi_pts) * np.cos(theta_pts)
537
+ y_pts = (r + 0.5) * np.sin(phi_pts) * np.sin(theta_pts)
538
+ z_pts = (r + 0.5) * np.cos(phi_pts)
539
+ fig.add_scatter3d(x=x_pts, y=y_pts, z=z_pts, mode='markers',
540
+ marker=dict(size=3, color='red'), name='PEG')
541
+ fig.update_layout(
542
+ title=f"Nanoparticle (r={r} nm, PEG={peg})",
543
+ scene=dict(xaxis_title='X (nm)', yaxis_title='Y (nm)', zaxis_title='Z (nm)'),
544
+ width=500, height=400
545
+ )
546
+ return fig
547
+
548
+ def plot_dna():
549
+ t = np.linspace(0, 4*np.pi, 200)
550
+ x1 = np.cos(t)
551
+ y1 = np.sin(t)
552
+ z1 = t
553
+ x2 = np.cos(t + np.pi)
554
+ y2 = np.sin(t + np.pi)
555
+ z2 = t
556
+ fig = go.Figure()
557
+ fig.add_scatter3d(x=x1, y=y1, z=z1, mode='lines', line=dict(color='blue', width=5), name='Strand 1')
558
+ fig.add_scatter3d(x=x2, y=y2, z=z2, mode='lines', line=dict(color='red', width=5), name='Strand 2')
559
+ for i in range(0, len(t), 10):
560
+ fig.add_scatter3d(x=[x1[i], x2[i]], y=[y1[i], y2[i]], z=[z1[i], z2[i]],
561
+ mode='lines', line=dict(color='gray', width=1), showlegend=False)
562
+ fig.update_layout(
563
+ title='DNA Double Helix',
564
+ scene=dict(xaxis_title='X', yaxis_title='Y', zaxis_title='Z'),
565
+ width=500, height=400
566
+ )
567
+ return fig
568
+
569
+ def plot_corona():
570
+ r = 5
571
+ theta = np.linspace(0, 2*np.pi, 20)
572
+ phi = np.linspace(0, np.pi, 20)
573
+ theta, phi = np.meshgrid(theta, phi)
574
+ x = r * np.sin(phi) * np.cos(theta)
575
+ y = r * np.sin(phi) * np.sin(theta)
576
+ z = r * np.cos(phi)
577
+ fig = go.Figure(data=[go.Surface(x=x, y=y, z=z, colorscale='Blues', opacity=0.5)])
578
+ n_proteins = 50
579
+ u = np.random.uniform(0, 1, n_proteins)
580
+ v = np.random.uniform(0, 1, n_proteins)
581
+ theta_pts = 2 * np.pi * u
582
+ phi_pts = np.arccos(2*v - 1)
583
+ x_pts = (r + 1.5) * np.sin(phi_pts) * np.cos(theta_pts)
584
+ y_pts = (r + 1.5) * np.sin(phi_pts) * np.sin(theta_pts)
585
+ z_pts = (r + 1.5) * np.cos(phi_pts)
586
+ fig.add_scatter3d(x=x_pts, y=y_pts, z=z_pts, mode='markers',
587
+ marker=dict(size=5, color='orange'), name='Proteins')
588
+ fig.update_layout(
589
+ title='Protein Corona',
590
+ scene=dict(xaxis_title='X', yaxis_title='Y', zaxis_title='Z'),
591
+ width=500, height=400
592
+ )
593
+ return fig
594
+
595
  # ========== CSS ==========
596
  css = f"""
597
  body, .gradio-container {{ background: {BG} !important; color: {TXT} !important; }}
 
684
  with gr.TabItem("🗺️ Lab Map"):
685
  gr.HTML(MAP_HTML)
686
 
687
+ # === S1-A · PHYLO-GENOMICS ===
688
  with gr.TabItem("S1-A · R1a · OpenVariant"):
689
  gr.HTML(proj_badge("S1-A · R1a", "OpenVariant — SNV Pathogenicity Classifier", "AUC = 0.939"))
690
  hgvs = gr.Textbox(label="HGVS notation", placeholder="BRCA1:p.R1699Q")
 
700
  ["BRCA2:p.D2723A",0.01,0.98,0.0]], inputs=[hgvs,sift,pp,gn], cache_examples=False)
701
  b_v.click(predict_variant, [hgvs,sift,pp,gn], o_v)
702
 
 
 
 
 
 
 
703
  with gr.TabItem("S1-A · R1b · Somatic Classifier 🔶"):
704
  gr.HTML(proj_badge("S1-A · R1b", "Somatic Mutation Classifier", "🔶 In progress"))
705
  gr.Markdown("> This module is in active development. Coming in the next release.")
 
 
 
706
 
707
+ # === S1-B · PHYLO-RNA ===
 
 
708
  with gr.TabItem("S1-B · R1a · BRCA2 miRNA"):
709
  gr.HTML(proj_badge("S1-B · R1a", "miRNA Silencing — BRCA1/2 · TP53"))
710
  g1 = gr.Dropdown(["BRCA2","BRCA1","TP53"], value="BRCA2", label="Gene")
 
712
  o1 = gr.Dataframe(label="Top 5 downregulated miRNAs")
713
  gr.Examples([["BRCA2"],["BRCA1"],["TP53"]], inputs=[g1])
714
  b1.click(predict_mirna, [g1], o1)
 
 
 
715
 
 
 
 
716
  with gr.TabItem("S1-B · R2a · TP53 siRNA"):
717
  gr.HTML(proj_badge("S1-B · R2a", "siRNA Synthetic Lethal — TP53-null"))
718
  g2 = gr.Dropdown(["LUAD","BRCA","COAD"], value="LUAD", label="Cancer type")
 
720
  o2 = gr.Dataframe(label="Top 5 synthetic lethal targets")
721
  gr.Examples([["LUAD"],["BRCA"],["COAD"]], inputs=[g2], cache_examples=False)
722
  b2.click(predict_sirna, [g2], o2)
 
 
 
723
 
 
 
 
724
  with gr.TabItem("S1-B · R3a · lncRNA-TREM2"):
725
  gr.HTML(proj_badge("S1-B · R3a", "lncRNA-TREM2 ceRNA Network"))
726
  b3 = gr.Button("Load Network", variant="primary")
727
  o3 = gr.Dataframe(label="ceRNA Network")
728
  b3.click(get_lncrna, [], o3)
 
 
 
729
 
 
 
 
730
  with gr.TabItem("S1-B · R3b · ASO Designer"):
731
  gr.HTML(proj_badge("S1-B · R3b", "ASO Candidates"))
732
  b4 = gr.Button("Show ASOs", variant="primary")
733
  o4 = gr.Dataframe(label="ASO Candidates")
734
  b4.click(get_aso, [], o4)
735
+
736
+ # === S1-C · PHYLO-DRUG ===
737
+ # Розкоментовано проблемну вкладку "оце" - FGFR3
738
+ with gr.TabItem("S1-C · R1a · FGFR3 RNA Drug"):
739
+ gr.HTML(proj_badge("S1-C · R1a", "FGFR3 RNA-Directed Drug Discovery", "top score 0.793"))
740
+ g4 = gr.Radio(["P1 (hairpin loop)","P10 (G-quadruplex)"],
741
+ value="P1 (hairpin loop)", label="Target pocket")
742
+ b4_drug = gr.Button("Screen Compounds", variant="primary")
743
+ o4t = gr.Dataframe(label="Top 5 candidates")
744
+ o4p = gr.Image(label="Binding scores")
745
+ gr.Examples([["P1 (hairpin loop)"],["P10 (G-quadruplex)"]], inputs=[g4])
746
+ b4_drug.click(predict_drug, [g4], [o4t, o4p])
747
+
 
 
 
 
 
 
 
 
 
 
748
  with gr.TabItem("S1-C · R1b · SL Drug Mapping 🔶"):
749
  gr.HTML(proj_badge("S1-C · R1b", "Synthetic Lethal Drug Mapping", "🔶 In progress"))
750
  gr.Markdown("> Coming soon.")
 
 
 
751
 
 
 
 
752
  with gr.TabItem("S1-C · R2a · Frontier 🔴"):
753
  gr.HTML(proj_badge("S1-C · R2a", "m6A × Ferroptosis × Circadian", "🔴 Frontier"))
754
  gr.Markdown("> Planned for Q3 2026")
 
 
 
755
 
756
+ # === S1-D · PHYLO-LNP ===
 
 
757
  with gr.TabItem("S1-D · R1a · LNP Corona"):
758
  gr.HTML(proj_badge("S1-D · R1a", "LNP Protein Corona (Serum)", "AUC = 0.791"))
759
  with gr.Row():
 
766
  o6 = gr.Markdown()
767
  gr.Examples([[100,-5,1.5,"Ionizable"],[80,5,0.5,"Cationic"]], inputs=[sz,zt,pg,lp])
768
  b6.click(predict_corona, [sz,zt,pg,lp], o6)
769
+
770
+ # Розкоментовано проблемну вкладку "оце" - Flow Corona
771
+ with gr.TabItem("S1-D · R2a · Flow Corona"):
772
+ gr.HTML(proj_badge("S1-D · R2a", "Flow Corona — Vroman Effect"))
773
+ with gr.Row():
774
+ s8 = gr.Slider(50,300,value=100,step=1,label="Size (nm)")
775
+ z8 = gr.Slider(-40,10,value=-5,step=1,label="Zeta (mV)")
776
+ pg8 = gr.Slider(0,5,value=1.5,step=0.1,label="PEG mol%")
777
+ with gr.Row():
778
+ ch8 = gr.Dropdown(["Ionizable","Cationic","Anionic","Neutral"],value="Ionizable",label="Charge")
779
+ fl8 = gr.Slider(0,40,value=20,step=1,label="Flow cm/s")
780
+ b8 = gr.Button("Model Vroman Effect", variant="primary")
781
+ o8t = gr.Markdown()
782
+ o8p = gr.Image(label="Kinetics")
783
+ gr.Examples([[100,-5,1.5,"Ionizable",40],[150,5,0.5,"Cationic",10]], inputs=[s8,z8,pg8,ch8,fl8])
784
+ b8.click(predict_flow, [s8,z8,pg8,ch8,fl8], [o8t,o8p])
785
+
786
+ # Розкоментовано проблемну вкладку "оце" - LNP Brain
787
+ with gr.TabItem("S1-D · R3a · LNP Brain"):
788
+ gr.HTML(proj_badge("S1-D · R3a", "LNP Brain Delivery — ApoE% + BBB probability"))
789
+ smi = gr.Textbox(label="Ionizable lipid SMILES",
790
+ value="CC(C)CC(=O)OCC(COC(=O)CC(C)C)OC(=O)CC(C)C")
791
+ with gr.Row():
792
+ pk = gr.Slider(4,8,value=6.5,step=0.1,label="pKa")
793
+ zt9 = gr.Slider(-20,10,value=-3,step=1,label="Zeta (mV)")
794
+ b9 = gr.Button("Predict BBB Crossing", variant="primary")
795
+ o9t = gr.Markdown()
796
+ o9p = gr.Image(label="Radar profile")
797
+ gr.Examples([["CC(C)CC(=O)OCC(COC(=O)CC(C)C)OC(=O)CC(C)C",6.5,-3]], inputs=[smi,pk,zt9])
798
+ b9.click(predict_bbb, [smi,pk,zt9], [o9t,o9p])
799
+
800
+ # Розкоментовано проблемну вкладку "оце" - AutoCorona NLP
801
+ with gr.TabItem("S1-D · R4a · AutoCorona NLP"):
802
+ gr.HTML(proj_badge("S1-D · R4a", "AutoCorona NLP — from abstracts", "F1 = 0.71"))
803
+ txt = gr.Textbox(lines=5,label="Paper abstract",placeholder="Paste abstract here...")
804
+ b10 = gr.Button("Extract Data", variant="primary")
805
+ o10j = gr.Code(label="Extracted JSON", language="json")
806
+ o10f = gr.Textbox(label="Validation flags")
807
+ gr.Examples([[
808
+ "LNPs composed of MC3, DSPC, Cholesterol (50:10:40 mol%) with 1.5% PEG-DMG. "
809
+ "Hydrodynamic diameter was 98 nm, zeta potential -3.2 mV, PDI 0.12. "
810
+ "Incubated in human plasma. Corona: albumin, apolipoprotein E, fibrinogen."
811
+ ]], inputs=[txt])
812
+ b10.click(extract_corona, txt, [o10j, o10f])
813
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
814
  with gr.TabItem("S1-D · R5a · CSF/BM 🔴"):
815
  gr.HTML(proj_badge("S1-D · R5a", "LNP Corona in CSF · Vitreous · Bone Marrow", "🔴 0 prior studies"))
816
  gr.Markdown("> Planned for Q2–Q3 2026")
817
+
818
+ # === S1-E · PHYLO-BIOMARKERS ===
819
+ # Розкоментовано проблемну вкладку "оце" - Liquid Biopsy
820
+ with gr.TabItem("S1-E · R1a · Liquid Biopsy"):
821
+ gr.HTML(proj_badge("S1-E · R1a", "Liquid Biopsy Classifier", "AUC = 0.992*"))
822
+ with gr.Row():
823
+ p1=gr.Slider(-3,3,value=0,step=0.1,label="CTHRC1")
824
+ p2=gr.Slider(-3,3,value=0,step=0.1,label="FHL2")
825
+ p3=gr.Slider(-3,3,value=0,step=0.1,label="LDHA")
826
+ p4=gr.Slider(-3,3,value=0,step=0.1,label="P4HA1")
827
+ p5=gr.Slider(-3,3,value=0,step=0.1,label="SERPINH1")
828
+ with gr.Row():
829
+ p6=gr.Slider(-3,3,value=0,step=0.1,label="ABCA8")
830
+ p7=gr.Slider(-3,3,value=0,step=0.1,label="CA4")
831
+ p8=gr.Slider(-3,3,value=0,step=0.1,label="CKB")
832
+ p9=gr.Slider(-3,3,value=0,step=0.1,label="NNMT")
833
+ p10=gr.Slider(-3,3,value=0,step=0.1,label="CACNA2D2")
834
+ b7=gr.Button("Classify", variant="primary")
835
+ o7t=gr.HTML()
836
+ o7p=gr.Image(label="Feature contributions")
837
+ gr.Examples([[2,2,1.5,1.8,1.6,-1,-1.2,-0.8,1.4,-1.1],[0]*10],
838
+ inputs=[p1,p2,p3,p4,p5,p6,p7,p8,p9,p10])
839
+ b7.click(predict_cancer, [p1,p2,p3,p4,p5,p6,p7,p8,p9,p10], [o7t,o7p])
840
+
 
 
 
 
 
 
 
 
 
841
  with gr.TabItem("S1-E · R1b · Validator 🔶"):
842
  gr.HTML(proj_badge("S1-E · R1b", "Protein Panel Validator", "🔶 In progress"))
843
  gr.Markdown("> Coming next.")
844
+
845
+ # === S1-F · PHYLO-RARE ===
846
+ # Розкоментовано проблемну вкладку "оце" - DIPG
847
+ with gr.TabItem("S1-F · R1a · DIPG Toolkit"):
848
+ gr.HTML(proj_badge("S1-F · R1a", "DIPG: H3K27M + CSF LNP + Circadian", "PBTA"))
849
+ sort_d = gr.Radio(["Frequency", "Drug status"], value="Frequency", label="Sort by")
850
+ b_dv = gr.Button("Load DIPG Variants", variant="primary")
851
+ o_dv = gr.Dataframe(label="H3K27M co-mutations")
852
+ b_dv.click(dipg_variants, [sort_d], o_dv)
853
+ gr.Markdown("---")
854
+ with gr.Row():
855
+ d_peg = gr.Slider(0.5, 3.0, value=1.5, step=0.1, label="PEG mol%")
856
+ d_size = gr.Slider(60, 150, value=90, step=5, label="Target size (nm)")
857
+ b_dc = gr.Button("Rank CSF Formulations", variant="primary")
858
+ o_dct = gr.Dataframe(label="CSF LNP ranking")
859
+ o_dcp = gr.Image(label="ApoE% in CSF corona")
860
+ b_dc.click(dipg_csf, [d_peg, d_size], [o_dct, o_dcp])
861
+
862
+ # Розкоментовано проблемну вкладку "оце" - UVM
863
+ with gr.TabItem("S1-F · R2a · UVM Toolkit"):
864
+ gr.HTML(proj_badge("S1-F · R2a", "UVM: GNAQ/GNA11 + vitreous + m6A", "TCGA-UVM"))
865
+ b_uv = gr.Button("Load UVM Variants", variant="primary")
866
+ o_uv = gr.Dataframe(label="GNAQ/GNA11 map")
867
+ b_uv.click(uvm_variants, [], o_uv)
868
+ b_uw = gr.Button("Rank Vitreous Formulations", variant="primary")
869
+ o_uwt = gr.Dataframe(label="Vitreous LNP retention ranking")
870
+ o_uwp = gr.Image(label="Retention (hours)")
871
+ b_uw.click(uvm_vitreous, [], [o_uwt, o_uwp])
872
+
873
+ # Розкоментовано проблемну вкладку "оце" - pAML
874
+ with gr.TabItem("S1-F · R3a · pAML Toolkit"):
875
+ gr.HTML(proj_badge("S1-F · R3a", "pAML: FLT3-ITD + BM niche + ferroptosis", "TARGET-AML"))
876
+ var_sel = gr.Dropdown(
877
+ ["FLT3-ITD", "NPM1 c.860_863dupTCAG", "DNMT3A p.R882H",
878
+ "CEBPA biallelic", "IDH1/2 mutation"],
879
+ value="FLT3-ITD", label="Select variant"
880
+ )
881
+ b_pf = gr.Button("Analyze Ferroptosis Profile", variant="primary")
882
+ o_pft = gr.HTML()
883
+ o_pfp = gr.Image(label="Target radar")
884
+ b_pf.click(paml_ferroptosis, var_sel, [o_pft, o_pfp])
885
+
886
+ # === S1-G · 3D Lab (НОВА ВКЛАДКА) ===
887
+ with gr.TabItem("🧊 3D Lab"):
888
+ gr.HTML(section_header(
889
+ "S1-G", "PHYLO-SIM", "— 3D Models & Simulations",
890
+ "Interactive visualizations for learning"
891
+ ))
892
+ with gr.Tabs(elem_classes="inner-tabs"):
893
+ with gr.TabItem("Nanoparticle"):
894
+ gr.Markdown("### 3D Model of a Lipid Nanoparticle")
895
+ with gr.Row():
896
+ np_radius = gr.Slider(2, 20, value=5, step=1, label="Radius (nm)")
897
+ np_peg = gr.Slider(0, 1, value=0.3, step=0.05, label="PEG density")
898
+ np_btn = gr.Button("Generate", variant="primary")
899
+ np_plot = gr.Plotly(label="Nanoparticle")
900
+ np_btn.click(plot_nanoparticle, [np_radius, np_peg], np_plot)
901
+
902
+ with gr.TabItem("DNA Helix"):
903
+ gr.Markdown("### 3D Model of a DNA Double Helix")
904
+ dna_btn = gr.Button("Generate DNA", variant="primary")
905
+ dna_plot = gr.Plotly()
906
+ dna_btn.click(plot_dna, [], dna_plot)
907
+
908
+ with gr.TabItem("Protein Corona"):
909
+ gr.Markdown("### Schematic of Protein Corona on Nanoparticle")
910
+ corona_btn = gr.Button("Show Corona", variant="primary")
911
+ corona_plot = gr.Plotly()
912
+ corona_btn.click(plot_corona, [], corona_plot)
913
+
914
+ # === Journal (покращений) ===
915
  with gr.TabItem("📓 Journal"):
916
  gr.Markdown("### Lab Journal")
917
  with gr.Row():
918
  note_text = gr.Textbox(label="📝 Observation", placeholder="What did you discover?", lines=3)
919
  note_tab = gr.Textbox(label="Project code (e.g. S1-A·R1a)", value="General")
920
+ save_btn = gr.Button("💾 Save", variant="primary")
921
+ clear_btn = gr.Button("🗑️ Clear Journal", variant="secondary")
 
 
922
  refresh_btn = gr.Button("🔄 Refresh")
923
+ journal_display = gr.Markdown(value=load_journal())
924
+
925
+ save_btn.click(save_note, [note_text, note_tab], journal_display)
926
+ refresh_btn.click(load_journal, [], journal_display)
927
+ clear_btn.click(clear_journal, [], journal_display)
928
 
929
+ # === Learning ===
930
  with gr.TabItem("📚 Learning"):
931
  gr.Markdown("""
932
  ## 🧪 Guided Investigations
 
937
  **S1-D · R2a** Flow Corona – compare flow 0 vs 40 cm/s
938
  **S1-B · R2a** siRNA – count "Novel" targets across cancer types
939
  **S1-E · R1a** Liquid Biopsy – find minimal signal for CANCER
940
+ **S1-G · 3D Lab** – explore nanoparticle, DNA, and protein corona models
941
  """)
942
 
943
  gr.Markdown(