TEZv commited on
Commit
6d4309f
·
verified ·
1 Parent(s): 6bb57a5

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +400 -286
app.py CHANGED
@@ -15,6 +15,9 @@ CARD = "#1e293b"
15
  ACC = "#f97316"
16
  ACC2 = "#38bdf8"
17
  TXT = "#f1f5f9"
 
 
 
18
 
19
  LOG_PATH = Path("/tmp/lab_journal.csv")
20
 
@@ -25,13 +28,9 @@ def log_entry(tab, inputs, result, note=""):
25
  w = csv.DictWriter(f, fieldnames=["timestamp","tab","inputs","result","note"])
26
  if write_header:
27
  w.writeheader()
28
- w.writerow({
29
- "timestamp": datetime.now().strftime("%Y-%m-%d %H:%M"),
30
- "tab": tab,
31
- "inputs": str(inputs),
32
- "result": str(result)[:200],
33
- "note": note
34
- })
35
  except Exception:
36
  pass
37
 
@@ -47,6 +46,7 @@ def save_note(note, tab, last_result):
47
  log_entry(tab, "", last_result, note)
48
  return "✅ Saved!", load_journal()
49
 
 
50
  MIRNA_DB = {
51
  "BRCA2": [
52
  {"miRNA":"hsa-miR-148a-3p","log2FC":-0.70,"padj":0.013,"targets":"DNMT1, AKT2","pathway":"Epigenetic reprogramming"},
@@ -70,7 +70,6 @@ MIRNA_DB = {
70
  {"miRNA":"hsa-miR-215-5p","log2FC":-0.51,"padj":0.038,"targets":"DTL, DHFR","pathway":"DNA damage response"},
71
  ],
72
  }
73
-
74
  SIRNA_DB = {
75
  "LUAD": [
76
  {"Gene":"SPC24","dCERES":-0.175,"log2FC":1.13,"Drug_status":"Novel","siRNA":"GCAGCUGAAGAAACUGAAU"},
@@ -94,7 +93,6 @@ SIRNA_DB = {
94
  {"Gene":"PKMYT1","dCERES":-0.122,"log2FC":1.07,"Drug_status":"Clinical","siRNA":"GACGCUCAAGAUGCAGAUU"},
95
  ],
96
  }
97
-
98
  CERNA = [
99
  {"lncRNA":"CYTOR","miRNA":"hsa-miR-138-5p","target":"AKT1","pathway":"TREM2 core signaling"},
100
  {"lncRNA":"CYTOR","miRNA":"hsa-miR-138-5p","target":"NFKB1","pathway":"Neuroinflammation"},
@@ -109,7 +107,6 @@ ASO = [
109
  {"lncRNA":"LINC00847","position":89,"accessibility":0.598,"GC_pct":56,"Tm":48.3,"priority":"MEDIUM"},
110
  {"lncRNA":"ZFAS1","position":312,"accessibility":0.571,"GC_pct":48,"Tm":45.5,"priority":"MEDIUM"},
111
  ]
112
-
113
  FGFR3 = {
114
  "P1 (hairpin loop)": [
115
  {"Compound":"CHEMBL1575701","RNA_score":0.809,"Toxicity":0.01,"Final_score":0.793},
@@ -126,7 +123,6 @@ FGFR3 = {
126
  {"Compound":"Berberine","RNA_score":0.735,"Toxicity":3.2,"Final_score":0.708},
127
  ],
128
  }
129
-
130
  VARIANT_DB = {
131
  "BRCA1:p.R1699Q": {"score":0.03,"cls":"Benign","conf":"High"},
132
  "BRCA1:p.R1699W": {"score":0.97,"cls":"Pathogenic","conf":"High"},
@@ -137,10 +133,10 @@ VARIANT_DB = {
137
  "ALK:p.F1174L": {"score":0.94,"cls":"Pathogenic","conf":"High"},
138
  }
139
  PLAIN = {
140
- "Pathogenic": "This variant is likely to cause disease. Clinical follow-up is strongly recommended.",
141
- "Likely Pathogenic":"This variant is probably harmful. Discuss with your doctor.",
142
- "Benign": "This variant is likely harmless. Common in the general population.",
143
- "Likely Benign": "This variant is probably harmless. No strong reason for concern.",
144
  }
145
  BM_W = {
146
  "CTHRC1":0.18,"FHL2":0.15,"LDHA":0.14,"P4HA1":0.13,
@@ -151,18 +147,19 @@ PROTEINS = ["albumin","apolipoprotein","fibrinogen","vitronectin",
151
  "clusterin","igm","iga","igg","complement","transferrin",
152
  "alpha-2-macroglobulin"]
153
 
 
154
  def predict_mirna(gene):
155
  df = pd.DataFrame(MIRNA_DB.get(gene, []))
156
- log_entry("BRCA2 miRNA", gene, f"Found {len(df)} miRNAs for {gene}")
157
  return df
158
 
159
  def predict_sirna(cancer):
160
  df = pd.DataFrame(SIRNA_DB.get(cancer, []))
161
- log_entry("TP53 siRNA", cancer, f"Found {len(df)} targets for {cancer}")
162
  return df
163
 
164
  def get_lncrna():
165
- log_entry("lncRNA-TREM2", "load", "ceRNA+ASO tables")
166
  return pd.DataFrame(CERNA), pd.DataFrame(ASO)
167
 
168
  def predict_drug(pocket):
@@ -172,15 +169,11 @@ def predict_drug(pocket):
172
  ax.barh(df["Compound"], df["Final_score"], color=ACC)
173
  ax.set_xlabel("Final Score", color=TXT)
174
  ax.tick_params(colors=TXT)
175
- for sp in ax.spines.values():
176
- sp.set_edgecolor("#334155")
177
  ax.set_title(f"Top compounds — {pocket}", color=TXT, fontsize=10)
178
  plt.tight_layout()
179
- buf = BytesIO()
180
- plt.savefig(buf, format="png", dpi=120, facecolor=CARD)
181
- plt.close()
182
- buf.seek(0)
183
- log_entry("FGFR3 Drug", pocket, f"Top: {df.iloc[0]['Compound'] if len(df) else 'none'}")
184
  return df, Image.open(buf)
185
 
186
  def predict_variant(hgvs, sift, polyphen, gnomad):
@@ -190,27 +183,26 @@ def predict_variant(hgvs, sift, polyphen, gnomad):
190
  cls, conf, score = r["cls"], r["conf"], r["score"]
191
  else:
192
  score = 0.0
193
- if sift < 0.05: score += 0.4
194
- if polyphen > 0.85: score += 0.35
195
- if gnomad < 0.0001: score += 0.25
196
  score = round(score, 3)
197
- cls = ("Pathogenic" if score > 0.6 else
198
- "Likely Pathogenic" if score > 0.4 else "Benign")
199
  conf = "High" if (sift < 0.01 or sift > 0.9) else "Moderate"
200
- colour = "#ef4444" if "Pathogenic" in cls else "#22c55e"
201
  icon = "⚠️ WARNING" if "Pathogenic" in cls else "✅ OK"
202
- bar_w = int(score * 100)
203
  explanation = PLAIN.get(cls, "")
204
- log_entry("OpenVariant", hgvs or f"SIFT={sift}", f"{cls} score={score}")
205
  return (
206
- f"<div style='background:{CARD};padding:16px;border-radius:8px;"
207
- f"font-family:sans-serif;color:{TXT}'>"
208
- f"<h3 style='color:{colour}'>{icon} {cls}</h3>"
 
209
  f"<p>Score: <b>{score:.3f}</b> &nbsp;|&nbsp; Confidence: <b>{conf}</b></p>"
210
- f"<div style='background:#334155;border-radius:4px;height:16px'>"
211
- f"<div style='background:{colour};height:16px;border-radius:4px;width:{bar_w}%'></div></div>"
212
- f"<p style='margin-top:12px'>{explanation}</p>"
213
- f"<p style='font-size:11px;color:#64748b'>Research only. Not clinical.</p>"
214
  f"</div>"
215
  )
216
 
@@ -221,14 +213,12 @@ def predict_corona(size, zeta, peg, lipid):
221
  if abs(zeta) < 10: score += 1
222
  if peg > 1.5: score += 2
223
  if size < 100: score += 1
224
- proteins = ["ApoE","Albumin","Fibrinogen","Vitronectin","ApoA-I"]
225
- dominant = proteins[min(score, 4)]
226
- efficacy = ("High" if score >= 4 else "Medium" if score >= 2 else "Low")
227
- log_entry("LNP Corona", f"size={size},zeta={zeta},peg={peg},lipid={lipid}",
228
- f"dominant={dominant},efficacy={efficacy}")
229
  return (f"**Dominant corona protein:** {dominant}\n\n"
230
- f"**Predicted efficacy class:** {efficacy}\n\n"
231
- f"**Composite score:** {score}/6")
232
 
233
  def predict_cancer(c1,c2,c3,c4,c5,c6,c7,c8,c9,c10):
234
  vals = [c1,c2,c3,c4,c5,c6,c7,c8,c9,c10]
@@ -237,209 +227,339 @@ def predict_cancer(c1,c2,c3,c4,c5,c6,c7,c8,c9,c10):
237
  raw = sum(v*w for v,w in zip(vals, weights))
238
  prob = 1 / (1 + np.exp(-raw * 2))
239
  label = "CANCER" if prob > 0.5 else "HEALTHY"
240
- colour = "#ef4444" if prob > 0.5 else "#22c55e"
241
  contribs = [v*w for v,w in zip(vals, weights)]
242
- cols = [ACC if c > 0 else ACC2 for c in contribs]
243
  fig, ax = plt.subplots(figsize=(6, 3.5), facecolor=CARD)
244
  ax.set_facecolor(CARD)
245
- ax.barh(names, contribs, color=cols)
246
  ax.axvline(0, color=TXT, linewidth=0.8)
247
  ax.set_xlabel("Contribution to cancer score", color=TXT)
248
  ax.tick_params(colors=TXT, labelsize=8)
249
- for sp in ax.spines.values():
250
- sp.set_edgecolor("#334155")
251
  ax.set_title("Protein contributions", color=TXT, fontsize=10)
252
  plt.tight_layout()
253
- buf = BytesIO()
254
- plt.savefig(buf, format="png", dpi=120, facecolor=CARD)
255
- plt.close()
256
- buf.seek(0)
257
- log_entry("Liquid Biopsy", f"CTHRC1={c1},FHL2={c2}...", f"{label} prob={prob:.2f}")
258
  return (
259
- f"<div style='background:{CARD};padding:12px;border-radius:8px;"
260
- f"color:{colour};font-size:20px;font-family:sans-serif'>"
261
- f"<b>{label}</b><br>"
262
- f"<span style='color:{TXT};font-size:14px'>Probability: {prob:.2f}</span></div>"
263
  ), Image.open(buf)
264
 
265
  def predict_flow(size, zeta, peg, charge, flow_rate):
266
- csi = ((flow_rate/40)*0.6 + (peg/5)*0.2 +
267
- (1 if charge == "Cationic" else 0)*0.2)
268
- csi = round(min(csi, 1.0), 3)
269
- stability = ("High remodeling" if csi > 0.6 else
270
- "Medium" if csi > 0.3 else "Stable")
271
  t = np.linspace(0, 60, 200)
272
  kf = 0.03 * (1 + flow_rate/40)
273
  ks = 0.038 * (1 + flow_rate/40)
274
  fig, ax = plt.subplots(figsize=(6, 3.5), facecolor=CARD)
275
  ax.set_facecolor(CARD)
276
  ax.plot(t, 60*np.exp(-0.03*t)+20, color="#60a5fa", ls="--", label="Albumin (static)")
277
- ax.plot(t, 60*np.exp(-kf*t)+10, color="#60a5fa", label="Albumin (flow)")
278
  ax.plot(t, 14*(1-np.exp(-0.038*t))+5, color=ACC, ls="--", label="ApoE (static)")
279
  ax.plot(t, 20*(1-np.exp(-ks*t))+5, color=ACC, label="ApoE (flow)")
280
- ax.set_xlabel("Time (min)", color=TXT)
281
- ax.set_ylabel("% Corona", color=TXT)
282
  ax.tick_params(colors=TXT)
283
  ax.legend(fontsize=7, labelcolor=TXT, facecolor=CARD)
284
- for sp in ax.spines.values():
285
- sp.set_edgecolor("#334155")
286
- ax.set_title("Vroman Effect", color=TXT, fontsize=9)
287
  plt.tight_layout()
288
- buf = BytesIO()
289
- plt.savefig(buf, format="png", dpi=120, facecolor=CARD)
290
- plt.close()
291
- buf.seek(0)
292
- log_entry("Flow Corona", f"flow={flow_rate},charge={charge}", f"CSI={csi},{stability}")
293
  return f"**Corona Shift Index: {csi}** — {stability}", Image.open(buf)
294
 
295
  def predict_bbb(smiles, pka, zeta):
296
  logp = smiles.count("C")*0.3 - smiles.count("O")*0.5 + 1.5
297
  apoe_pct = max(0, min(40, (7.0-pka)*8 + abs(zeta)*0.5 + logp*0.8))
298
  bbb_prob = min(0.95, apoe_pct/30)
299
- tier = ("HIGH (>20%)" if apoe_pct > 20 else
300
- "MEDIUM (10-20%)" if apoe_pct > 10 else "LOW (<10%)")
301
- cats = ["ApoE%","BBB","logP","pKa fit","Zeta"]
302
- vals = [apoe_pct/40, bbb_prob, min(logp/5,1),
303
- (7-abs(pka-6.5))/7, (10-abs(zeta))/10]
304
- angles = np.linspace(0, 2*np.pi, len(cats), endpoint=False).tolist()
305
- v2, a2 = vals+[vals[0]], angles+[angles[0]]
306
- fig, ax = plt.subplots(figsize=(5, 4), subplot_kw={"polar":True}, facecolor=CARD)
307
  ax.set_facecolor(CARD)
308
- ax.plot(a2, v2, color=ACC, linewidth=2)
309
- ax.fill(a2, v2, color=ACC, alpha=0.2)
310
- ax.set_xticks(angles)
311
- ax.set_xticklabels(cats, color=TXT, fontsize=8)
312
  ax.tick_params(colors=TXT)
313
  plt.tight_layout()
314
- buf = BytesIO()
315
- plt.savefig(buf, format="png", dpi=120, facecolor=CARD)
316
- plt.close()
317
- buf.seek(0)
318
- log_entry("LNP Brain", f"pka={pka},zeta={zeta}", f"ApoE={apoe_pct:.1f}%,BBB={bbb_prob:.2f}")
319
- return (f"**Predicted ApoE:** {apoe_pct:.1f}% — {tier}\n\n"
320
- f"**BBB Probability:** {bbb_prob:.2f}"), Image.open(buf)
321
 
322
  def extract_corona(text):
323
- out = {
324
- "nanoparticle_composition": "",
325
- "size_nm": None, "zeta_mv": None, "PDI": None,
326
- "protein_source": "", "corona_proteins": [], "confidence": {}
327
- }
328
  m = re.search(r"(\d+\.?\d*)\s*(?:nm|nanometer)", text, re.I)
329
- if m:
330
- out["size_nm"] = float(m.group(1))
331
- out["confidence"]["size_nm"] = "HIGH"
332
  m = re.search(r"([+-]?\d+\.?\d*)\s*mV", text, re.I)
333
- if m:
334
- out["zeta_mv"] = float(m.group(1))
335
- out["confidence"]["zeta_mv"] = "HIGH"
336
  m = re.search(r"PDI\s*[=:of]*\s*(\d+\.?\d*)", text, re.I)
337
- if m:
338
- out["PDI"] = float(m.group(1))
339
- out["confidence"]["PDI"] = "HIGH"
340
  for src in ["human plasma","human serum","fetal bovine serum","FBS","PBS"]:
341
  if src.lower() in text.lower():
342
- out["protein_source"] = src
343
- out["confidence"]["protein_source"] = "HIGH"
344
- break
345
- out["corona_proteins"] = [
346
- {"name": p, "confidence": "MEDIUM"} for p in PROTEINS if p in text.lower()
347
- ]
348
  for lip in ["DSPC","DOPE","MC3","DLin","cholesterol","PEG","DOTAP"]:
349
- if lip in text:
350
- out["nanoparticle_composition"] += lip + " "
351
  out["nanoparticle_composition"] = out["nanoparticle_composition"].strip()
352
  flags = []
353
  if not out["size_nm"]: flags.append("size_nm not found")
354
  if not out["zeta_mv"]: flags.append("zeta_mv not found")
355
  if not out["corona_proteins"]: flags.append("no proteins detected")
356
  summary = "All key fields extracted" if not flags else " | ".join(flags)
357
- log_entry("AutoCorona NLP", text[:80]+"...",
358
- f"proteins={len(out['corona_proteins'])},{summary}")
359
  return json.dumps(out, indent=2), summary
360
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
361
  css = (
362
  f"body,.gradio-container{{background:{BG}!important;color:{TXT}!important}}"
363
- f".tab-nav button{{color:{TXT}!important;background:{CARD}!important}}"
364
  f".tab-nav button.selected{{border-bottom:2px solid {ACC}!important;color:{ACC}!important}}"
365
  f"h1,h2,h3{{color:{ACC}!important}}"
366
  f".gr-button-primary{{background:{ACC}!important;border:none!important}}"
367
  f"footer{{display:none!important}}"
 
 
 
368
  )
369
 
370
- LEARNING_CASES = """
371
- ## 🧪 Top 5 Guided Investigations
372
- ### Case 1 — Beginner 🟢
373
- **Question:** Why is the same gene position benign vs pathogenic?
374
- 1. OpenVariant → enter `BRCA1:p.R1699Q` → Benign
375
- 2. Enter `BRCA1:p.R1699W` Pathogenic
376
- 3. Same position, different amino acid — what changed?
377
- **Key concept:** Amino acid polarity determines protein folding impact.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
378
  ---
379
- ### Case 2 — Beginner 🟢
380
- **Question:** How does PEG% change what protein sticks to LNPs?
381
- 1. LNP Corona Ionizable, Zeta=-5, Size=100, PEG=0.5% → note protein
382
- 2. PEG=2.5% → compare
383
- 3. LNP Brain pKa=6.5compare ApoE%
384
- **Key concept:** More PEGless Fibrinogen, more ApoE.
 
 
 
 
385
  ---
386
- ### Case 3 — Intermediate 🟡
387
- **Question:** Does blood flow change corona composition?
388
- 1. Flow Corona Flow=0, Ionizable
389
- 2. Flow=40 (arterial) → compare ApoE curve
390
- 3. At what minute does ApoE plateau?
391
- **Key concept:** Vroman effect albumin displaced by ApoE under flow.
 
 
 
 
392
  ---
393
- ### Case 4 — Intermediate 🟡
394
- **Question:** Which cancer has the most novel siRNA targets?
395
- 1. TP53 siRNA LUAD count "Novel"
396
- 2. Repeat BRCA, COAD
397
- 3. Pick one Novel geneGoogle: "[gene] cancer therapeutic target"
 
 
 
 
 
398
  ---
399
- ### Case 5 — Advanced 🔴
400
- **Question:** Can you identify cancer from protein levels?
401
- 1. Liquid Biopsy all sliders=0 HEALTHY
 
 
 
 
 
 
 
 
 
 
 
 
 
402
  2. Set CTHRC1=2.5, FHL2=2.0, LDHA=1.8 → observe
403
- 3. Find minimum CTHRC1 that tips to CANCER
404
- **Key concept:** CTHRC1 weight (0.18) dominates the score.
 
 
 
 
 
 
 
 
 
 
 
 
405
  """
406
 
407
- with gr.Blocks(css=css, title="K R&D Lab") as demo:
 
408
 
409
  gr.Markdown(
410
- "# 🧬 K R&D Lab Computational Biology Suite\n"
411
- "**Oksana Kolisnyk** · ML Engineer · "
412
- "[KOSATIKS GROUP](https://kosatiks-group.pp.ua)\n"
413
- "> 10 open-source tools + lab journal."
 
 
414
  )
415
 
416
  with gr.Tabs():
417
 
418
- with gr.TabItem("🧬 BRCA2 miRNA"):
419
- gr.Markdown("### Tumor Suppressor miRNAs")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
420
  g1 = gr.Dropdown(["BRCA2","BRCA1","TP53"], value="BRCA2", label="Gene")
421
  b1 = gr.Button("Find miRNAs", variant="primary")
422
  o1 = gr.Dataframe(label="Top 5 downregulated miRNAs")
423
  gr.Examples([["BRCA2"],["TP53"]], inputs=[g1])
424
  b1.click(predict_mirna, g1, o1)
425
 
426
- with gr.TabItem("💉 TP53 siRNA"):
427
- gr.Markdown("### Synthetic Lethal siRNA Targets")
 
428
  g2 = gr.Dropdown(["LUAD","BRCA","COAD"], value="LUAD", label="Cancer type")
429
  b2 = gr.Button("Find Targets", variant="primary")
430
- o2 = gr.Dataframe(label="Top 5 siRNA targets")
431
- gr.Examples([["LUAD"],["BRCA"]], inputs=[g2])
432
  b2.click(predict_sirna, g2, o2)
433
 
434
- with gr.TabItem("🧠 lncRNA-TREM2"):
435
- gr.Markdown("### lncRNA Networks in Alzheimer's")
 
436
  b3 = gr.Button("Load Results", variant="primary")
437
  o3a = gr.Dataframe(label="ceRNA Network")
438
- o3b = gr.Dataframe(label="ASO Candidates")
439
  b3.click(get_lncrna, [], [o3a, o3b])
440
 
441
- with gr.TabItem("💊 FGFR3 Drug"):
442
- gr.Markdown("### RNA-Directed Drug Discovery: FGFR3")
 
 
 
 
 
 
 
 
 
 
 
 
443
  g4 = gr.Radio(["P1 (hairpin loop)","P10 (G-quadruplex)"],
444
  value="P1 (hairpin loop)", label="Target pocket")
445
  b4 = gr.Button("Screen Compounds", variant="primary")
@@ -448,94 +568,61 @@ with gr.Blocks(css=css, title="K R&D Lab") as demo:
448
  gr.Examples([["P1 (hairpin loop)"],["P10 (G-quadruplex)"]], inputs=[g4])
449
  b4.click(predict_drug, g4, [o4t, o4p])
450
 
451
- with gr.TabItem("🔬 OpenVariant"):
452
- gr.Markdown("### OpenVariant Pathogenicity Classifier\nAUC=0.939 on ClinVar 2026.")
453
- hgvs = gr.Textbox(label="HGVS notation", placeholder="BRCA1:p.R1699Q")
454
- gr.Markdown("**Or enter scores manually:**")
455
- with gr.Row():
456
- sift = gr.Slider(0, 1, value=0.5, step=0.01, label="SIFT (0=damaging)")
457
- pp = gr.Slider(0, 1, value=0.5, step=0.01, label="PolyPhen-2")
458
- gn = gr.Slider(0, 0.01, value=0.001, step=0.0001, label="gnomAD AF")
459
- b5 = gr.Button("Predict Pathogenicity", variant="primary")
460
- o5 = gr.HTML(label="Result")
461
- gr.Examples(
462
- [["BRCA1:p.R1699Q", 0.82, 0.05, 0.0012],
463
- ["TP53:p.R248W", 0.00, 1.00, 0.0],
464
- ["BRCA2:p.D2723A", 0.01, 0.98, 0.0]],
465
- inputs=[hgvs, sift, pp, gn])
466
- b5.click(predict_variant, [hgvs, sift, pp, gn], o5)
467
-
468
- with gr.TabItem("🧪 LNP Corona"):
469
- gr.Markdown("### LNP Protein Corona Prediction")
470
  with gr.Row():
471
- sz = gr.Slider(50, 300, value=100, step=1, label="Size (nm)")
472
- zt = gr.Slider(-40, 10, value=-5, step=1, label="Zeta (mV)")
473
  with gr.Row():
474
- pg = gr.Slider(0, 5, value=1.5, step=0.1, label="PEG mol%")
475
- lp = gr.Dropdown(["Ionizable","Cationic","Anionic","Neutral"],
476
- value="Ionizable", label="Lipid type")
477
- b6 = gr.Button("Predict", variant="primary")
478
- o6 = gr.Markdown()
479
  gr.Examples([[100,-5,1.5,"Ionizable"],[80,5,0.5,"Cationic"]], inputs=[sz,zt,pg,lp])
480
  b6.click(predict_corona, [sz,zt,pg,lp], o6)
481
 
482
- with gr.TabItem("🩸 Liquid Biopsy"):
483
- gr.Markdown("### Protein Corona Cancer Diagnostics\nClassify cancer vs healthy.")
484
- with gr.Row():
485
- p1 = gr.Slider(-3, 3, value=0, step=0.1, label="CTHRC1")
486
- p2 = gr.Slider(-3, 3, value=0, step=0.1, label="FHL2")
487
- p3 = gr.Slider(-3, 3, value=0, step=0.1, label="LDHA")
488
- p4 = gr.Slider(-3, 3, value=0, step=0.1, label="P4HA1")
489
- p5 = gr.Slider(-3, 3, value=0, step=0.1, label="SERPINH1")
490
- with gr.Row():
491
- p6 = gr.Slider(-3, 3, value=0, step=0.1, label="ABCA8")
492
- p7 = gr.Slider(-3, 3, value=0, step=0.1, label="CA4")
493
- p8 = gr.Slider(-3, 3, value=0, step=0.1, label="CKB")
494
- p9 = gr.Slider(-3, 3, value=0, step=0.1, label="NNMT")
495
- p10 = gr.Slider(-3, 3, value=0, step=0.1, label="CACNA2D2")
496
- b7 = gr.Button("Classify", variant="primary")
497
- o7t = gr.HTML()
498
- o7p = gr.Image(label="Feature contributions")
499
- gr.Examples(
500
- [[2,2,1.5,1.8,1.6,-1,-1.2,-0.8,1.4,-1.1],
501
- [0,0,0,0,0,0,0,0,0,0]],
502
- inputs=[p1,p2,p3,p4,p5,p6,p7,p8,p9,p10])
503
- b7.click(predict_cancer, [p1,p2,p3,p4,p5,p6,p7,p8,p9,p10], [o7t,o7p])
504
-
505
- with gr.TabItem("🌊 Flow Corona"):
506
- gr.Markdown("### Corona Remodeling Under Blood Flow")
507
  with gr.Row():
508
- s8 = gr.Slider(50, 300, value=100, step=1, label="Size (nm)")
509
- z8 = gr.Slider(-40, 10, value=-5, step=1, label="Zeta (mV)")
510
- pg8 = gr.Slider(0, 5, value=1.5, step=0.1, label="PEG mol%")
511
  with gr.Row():
512
- ch8 = gr.Dropdown(["Ionizable","Cationic","Anionic","Neutral"],
513
- value="Ionizable", label="Charge type")
514
- fl8 = gr.Slider(0, 40, value=20, step=1, label="Flow rate cm/s (aorta=40)")
515
- b8 = gr.Button("Model Vroman Effect", variant="primary")
516
- o8t = gr.Markdown()
517
- o8p = gr.Image(label="Kinetics plot")
518
- gr.Examples([[100,-5,1.5,"Ionizable",40],[150,5,0.5,"Cationic",10]],
519
- inputs=[s8,z8,pg8,ch8,fl8])
520
  b8.click(predict_flow, [s8,z8,pg8,ch8,fl8], [o8t,o8p])
521
 
522
- with gr.TabItem("🧠 LNP Brain"):
523
- gr.Markdown("### LNP Brain Delivery Predictor")
524
- smi = gr.Textbox(label="Ionizable lipid SMILES",
525
- value="CC(C)CC(=O)OCC(COC(=O)CC(C)C)OC(=O)CC(C)C")
526
  with gr.Row():
527
- pk = gr.Slider(4, 8, value=6.5, step=0.1, label="pKa")
528
- zt9 = gr.Slider(-20, 10, value=-3, step=1, label="Zeta (mV)")
529
- b9 = gr.Button("Predict BBB Crossing", variant="primary")
530
- o9t = gr.Markdown()
531
- o9p = gr.Image(label="Radar profile")
532
- gr.Examples([["CC(C)CC(=O)OCC(COC(=O)CC(C)C)OC(=O)CC(C)C", 6.5, -3]],
533
- inputs=[smi, pk, zt9])
534
- b9.click(predict_bbb, [smi, pk, zt9], [o9t, o9p])
535
-
536
- with gr.TabItem("📄 AutoCorona NLP"):
537
- gr.Markdown("### AutoCorona NLP Extraction\nPaste any paper abstract.")
538
- txt = gr.Textbox(lines=6, label="Paper abstract", placeholder="Paste text here...")
539
  b10 = gr.Button("Extract Data", variant="primary")
540
  o10j = gr.Code(label="Extracted JSON", language="json")
541
  o10f = gr.Textbox(label="Validation flags")
@@ -546,55 +633,82 @@ with gr.Blocks(css=css, title="K R&D Lab") as demo:
546
  ]], inputs=[txt])
547
  b10.click(extract_corona, txt, [o10j, o10f])
548
 
549
- with gr.TabItem("📓 Lab Journal"):
550
- gr.Markdown("### Your Research Log\nEvery query is auto-saved.")
 
 
 
 
 
 
 
 
 
 
 
551
  with gr.Row():
552
- note_text = gr.Textbox(
553
- label="📝 Add observation / conclusion",
554
- placeholder="What did you discover? What's your next question?",
555
- lines=3)
556
- note_tab = gr.Textbox(label="Which tool?", value="General")
557
- note_last = gr.Textbox(label="Result to annotate", visible=False)
558
- save_btn = gr.Button("💾 Save Observation", variant="primary")
559
- save_msg = gr.Markdown()
560
- journal_df = gr.Dataframe(
561
- label="📋 Full History",
562
- value=load_journal(),
563
- interactive=False)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
564
  refresh_btn = gr.Button("🔄 Refresh")
565
  refresh_btn.click(load_journal, [], journal_df)
566
- save_btn.click(save_note, [note_text, note_tab, note_last], [save_msg, journal_df])
567
- gr.Markdown("📥 Log saved as `lab_journal.csv` in the app folder.")
568
 
569
- with gr.TabItem("📚 Learning Mode"):
570
  gr.Markdown(LEARNING_CASES)
571
- gr.Markdown("---\n### 📖 Quick Reference")
572
- gr.Markdown("""
573
- | Tool | Predicts | Key input |
574
- |------|----------|-----------|
575
- | OpenVariant | Pathogenic/Benign | Gene mutation |
576
- | LNP Corona | Dominant protein | Formulation |
577
- | Flow Corona | Vroman kinetics | Flow rate |
578
- | LNP Brain | ApoE% + BBB prob | pKa + zeta |
579
- | Liquid Biopsy | Cancer/Healthy | Protein z-scores |
580
- | BRCA2 miRNA | Downregulated miRNAs | Gene name |
581
- | TP53 siRNA | Synthetic lethal targets | Cancer type |
582
- | lncRNA-TREM2 | ceRNA + ASOs | |
583
- | FGFR3 Drug | Small molecules | Pocket type |
584
- | AutoCorona NLP | Structured data | Abstract text |
585
- """)
586
- gr.Markdown("""
587
  ### 🔗 Resources
588
- - [PubMed](https://pubmed.ncbi.nlm.nih.gov)
589
- - [ClinVar](https://www.ncbi.nlm.nih.gov/clinvar/)
590
- - [UniProt](https://www.uniprot.org)
591
- - [ChEMBL](https://www.ebi.ac.uk/chembl/)
592
  """)
593
 
594
  gr.Markdown(
595
- "---\n**K R&D Lab** | Research only — not clinical | "
596
- "[GitHub](https://github.com/TEZv/K-RnD-Lab-PHYLO-03_2026) | "
597
- "[KOSATIKS GROUP 🦈](https://kosatiks-group.pp.ua)"
 
 
598
  )
599
 
600
- demo.launch(server_name="0.0.0.0", server_port=7860)
 
15
  ACC = "#f97316"
16
  ACC2 = "#38bdf8"
17
  TXT = "#f1f5f9"
18
+ GRN = "#22c55e"
19
+ RED = "#ef4444"
20
+ DIM = "#64748b"
21
 
22
  LOG_PATH = Path("/tmp/lab_journal.csv")
23
 
 
28
  w = csv.DictWriter(f, fieldnames=["timestamp","tab","inputs","result","note"])
29
  if write_header:
30
  w.writeheader()
31
+ w.writerow({"timestamp": datetime.now().strftime("%Y-%m-%d %H:%M"),
32
+ "tab": tab, "inputs": str(inputs),
33
+ "result": str(result)[:200], "note": note})
 
 
 
 
34
  except Exception:
35
  pass
36
 
 
46
  log_entry(tab, "", last_result, note)
47
  return "✅ Saved!", load_journal()
48
 
49
+ # ── DATABASES ─────────────────────────────────────────────────────────────────
50
  MIRNA_DB = {
51
  "BRCA2": [
52
  {"miRNA":"hsa-miR-148a-3p","log2FC":-0.70,"padj":0.013,"targets":"DNMT1, AKT2","pathway":"Epigenetic reprogramming"},
 
70
  {"miRNA":"hsa-miR-215-5p","log2FC":-0.51,"padj":0.038,"targets":"DTL, DHFR","pathway":"DNA damage response"},
71
  ],
72
  }
 
73
  SIRNA_DB = {
74
  "LUAD": [
75
  {"Gene":"SPC24","dCERES":-0.175,"log2FC":1.13,"Drug_status":"Novel","siRNA":"GCAGCUGAAGAAACUGAAU"},
 
93
  {"Gene":"PKMYT1","dCERES":-0.122,"log2FC":1.07,"Drug_status":"Clinical","siRNA":"GACGCUCAAGAUGCAGAUU"},
94
  ],
95
  }
 
96
  CERNA = [
97
  {"lncRNA":"CYTOR","miRNA":"hsa-miR-138-5p","target":"AKT1","pathway":"TREM2 core signaling"},
98
  {"lncRNA":"CYTOR","miRNA":"hsa-miR-138-5p","target":"NFKB1","pathway":"Neuroinflammation"},
 
107
  {"lncRNA":"LINC00847","position":89,"accessibility":0.598,"GC_pct":56,"Tm":48.3,"priority":"MEDIUM"},
108
  {"lncRNA":"ZFAS1","position":312,"accessibility":0.571,"GC_pct":48,"Tm":45.5,"priority":"MEDIUM"},
109
  ]
 
110
  FGFR3 = {
111
  "P1 (hairpin loop)": [
112
  {"Compound":"CHEMBL1575701","RNA_score":0.809,"Toxicity":0.01,"Final_score":0.793},
 
123
  {"Compound":"Berberine","RNA_score":0.735,"Toxicity":3.2,"Final_score":0.708},
124
  ],
125
  }
 
126
  VARIANT_DB = {
127
  "BRCA1:p.R1699Q": {"score":0.03,"cls":"Benign","conf":"High"},
128
  "BRCA1:p.R1699W": {"score":0.97,"cls":"Pathogenic","conf":"High"},
 
133
  "ALK:p.F1174L": {"score":0.94,"cls":"Pathogenic","conf":"High"},
134
  }
135
  PLAIN = {
136
+ "Pathogenic": "This variant is likely to cause disease. Clinical follow-up is strongly recommended.",
137
+ "Likely Pathogenic": "This variant is probably harmful. Discuss with your doctor.",
138
+ "Benign": "This variant is likely harmless. Common in the general population.",
139
+ "Likely Benign": "This variant is probably harmless. No strong reason for concern.",
140
  }
141
  BM_W = {
142
  "CTHRC1":0.18,"FHL2":0.15,"LDHA":0.14,"P4HA1":0.13,
 
147
  "clusterin","igm","iga","igg","complement","transferrin",
148
  "alpha-2-macroglobulin"]
149
 
150
+ # ── LOGIC ─────────────────────────────────────────────────────────────────────
151
  def predict_mirna(gene):
152
  df = pd.DataFrame(MIRNA_DB.get(gene, []))
153
+ log_entry("S1-B | S1-R2 | miRNA", gene, f"{len(df)} miRNAs")
154
  return df
155
 
156
  def predict_sirna(cancer):
157
  df = pd.DataFrame(SIRNA_DB.get(cancer, []))
158
+ log_entry("S1-B | S1-R3 | siRNA", cancer, f"{len(df)} targets")
159
  return df
160
 
161
  def get_lncrna():
162
+ log_entry("S1-B | S1-R4 | lncRNA", "load", "ceRNA+ASO")
163
  return pd.DataFrame(CERNA), pd.DataFrame(ASO)
164
 
165
  def predict_drug(pocket):
 
169
  ax.barh(df["Compound"], df["Final_score"], color=ACC)
170
  ax.set_xlabel("Final Score", color=TXT)
171
  ax.tick_params(colors=TXT)
172
+ for sp in ax.spines.values(): sp.set_edgecolor("#334155")
 
173
  ax.set_title(f"Top compounds — {pocket}", color=TXT, fontsize=10)
174
  plt.tight_layout()
175
+ buf = BytesIO(); plt.savefig(buf, format="png", dpi=120, facecolor=CARD); plt.close(); buf.seek(0)
176
+ log_entry("S1-C | S1-R5 | Drug", pocket, f"Top: {df.iloc[0]['Compound'] if len(df) else 'none'}")
 
 
 
177
  return df, Image.open(buf)
178
 
179
  def predict_variant(hgvs, sift, polyphen, gnomad):
 
183
  cls, conf, score = r["cls"], r["conf"], r["score"]
184
  else:
185
  score = 0.0
186
+ if sift < 0.05: score += 0.4
187
+ if polyphen > 0.85: score += 0.35
188
+ if gnomad < 0.0001: score += 0.25
189
  score = round(score, 3)
190
+ cls = "Pathogenic" if score > 0.6 else "Likely Pathogenic" if score > 0.4 else "Benign"
 
191
  conf = "High" if (sift < 0.01 or sift > 0.9) else "Moderate"
192
+ colour = RED if "Pathogenic" in cls else GRN
193
  icon = "⚠️ WARNING" if "Pathogenic" in cls else "✅ OK"
 
194
  explanation = PLAIN.get(cls, "")
195
+ log_entry("S1-A | S1-R1 | OpenVariant", hgvs or f"SIFT={sift}", f"{cls} score={score}")
196
  return (
197
+ f"<div style=\'background:{CARD};padding:16px;border-radius:8px;font-family:sans-serif;color:{TXT}\'>"
198
+ f"<p style=\'font-size:11px;color:{DIM};margin:0 0 8px\'>"
199
+ f"S1 · Biomedical &nbsp;›&nbsp; S1-A · PHYLO-GENOMICS &nbsp;›&nbsp; S1-R1 · OpenVariant</p>"
200
+ f"<h3 style=\'color:{colour};margin:0 0 8px\'>{icon} {cls}</h3>"
201
  f"<p>Score: <b>{score:.3f}</b> &nbsp;|&nbsp; Confidence: <b>{conf}</b></p>"
202
+ f"<div style=\'background:#334155;border-radius:4px;height:14px\'>"
203
+ f"<div style=\'background:{colour};height:14px;border-radius:4px;width:{int(score*100)}%\'></div></div>"
204
+ f"<p style=\'margin-top:12px\'>{explanation}</p>"
205
+ f"<p style=\'font-size:11px;color:{DIM}\'>Research only. Not clinical advice.</p>"
206
  f"</div>"
207
  )
208
 
 
213
  if abs(zeta) < 10: score += 1
214
  if peg > 1.5: score += 2
215
  if size < 100: score += 1
216
+ dominant = ["ApoE","Albumin","Fibrinogen","Vitronectin","ApoA-I"][min(score, 4)]
217
+ efficacy = "High" if score >= 4 else "Medium" if score >= 2 else "Low"
218
+ log_entry("S1-D | S1-R6 | Corona", f"size={size},peg={peg}", f"dominant={dominant}")
 
 
219
  return (f"**Dominant corona protein:** {dominant}\n\n"
220
+ f"**Predicted efficacy:** {efficacy}\n\n"
221
+ f"**Score:** {score}/6")
222
 
223
  def predict_cancer(c1,c2,c3,c4,c5,c6,c7,c8,c9,c10):
224
  vals = [c1,c2,c3,c4,c5,c6,c7,c8,c9,c10]
 
227
  raw = sum(v*w for v,w in zip(vals, weights))
228
  prob = 1 / (1 + np.exp(-raw * 2))
229
  label = "CANCER" if prob > 0.5 else "HEALTHY"
230
+ colour = RED if prob > 0.5 else GRN
231
  contribs = [v*w for v,w in zip(vals, weights)]
 
232
  fig, ax = plt.subplots(figsize=(6, 3.5), facecolor=CARD)
233
  ax.set_facecolor(CARD)
234
+ ax.barh(names, contribs, color=[ACC if c > 0 else ACC2 for c in contribs])
235
  ax.axvline(0, color=TXT, linewidth=0.8)
236
  ax.set_xlabel("Contribution to cancer score", color=TXT)
237
  ax.tick_params(colors=TXT, labelsize=8)
238
+ for sp in ax.spines.values(): sp.set_edgecolor("#334155")
 
239
  ax.set_title("Protein contributions", color=TXT, fontsize=10)
240
  plt.tight_layout()
241
+ buf = BytesIO(); plt.savefig(buf, format="png", dpi=120, facecolor=CARD); plt.close(); buf.seek(0)
242
+ log_entry("S1-E | S1-R9 | LiquidBiopsy", f"CTHRC1={c1},FHL2={c2}", f"{label} {prob:.2f}")
 
 
 
243
  return (
244
+ f"<div style=\'background:{CARD};padding:12px;border-radius:8px;font-family:sans-serif;\'>"
245
+ f"<p style=\'font-size:11px;color:{DIM};margin:0 0 6px\'>S1-E · PHYLO-BIOMARKERS · S1-R9</p>"
246
+ f"<span style=\'color:{colour};font-size:22px;font-weight:bold\'>{label}</span><br>"
247
+ f"<span style=\'color:{TXT};font-size:14px\'>Probability: {prob:.2f}</span></div>"
248
  ), Image.open(buf)
249
 
250
  def predict_flow(size, zeta, peg, charge, flow_rate):
251
+ csi = round(min((flow_rate/40)*0.6 + (peg/5)*0.2 + (1 if charge=="Cationic" else 0)*0.2, 1.0), 3)
252
+ stability = "High remodeling" if csi > 0.6 else "Medium" if csi > 0.3 else "Stable"
 
 
 
253
  t = np.linspace(0, 60, 200)
254
  kf = 0.03 * (1 + flow_rate/40)
255
  ks = 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)
264
  ax.legend(fontsize=7, labelcolor=TXT, facecolor=CARD)
265
+ for sp in ax.spines.values(): sp.set_edgecolor("#334155")
266
+ ax.set_title("Vroman Effect — flow vs static", color=TXT, fontsize=9)
 
267
  plt.tight_layout()
268
+ buf = BytesIO(); plt.savefig(buf, format="png", dpi=120, facecolor=CARD); plt.close(); buf.seek(0)
269
+ log_entry("S1-D | S1-R7 | FlowCorona", f"flow={flow_rate}", f"CSI={csi}")
 
 
 
270
  return f"**Corona Shift Index: {csi}** — {stability}", Image.open(buf)
271
 
272
  def predict_bbb(smiles, pka, zeta):
273
  logp = smiles.count("C")*0.3 - smiles.count("O")*0.5 + 1.5
274
  apoe_pct = max(0, min(40, (7.0-pka)*8 + abs(zeta)*0.5 + logp*0.8))
275
  bbb_prob = min(0.95, apoe_pct/30)
276
+ tier = "HIGH (>20%)" if apoe_pct > 20 else "MEDIUM (10-20%)" if apoe_pct > 10 else "LOW (<10%)"
277
+ cats = ["ApoE%","BBB","logP","pKa fit","Zeta"]
278
+ vals = [apoe_pct/40, bbb_prob, min(logp/5,1), (7-abs(pka-6.5))/7, (10-abs(zeta))/10]
279
+ angles = np.linspace(0, 2*np.pi, len(cats), endpoint=False).tolist()
280
+ v2, a2 = vals+[vals[0]], angles+[angles[0]]
281
+ fig, ax = plt.subplots(figsize=(5, 4), subplot_kw={"polar":True}, facecolor=CARD)
 
 
282
  ax.set_facecolor(CARD)
283
+ ax.plot(a2, v2, color=ACC, linewidth=2); ax.fill(a2, v2, color=ACC, alpha=0.2)
284
+ ax.set_xticks(angles); ax.set_xticklabels(cats, color=TXT, fontsize=8)
 
 
285
  ax.tick_params(colors=TXT)
286
  plt.tight_layout()
287
+ buf = BytesIO(); plt.savefig(buf, format="png", dpi=120, facecolor=CARD); plt.close(); buf.seek(0)
288
+ log_entry("S1-D | S1-R8 | LNPBrain", f"pka={pka},zeta={zeta}", f"ApoE={apoe_pct:.1f}%")
289
+ return f"**Predicted ApoE:** {apoe_pct:.1f}% — {tier}\n\n**BBB Probability:** {bbb_prob:.2f}", Image.open(buf)
 
 
 
 
290
 
291
  def extract_corona(text):
292
+ out = {"nanoparticle_composition":"","size_nm":None,"zeta_mv":None,"PDI":None,
293
+ "protein_source":"","corona_proteins":[],"confidence":{}}
 
 
 
294
  m = re.search(r"(\d+\.?\d*)\s*(?:nm|nanometer)", text, re.I)
295
+ if m: out["size_nm"] = float(m.group(1)); out["confidence"]["size_nm"] = "HIGH"
 
 
296
  m = re.search(r"([+-]?\d+\.?\d*)\s*mV", text, re.I)
297
+ if m: out["zeta_mv"] = float(m.group(1)); out["confidence"]["zeta_mv"] = "HIGH"
 
 
298
  m = re.search(r"PDI\s*[=:of]*\s*(\d+\.?\d*)", text, re.I)
299
+ if m: out["PDI"] = float(m.group(1)); out["confidence"]["PDI"] = "HIGH"
 
 
300
  for src in ["human plasma","human serum","fetal bovine serum","FBS","PBS"]:
301
  if src.lower() in text.lower():
302
+ out["protein_source"] = src; out["confidence"]["protein_source"] = "HIGH"; break
303
+ out["corona_proteins"] = [{"name":p,"confidence":"MEDIUM"} for p in PROTEINS if p in text.lower()]
 
 
 
 
304
  for lip in ["DSPC","DOPE","MC3","DLin","cholesterol","PEG","DOTAP"]:
305
+ if lip in text: out["nanoparticle_composition"] += lip + " "
 
306
  out["nanoparticle_composition"] = out["nanoparticle_composition"].strip()
307
  flags = []
308
  if not out["size_nm"]: flags.append("size_nm not found")
309
  if not out["zeta_mv"]: flags.append("zeta_mv not found")
310
  if not out["corona_proteins"]: flags.append("no proteins detected")
311
  summary = "All key fields extracted" if not flags else " | ".join(flags)
312
+ log_entry("S1-D | S1-R10 | NLP", text[:80], f"proteins={len(out['corona_proteins'])}")
 
313
  return json.dumps(out, indent=2), summary
314
 
315
+ # ── SECTION BADGE HELPER ──────────────────────────────────────────────────────
316
+ def badge(sphere_code, sphere_name, project_code, project_name, description, metric=""):
317
+ metric_html = (f"<span style=\'background:#1e3a5f;color:{ACC2};padding:2px 8px;"
318
+ f"border-radius:4px;font-size:11px;margin-left:8px\'>{metric}</span>"
319
+ if metric else "")
320
+ return (
321
+ f"<div style=\'background:{CARD};border-left:3px solid {ACC};"
322
+ f"padding:10px 14px;border-radius:0 6px 6px 0;margin-bottom:6px;\'>"
323
+ f"<span style=\'color:{DIM};font-size:11px\'>S1 · Biomedical &nbsp;›&nbsp; "
324
+ f"{sphere_code} · {sphere_name} &nbsp;›&nbsp; {project_code}</span>{metric_html}<br>"
325
+ f"<span style=\'color:{TXT};font-weight:bold;font-size:15px\'>{project_name}</span><br>"
326
+ f"<span style=\'color:{DIM};font-size:12px\'>{description}</span>"
327
+ f"</div>"
328
+ )
329
+
330
+ # ── CSS ───────────────────────────────────────────────────────────────────────
331
  css = (
332
  f"body,.gradio-container{{background:{BG}!important;color:{TXT}!important}}"
333
+ f".tab-nav button{{color:{TXT}!important;background:{CARD}!important;font-size:13px!important}}"
334
  f".tab-nav button.selected{{border-bottom:2px solid {ACC}!important;color:{ACC}!important}}"
335
  f"h1,h2,h3{{color:{ACC}!important}}"
336
  f".gr-button-primary{{background:{ACC}!important;border:none!important}}"
337
  f"footer{{display:none!important}}"
338
+ # section divider tabs look different
339
+ f".tab-nav button[data-testid*='divider']{{color:{DIM}!important;cursor:default!important;"
340
+ f"border-bottom:1px solid #334155!important;font-size:11px!important}}"
341
  )
342
 
343
+ # ── CONTENT STRINGS ───────────────────────────────────────────────────────────
344
+ MAP_HTML = f"""
345
+ <div style="background:{CARD};padding:20px;border-radius:8px;font-family:monospace;
346
+ font-size:13px;line-height:1.9;color:{TXT}">
347
+
348
+ <span style="color:{ACC};font-size:15px;font-weight:bold">K R&D Lab · S1 Biomedical</span>
349
+ <span style="color:{DIM};font-size:11px"> Science Sphere, sub-direction 1</span>
350
+ <br><br>
351
+
352
+ <span style="color:{ACC2}">S1-A · PHYLO-GENOMICS</span> &nbsp;<span style="color:{DIM}">← What breaks in DNA</span><br>
353
+ &nbsp;&nbsp;&nbsp;├─ <b>S1-R1</b> &nbsp;OpenVariant &nbsp;<span style="color:{GRN}">AUC=0.939 ✅</span><br>
354
+ &nbsp;&nbsp;&nbsp;├─ <b>S1-R1b</b> Somatic classifier &nbsp;<span style="color:#f59e0b">🔶 In progress</span><br>
355
+ &nbsp;&nbsp;&nbsp;└─ <b>S1-R12a</b> Rare variants (DIPG · UVM) &nbsp;<span style="color:{DIM}">🔴 Planned</span><br>
356
+ <br>
357
+
358
+ <span style="color:{ACC2}">S1-B · PHYLO-RNA</span> &nbsp;<span style="color:{DIM}">← How to silence it via RNA</span><br>
359
+ &nbsp;&nbsp;&nbsp;├─ <b>S1-R2</b> &nbsp;miRNA silencing (BRCA1/2, TP53) &nbsp;<span style="color:{GRN}">✅</span><br>
360
+ &nbsp;&nbsp;&nbsp;├─ <b>S1-R3</b> &nbsp;siRNA synthetic lethal (20+ cancers) &nbsp;<span style="color:{GRN}">✅</span><br>
361
+ &nbsp;&nbsp;&nbsp;├─ <b>S1-R4</b> &nbsp;lncRNA-TREM2 ceRNA network &nbsp;<span style="color:{GRN}">✅</span><br>
362
+ &nbsp;&nbsp;&nbsp;└─ <b>S1-R4b</b> ASO designer &nbsp;<span style="color:{GRN}">✅</span><br>
363
+ <br>
364
+
365
+ <span style="color:{ACC2}">S1-C · PHYLO-DRUG</span> &nbsp;<span style="color:{DIM}">← Which molecule treats it</span><br>
366
+ &nbsp;&nbsp;&nbsp;├─ <b>S1-R5</b> &nbsp;FGFR3 RNA-directed compounds &nbsp;<span style="color:{GRN}">✅</span><br>
367
+ &nbsp;&nbsp;&nbsp;├─ <b>S1-R5b</b> Synthetic lethal drug mapping &nbsp;<span style="color:#f59e0b">🔶</span><br>
368
+ &nbsp;&nbsp;&nbsp;└─ <b>S1-R13</b> m6A × Ferroptosis × Circadian ⭐ &nbsp;<span style="color:{DIM}">🔴 Frontier</span><br>
369
+ <br>
370
+
371
+ <span style="color:{ACC2}">S1-D · PHYLO-LNP</span> &nbsp;<span style="color:{DIM}">← How to deliver the drug</span><br>
372
+ &nbsp;&nbsp;&nbsp;├─ <b>S1-R6</b> &nbsp;LNP corona (serum) &nbsp;<span style="color:{GRN}">AUC=0.791 ✅</span><br>
373
+ &nbsp;&nbsp;&nbsp;├─ <b>S1-R7</b> &nbsp;Flow corona — Vroman effect &nbsp;<span style="color:{GRN}">✅</span><br>
374
+ &nbsp;&nbsp;&nbsp;├─ <b>S1-R8</b> &nbsp;LNP brain / BBB / ApoE &nbsp;<span style="color:{GRN}">✅</span><br>
375
+ &nbsp;&nbsp;&nbsp;├─ <b>S1-R10</b> AutoCorona NLP &nbsp;<span style="color:{GRN}">F1=0.71 ✅</span><br>
376
+ &nbsp;&nbsp;&nbsp;└─ <b>S1-R11</b> CSF · Vitreous · Bone Marrow ⭐ &nbsp;<span style="color:{DIM}">🔴 0 prior studies</span><br>
377
+ <br>
378
+
379
+ <span style="color:{ACC2}">S1-E · PHYLO-BIOMARKERS</span> &nbsp;<span style="color:{DIM}">← Detect without biopsy</span><br>
380
+ &nbsp;&nbsp;&nbsp;├─ <b>S1-R9</b> &nbsp;Liquid Biopsy classifier &nbsp;<span style="color:{GRN}">AUC=0.992* ✅</span><br>
381
+ &nbsp;&nbsp;&nbsp;├─ <b>S1-R9b</b> Protein panel validator &nbsp;<span style="color:#f59e0b">🔶</span><br>
382
+ &nbsp;&nbsp;&nbsp;└─ <b>S1-R9c</b> ctDNA gap analysis &nbsp;<span style="color:{DIM}">🔴</span><br>
383
+ <br>
384
+
385
+ <span style="color:{ACC2}">S1-F · PHYLO-RARE</span> &nbsp;<span style="color:{DIM}">← Where nobody looked yet</span><br>
386
+ &nbsp;&nbsp;&nbsp;├─ <b>S1-R12b</b> DIPG toolkit (H3K27M) &nbsp;<span style="color:{DIM}">🔴</span><br>
387
+ &nbsp;&nbsp;&nbsp;├─ <b>S1-R12c</b> UVM toolkit (GNAQ/GNA11) &nbsp;<span style="color:{DIM}">🔴</span><br>
388
+ &nbsp;&nbsp;&nbsp;└─ <b>S1-R12d</b> pAML toolkit (FLT3-ITD) &nbsp;<span style="color:{DIM}">🔴</span><br>
389
+ <br>
390
+ <span style="color:{DIM};font-size:11px">
391
+ ✅ Active in this demo &nbsp;·&nbsp; 🔶 In progress &nbsp;·&nbsp; 🔴 Planned / Frontier<br>
392
+ ⭐ = gap research (0–1 prior studies globally) &nbsp;·&nbsp; * = tissue proxy, plasma validation pending
393
+ </span>
394
+ </div>
395
+ """
396
+
397
+ LEARNING_CASES = f"""
398
+ ## 🧪 Guided Investigations — S1 Biomedical
399
+
400
+ > Progress through levels: 🟢 Beginner → 🟡 Intermediate → 🔴 Advanced
401
+
402
  ---
403
+
404
+ ### 🟢 Case 1 · S1-A · S1-R1 Variant Pathogenicity
405
+ **Why does the same position give two different outcomes?**
406
+
407
+ 1. **OpenVariant**`BRCA1:p.R1699Q`Benign
408
+ 2. **OpenVariant** `BRCA1:p.R1699W`Pathogenic
409
+ 3. Same position (R1699), different amino acid (Q vs W). What changed?
410
+
411
+ *Key concept: Amino acid polarity determines BRCT domain folding. Q is polar-uncharged; W is bulky-aromatic.*
412
+
413
  ---
414
+
415
+ ### 🟢 Case 2 · S1-D · S1-R6 + S1-R8 — PEG and Brain Delivery
416
+ **How does PEG% change which protein coats your nanoparticle?**
417
+
418
+ 1. **LNP Corona** Ionizable, Zeta=−5, Size=100, **PEG=0.5%** → note protein
419
+ 2. **PEG=2.5%** compare dominant protein
420
+ 3. **LNP Brain** → pKa=6.5 → check ApoE%
421
+
422
+ *Key concept: More PEG → steric shielding → less Fibrinogen → more ApoE → better BBB crossing.*
423
+
424
  ---
425
+
426
+ ### 🟡 Case 3 · S1-D · S1-R7 — Vroman Effect Under Flow
427
+ **Does blood flow speed reshape the corona over time?**
428
+
429
+ 1. **Flow Corona** Flow=0, Ionizable observe ApoE plateau minute
430
+ 2. **Flow=40** (arterial) → compare same curve
431
+ 3. At what minute does ApoE dominate under arterial flow?
432
+
433
+ *Key concept: Albumin adsorbs first (abundance), then displaced by ApoE (affinity). Flow accelerates exchange 3–4×.*
434
+
435
  ---
436
+
437
+ ### 🟡 Case 4 · S1-B · S1-R3 — Novel siRNA Targets
438
+ **Which cancer type has the most undrugged therapeutic targets?**
439
+
440
+ 1. **TP53 siRNA** → LUAD → count Drug_status = "Novel"
441
+ 2. Repeat for BRCA, COAD
442
+ 3. Pick one Novel gene → search: `[gene name] cancer PubMed`
443
+
444
+ *Key concept: "Novel" = no approved or clinical drug yet — highest opportunity for new therapeutic development.*
445
+
446
+ ---
447
+
448
+ ### 🔴 Case 5 · S1-E · S1-R9 — Cancer Detection Threshold
449
+ **What is the minimum signal that flips diagnosis to CANCER?**
450
+
451
+ 1. **Liquid Biopsy** → all sliders = 0 → HEALTHY
452
  2. Set CTHRC1=2.5, FHL2=2.0, LDHA=1.8 → observe
453
+ 3. Reset all. Increase only CTHRC1 step by step. At what value does it tip?
454
+
455
+ *Key concept: CTHRC1 weight=0.18 is the dominant feature. One protein can outweigh a full panel.*
456
+
457
+ ---
458
+
459
+ ### 🔴 Case 6 · S1-B + S1-C · Cross-tool — Convergent Evidence
460
+ **Is there target overlap between RNA silencing and drug discovery?**
461
+
462
+ 1. **miRNA** → gene=TP53 → find top silenced targets (BCL2, CDK6)
463
+ 2. **FGFR3 Drug** → P1 → find if CDK6 pathway appears in compound targets
464
+ 3. **siRNA** → BRCA → does CDK6 appear in synthetic lethal list?
465
+
466
+ *Key concept: Convergence across S1-B and S1-C = higher-confidence therapeutic hypothesis. This is how real pipelines are built.*
467
  """
468
 
469
+ # ── BLOCKS UI ─────────────────────────────────────────────────────────────────
470
+ with gr.Blocks(css=css, title="K R&D Lab · S1 Biomedical") as demo:
471
 
472
  gr.Markdown(
473
+ "# 🔬 K R&D Lab · Science Sphere\n"
474
+ "**S1 Biomedical** 10 active projects · "
475
+ "[Oksana Kolisnyk](https://kosatiks-group.pp.ua) · KOSATIKS GROUP\n"
476
+ "> Research only. Not clinical advice. &nbsp;|&nbsp; "
477
+ "[GitHub](https://github.com/K-RnD-Lab) &nbsp; "
478
+ "[HuggingFace](https://huggingface.co/K-RnD-Lab)"
479
  )
480
 
481
  with gr.Tabs():
482
 
483
+ # ── MAP ───────────────────────────────────────────────────────────────
484
+ with gr.TabItem("🗺️ Lab Map"):
485
+ gr.HTML(MAP_HTML)
486
+
487
+ # ── S1-A · GENOMICS ───────────────────────────────────────────────────
488
+ with gr.TabItem("── S1-A · GENOMICS ──"):
489
+ gr.HTML(
490
+ f"<div style=\'background:{CARD};padding:14px;border-radius:8px;\'>"
491
+ f"<b style=\'color:{ACC2}\'>S1-A · PHYLO-GENOMICS</b>"
492
+ f"<span style=\'color:{DIM};font-size:12px\'> — What breaks in DNA</span><br>"
493
+ f"<span style=\'color:{TXT};font-size:13px\'>Projects: S1-R1 (active) · S1-R1b (in progress) · S1-R12a (planned)</span>"
494
+ f"</div>"
495
+ )
496
+
497
+ with gr.TabItem("S1-R1 · OpenVariant"):
498
+ gr.HTML(badge("S1-A","PHYLO-GENOMICS","S1-R1","OpenVariant",
499
+ "SNV pathogenicity classifier · ClinVar 2026","AUC = 0.939"))
500
+ hgvs = gr.Textbox(label="HGVS notation", placeholder="BRCA1:p.R1699Q")
501
+ gr.Markdown("**Or enter functional scores manually:**")
502
+ with gr.Row():
503
+ sift = gr.Slider(0,1,value=0.5,step=0.01,label="SIFT (0=damaging)")
504
+ pp = gr.Slider(0,1,value=0.5,step=0.01,label="PolyPhen-2")
505
+ gn = gr.Slider(0,0.01,value=0.001,step=0.0001,label="gnomAD AF")
506
+ b5 = gr.Button("Predict Pathogenicity", variant="primary")
507
+ o5 = gr.HTML()
508
+ gr.Examples([["BRCA1:p.R1699Q",0.82,0.05,0.0012],
509
+ ["TP53:p.R248W",0.00,1.00,0.0],
510
+ ["BRCA2:p.D2723A",0.01,0.98,0.0]], inputs=[hgvs,sift,pp,gn])
511
+ b5.click(predict_variant, [hgvs,sift,pp,gn], o5)
512
+
513
+ # ── S1-B · RNA THERAPEUTICS ───────────────────────────────────────────
514
+ with gr.TabItem("── S1-B · RNA THER ──"):
515
+ gr.HTML(
516
+ f"<div style=\'background:{CARD};padding:14px;border-radius:8px;\'>"
517
+ f"<b style=\'color:{ACC2}\'>S1-B · PHYLO-RNA</b>"
518
+ f"<span style=\'color:{DIM};font-size:12px\'> — How to silence it via RNA</span><br>"
519
+ f"<span style=\'color:{TXT};font-size:13px\'>Projects: S1-R2 · S1-R3 · S1-R4 · S1-R4b (all active)</span>"
520
+ f"</div>"
521
+ )
522
+
523
+ with gr.TabItem("S1-R2 · miRNA"):
524
+ gr.HTML(badge("S1-B","PHYLO-RNA","S1-R2","miRNA Silencing",
525
+ "Downregulated miRNAs in tumor suppressor knockouts · BRCA1/2 · TP53"))
526
  g1 = gr.Dropdown(["BRCA2","BRCA1","TP53"], value="BRCA2", label="Gene")
527
  b1 = gr.Button("Find miRNAs", variant="primary")
528
  o1 = gr.Dataframe(label="Top 5 downregulated miRNAs")
529
  gr.Examples([["BRCA2"],["TP53"]], inputs=[g1])
530
  b1.click(predict_mirna, g1, o1)
531
 
532
+ with gr.TabItem("S1-R3 · siRNA"):
533
+ gr.HTML(badge("S1-B","PHYLO-RNA","S1-R3","siRNA Synthetic Lethal",
534
+ "TP53-null cancer — novel undrugged SL targets · LUAD · BRCA · COAD"))
535
  g2 = gr.Dropdown(["LUAD","BRCA","COAD"], value="LUAD", label="Cancer type")
536
  b2 = gr.Button("Find Targets", variant="primary")
537
+ o2 = gr.Dataframe(label="Top 5 synthetic lethal targets")
538
+ gr.Examples([["LUAD"],["BRCA"],["COAD"]], inputs=[g2])
539
  b2.click(predict_sirna, g2, o2)
540
 
541
+ with gr.TabItem("S1-R4 · lncRNA"):
542
+ gr.HTML(badge("S1-B","PHYLO-RNA","S1-R4","lncRNA-TREM2 Network",
543
+ "ceRNA axis in Alzheimer neuroinflammation · CYTOR→miR-138-5p→AKT1"))
544
  b3 = gr.Button("Load Results", variant="primary")
545
  o3a = gr.Dataframe(label="ceRNA Network")
546
+ o3b = gr.Dataframe(label="ASO Candidates (S1-R4b)")
547
  b3.click(get_lncrna, [], [o3a, o3b])
548
 
549
+ # ── S1-C · DRUG DISCOVERY ─────────────────────────────────────────────
550
+ with gr.TabItem("── S1-C · DRUG ──"):
551
+ gr.HTML(
552
+ f"<div style=\'background:{CARD};padding:14px;border-radius:8px;\'>"
553
+ f"<b style=\'color:{ACC2}\'>S1-C · PHYLO-DRUG</b>"
554
+ f"<span style=\'color:{DIM};font-size:12px\'> — Which molecule treats it</span><br>"
555
+ f"<span style=\'color:{TXT};font-size:13px\'>Projects: S1-R5 (active) · S1-R5b (in progress) · S1-R13 (frontier)</span>"
556
+ f"</div>"
557
+ )
558
+
559
+ with gr.TabItem("S1-R5 · Drug"):
560
+ gr.HTML(badge("S1-C","PHYLO-DRUG","S1-R5","FGFR3 RNA-Directed Drug Discovery",
561
+ "Small molecules binding FGFR3 RNA structural pockets · ChEMBL screen",
562
+ "top score 0.793"))
563
  g4 = gr.Radio(["P1 (hairpin loop)","P10 (G-quadruplex)"],
564
  value="P1 (hairpin loop)", label="Target pocket")
565
  b4 = gr.Button("Screen Compounds", variant="primary")
 
568
  gr.Examples([["P1 (hairpin loop)"],["P10 (G-quadruplex)"]], inputs=[g4])
569
  b4.click(predict_drug, g4, [o4t, o4p])
570
 
571
+ # ── S1-D · LNP DELIVERY ───────────────────────────────────────────────
572
+ with gr.TabItem("── S1-D · LNP ──"):
573
+ gr.HTML(
574
+ f"<div style=\'background:{CARD};padding:14px;border-radius:8px;\'>"
575
+ f"<b style=\'color:{ACC2}\'>S1-D · PHYLO-LNP</b>"
576
+ f"<span style=\'color:{DIM};font-size:12px\'> How to deliver the drug to the cell</span><br>"
577
+ f"<span style=\'color:{TXT};font-size:13px\'>Active: S1-R6 · S1-R7 · S1-R8 · S1-R10 &nbsp;|&nbsp; "
578
+ f"Frontier: S1-R11 (CSF/Vitreous/Bone Marrow 0 prior studies)</span>"
579
+ f"</div>"
580
+ )
581
+
582
+ with gr.TabItem("S1-R6 · Corona"):
583
+ gr.HTML(badge("S1-D","PHYLO-LNP","S1-R6","LNP Protein Corona (Serum)",
584
+ "Dominant corona protein from formulation parameters","AUC = 0.791"))
 
 
 
 
 
585
  with gr.Row():
586
+ sz = gr.Slider(50,300,value=100,step=1,label="Size (nm)")
587
+ zt = gr.Slider(-40,10,value=-5,step=1,label="Zeta (mV)")
588
  with gr.Row():
589
+ pg = gr.Slider(0,5,value=1.5,step=0.1,label="PEG mol%")
590
+ lp = gr.Dropdown(["Ionizable","Cationic","Anionic","Neutral"],value="Ionizable",label="Lipid type")
591
+ b6 = gr.Button("Predict", variant="primary"); o6 = gr.Markdown()
 
 
592
  gr.Examples([[100,-5,1.5,"Ionizable"],[80,5,0.5,"Cationic"]], inputs=[sz,zt,pg,lp])
593
  b6.click(predict_corona, [sz,zt,pg,lp], o6)
594
 
595
+ with gr.TabItem("S1-R7 · Flow"):
596
+ gr.HTML(badge("S1-D","PHYLO-LNP","S1-R7","Flow Corona Vroman Effect",
597
+ "Blood flow reshapes LNP corona over time · albumin→ApoE exchange kinetics"))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
598
  with gr.Row():
599
+ s8 = gr.Slider(50,300,value=100,step=1,label="Size (nm)")
600
+ z8 = gr.Slider(-40,10,value=-5,step=1,label="Zeta (mV)")
601
+ pg8 = gr.Slider(0,5,value=1.5,step=0.1,label="PEG mol%")
602
  with gr.Row():
603
+ ch8 = gr.Dropdown(["Ionizable","Cationic","Anionic","Neutral"],value="Ionizable",label="Charge")
604
+ fl8 = gr.Slider(0,40,value=20,step=1,label="Flow rate cm/s (aorta=40)")
605
+ b8 = gr.Button("Model Vroman Effect", variant="primary")
606
+ o8t = gr.Markdown(); o8p = gr.Image(label="Kinetics")
607
+ gr.Examples([[100,-5,1.5,"Ionizable",40],[150,5,0.5,"Cationic",10]], inputs=[s8,z8,pg8,ch8,fl8])
 
 
 
608
  b8.click(predict_flow, [s8,z8,pg8,ch8,fl8], [o8t,o8p])
609
 
610
+ with gr.TabItem("S1-R8 · Brain"):
611
+ gr.HTML(badge("S1-D","PHYLO-LNP","S1-R8","LNP Brain Delivery",
612
+ "ApoE corona % and blood-brain barrier crossing probability"))
613
+ smi = gr.Textbox(label="Ionizable lipid SMILES",value="CC(C)CC(=O)OCC(COC(=O)CC(C)C)OC(=O)CC(C)C")
614
  with gr.Row():
615
+ pk = gr.Slider(4,8,value=6.5,step=0.1,label="pKa")
616
+ zt9 = gr.Slider(-20,10,value=-3,step=1,label="Zeta (mV)")
617
+ b9 = gr.Button("Predict BBB Crossing", variant="primary")
618
+ o9t = gr.Markdown(); o9p = gr.Image(label="Radar profile")
619
+ gr.Examples([["CC(C)CC(=O)OCC(COC(=O)CC(C)C)OC(=O)CC(C)C",6.5,-3]], inputs=[smi,pk,zt9])
620
+ b9.click(predict_bbb, [smi,pk,zt9], [o9t,o9p])
621
+
622
+ with gr.TabItem("S1-R10 · NLP"):
623
+ gr.HTML(badge("S1-D","PHYLO-LNP","S1-R10","AutoCorona NLP",
624
+ "Extract structured LNP data from PubMed/PMC abstracts","F1 = 0.71"))
625
+ txt = gr.Textbox(lines=6,label="Paper abstract",placeholder="Paste abstract text here...")
 
626
  b10 = gr.Button("Extract Data", variant="primary")
627
  o10j = gr.Code(label="Extracted JSON", language="json")
628
  o10f = gr.Textbox(label="Validation flags")
 
633
  ]], inputs=[txt])
634
  b10.click(extract_corona, txt, [o10j, o10f])
635
 
636
+ # ── S1-E · BIOMARKERS ─────────────────────────────────────────────────
637
+ with gr.TabItem("── S1-E · BIO ──"):
638
+ gr.HTML(
639
+ f"<div style=\'background:{CARD};padding:14px;border-radius:8px;\'>"
640
+ f"<b style=\'color:{ACC2}\'>S1-E · PHYLO-BIOMARKERS</b>"
641
+ f"<span style=\'color:{DIM};font-size:12px\'> — Detect cancer without tissue biopsy</span><br>"
642
+ f"<span style=\'color:{TXT};font-size:13px\'>Projects: S1-R9 (active) · S1-R9b (in progress) · S1-R9c (planned)</span>"
643
+ f"</div>"
644
+ )
645
+
646
+ with gr.TabItem("S1-R9 · Biopsy"):
647
+ gr.HTML(badge("S1-E","PHYLO-BIOMARKERS","S1-R9","Liquid Biopsy Classifier",
648
+ "CTHRC1 · FHL2 · LDHA panel · tissue proteomics proxy","AUC = 0.992*"))
649
  with gr.Row():
650
+ p1=gr.Slider(-3,3,value=0,step=0.1,label="CTHRC1")
651
+ p2=gr.Slider(-3,3,value=0,step=0.1,label="FHL2")
652
+ p3=gr.Slider(-3,3,value=0,step=0.1,label="LDHA")
653
+ p4=gr.Slider(-3,3,value=0,step=0.1,label="P4HA1")
654
+ p5=gr.Slider(-3,3,value=0,step=0.1,label="SERPINH1")
655
+ with gr.Row():
656
+ p6=gr.Slider(-3,3,value=0,step=0.1,label="ABCA8")
657
+ p7=gr.Slider(-3,3,value=0,step=0.1,label="CA4")
658
+ p8=gr.Slider(-3,3,value=0,step=0.1,label="CKB")
659
+ p9=gr.Slider(-3,3,value=0,step=0.1,label="NNMT")
660
+ p10=gr.Slider(-3,3,value=0,step=0.1,label="CACNA2D2")
661
+ b7=gr.Button("Classify", variant="primary")
662
+ o7t=gr.HTML(); o7p=gr.Image(label="Feature contributions")
663
+ gr.Examples([[2,2,1.5,1.8,1.6,-1,-1.2,-0.8,1.4,-1.1],[0,0,0,0,0,0,0,0,0,0]],
664
+ inputs=[p1,p2,p3,p4,p5,p6,p7,p8,p9,p10])
665
+ b7.click(predict_cancer, [p1,p2,p3,p4,p5,p6,p7,p8,p9,p10], [o7t,o7p])
666
+
667
+ # ── LAB ───────────────────────────────────────────────────────────────
668
+ with gr.TabItem("📓 Journal"):
669
+ gr.Markdown("### Lab Journal · Auto-logged by project code\nEvery tool call is saved with its S1-X · S1-RX label.")
670
+ with gr.Row():
671
+ note_text = gr.Textbox(label="📝 Observation / conclusion",
672
+ placeholder="What did you discover?", lines=3)
673
+ note_tab = gr.Textbox(label="Project code (e.g. S1-R1)", value="General")
674
+ note_last = gr.Textbox(visible=False)
675
+ save_btn = gr.Button("💾 Save", variant="primary")
676
+ save_msg = gr.Markdown()
677
+ journal_df = gr.Dataframe(label="📋 Full History", value=load_journal(), interactive=False)
678
  refresh_btn = gr.Button("🔄 Refresh")
679
  refresh_btn.click(load_journal, [], journal_df)
680
+ save_btn.click(save_note, [note_text,note_tab,note_last], [save_msg,journal_df])
 
681
 
682
+ with gr.TabItem("📚 Learning"):
683
  gr.Markdown(LEARNING_CASES)
684
+ gr.Markdown("""---
685
+ ### 📖 Quick Reference — S1 Active Tools
686
+
687
+ | Code | Sub-dir | Tool | Key metric |
688
+ |------|---------|------|------------|
689
+ | S1-R1 | S1-A | OpenVariant | AUC=0.939 |
690
+ | S1-R2 | S1-B | miRNA silencing | hsa-miR-148a-3p top hit |
691
+ | S1-R3 | S1-B | siRNA SL targets | SPC24 top in LUAD |
692
+ | S1-R4 | S1-B | lncRNA-TREM2 | CYTOR→AKT1 |
693
+ | S1-R5 | S1-C | FGFR3 drug | score=0.793 |
694
+ | S1-R6 | S1-D | LNP corona | AUC=0.791 |
695
+ | S1-R7 | S1-D | Flow corona | 3–4× ApoE acceleration |
696
+ | S1-R8 | S1-D | LNP brain | pKa 6.2–6.8 optimal |
697
+ | S1-R9 | S1-E | Liquid biopsy | AUC=0.992* |
698
+ | S1-R10 | S1-D | AutoCorona NLP | F1=0.71 |
699
+
700
  ### 🔗 Resources
701
+ [PubMed](https://pubmed.ncbi.nlm.nih.gov) · [ClinVar](https://ncbi.nlm.nih.gov/clinvar/) ·
702
+ [miRBase](https://mirbase.org) · [ChEMBL](https://ebi.ac.uk/chembl/) ·
703
+ [DepMap](https://depmap.org) · [UniProt](https://uniprot.org)
 
704
  """)
705
 
706
  gr.Markdown(
707
+ f"---\n"
708
+ f"**K R&D Lab** · Science Sphere · S1 Biomedical · "
709
+ f"[GitHub](https://github.com/K-RnD-Lab) · "
710
+ f"[HuggingFace](https://huggingface.co/K-RnD-Lab) · "
711
+ f"[KOSATIKS GROUP 🦈](https://kosatiks-group.pp.ua) · MIT License"
712
  )
713
 
714
+ demo.launch(server_name="0.0.0.0", server_port=7860)