TEZv commited on
Commit
e1d3e96
·
verified ·
1 Parent(s): f3f3bd1

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +610 -9
app.py CHANGED
@@ -1,10 +1,611 @@
1
  import gradio as gr
2
- def hello(name):
3
- return f"Hello {name}!"
4
- with gr.Blocks() as demo:
5
- gr.Markdown("# Test")
6
- name = gr.Textbox()
7
- out = gr.Textbox()
8
- btn = gr.Button("Click")
9
- btn.click(hello, name, out)
10
- demo.launch()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  import gradio as gr
2
+ import pandas as pd
3
+ import numpy as np
4
+ import json, re, csv
5
+ import matplotlib
6
+ matplotlib.use("Agg")
7
+ import matplotlib.pyplot as plt
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__)
15
+ print("Starting app...")
16
+
17
+ # ========== Кольори ==========
18
+ BG = "#0f172a"
19
+ CARD = "#1e293b"
20
+ ACC = "#f97316"
21
+ ACC2 = "#38bdf8"
22
+ TXT = "#f1f5f9"
23
+ GRN = "#22c55e"
24
+ RED = "#ef4444"
25
+ DIM = "#8e9bae"
26
+ BORDER = "#334155"
27
+
28
+ # ========== Логування ==========
29
+ LOG_PATH = Path("/tmp/lab_journal.csv")
30
+
31
+ def log_entry(tab, inputs, result, note=""):
32
+ try:
33
+ write_header = not LOG_PATH.exists()
34
+ with open(LOG_PATH, "a", newline="", encoding="utf-8") as f:
35
+ w = csv.DictWriter(f, fieldnames=["timestamp","tab","inputs","result","note"])
36
+ if write_header: w.writeheader()
37
+ w.writerow({"timestamp": datetime.now().strftime("%Y-%m-%d %H:%M"),
38
+ "tab": tab, "inputs": str(inputs),
39
+ "result": str(result)[:200], "note": note})
40
+ except Exception: pass
41
+
42
+ 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 = {
56
+ "BRCA2": [
57
+ {"miRNA":"hsa-miR-148a-3p","log2FC":-0.70,"padj":0.013,"targets":"DNMT1, AKT2","pathway":"Epigenetic reprogramming"},
58
+ {"miRNA":"hsa-miR-30e-5p","log2FC":-0.49,"padj":0.032,"targets":"MYC, KRAS","pathway":"Oncogene suppression"},
59
+ {"miRNA":"hsa-miR-551b-3p","log2FC":-0.59,"padj":0.048,"targets":"SMAD4, CDK6","pathway":"TGF-beta / CDK4/6"},
60
+ {"miRNA":"hsa-miR-22-3p","log2FC":-0.43,"padj":0.041,"targets":"HIF1A, PTEN","pathway":"Hypoxia / PI3K"},
61
+ {"miRNA":"hsa-miR-200c-3p","log2FC":-0.38,"padj":0.044,"targets":"ZEB1, ZEB2","pathway":"EMT suppression"},
62
+ ],
63
+ "BRCA1": [
64
+ {"miRNA":"hsa-miR-155-5p","log2FC":-0.81,"padj":0.008,"targets":"SHIP1, SOCS1","pathway":"Immune evasion"},
65
+ {"miRNA":"hsa-miR-146a-5p","log2FC":-0.65,"padj":0.019,"targets":"TRAF6, IRAK1","pathway":"NF-kB signalling"},
66
+ {"miRNA":"hsa-miR-21-5p","log2FC":-0.55,"padj":0.027,"targets":"PTEN, PDCD4","pathway":"Apoptosis"},
67
+ {"miRNA":"hsa-miR-17-5p","log2FC":-0.47,"padj":0.036,"targets":"RB1, E2F1","pathway":"Cell cycle"},
68
+ {"miRNA":"hsa-miR-34a-5p","log2FC":-0.41,"padj":0.049,"targets":"BCL2, CDK6","pathway":"p53 axis"},
69
+ ],
70
+ "TP53": [
71
+ {"miRNA":"hsa-miR-34a-5p","log2FC":-1.10,"padj":0.001,"targets":"BCL2, CDK6","pathway":"p53-miR-34 axis"},
72
+ {"miRNA":"hsa-miR-192-5p","log2FC":-0.90,"padj":0.005,"targets":"MDM2, DHFR","pathway":"p53 feedback"},
73
+ {"miRNA":"hsa-miR-145-5p","log2FC":-0.75,"padj":0.012,"targets":"MYC, EGFR","pathway":"Growth suppression"},
74
+ {"miRNA":"hsa-miR-107","log2FC":-0.62,"padj":0.023,"targets":"CDK6, HIF1B","pathway":"Hypoxia / cell cycle"},
75
+ {"miRNA":"hsa-miR-215-5p","log2FC":-0.51,"padj":0.038,"targets":"DTL, DHFR","pathway":"DNA damage response"},
76
+ ],
77
+ }
78
+ SIRNA_DB = {
79
+ "LUAD": [
80
+ {"Gene":"SPC24","dCERES":-0.175,"log2FC":1.13,"Drug_status":"Novel","siRNA":"GCAGCUGAAGAAACUGAAU"},
81
+ {"Gene":"BUB1B","dCERES":-0.119,"log2FC":1.12,"Drug_status":"Novel","siRNA":"CCAAAGAGCUGAAGAACAU"},
82
+ {"Gene":"CDC45","dCERES":-0.144,"log2FC":1.26,"Drug_status":"Novel","siRNA":"GCAUCAAGAUGAAGGAGAU"},
83
+ {"Gene":"PLK1","dCERES":-0.239,"log2FC":1.03,"Drug_status":"Clinical","siRNA":"GACGCUCAAGAUGCAGAUU"},
84
+ {"Gene":"CDK1","dCERES":-0.201,"log2FC":1.00,"Drug_status":"Clinical","siRNA":"GCAGAAGCACUGAAGAUUU"},
85
+ ],
86
+ "BRCA": [
87
+ {"Gene":"AURKA","dCERES":-0.165,"log2FC":1.20,"Drug_status":"Clinical","siRNA":"GCACUGAAGAUGCAGAAUU"},
88
+ {"Gene":"AURKB","dCERES":-0.140,"log2FC":1.15,"Drug_status":"Clinical","siRNA":"CCUGAAGACGCUCAAGGUU"},
89
+ {"Gene":"CENPW","dCERES":-0.125,"log2FC":0.95,"Drug_status":"Novel","siRNA":"GCAGAAGCACUGAAGAUUU"},
90
+ {"Gene":"RFC2","dCERES":-0.136,"log2FC":0.50,"Drug_status":"Novel","siRNA":"GCAAGAUGCAGAAGCACUU"},
91
+ {"Gene":"TYMS","dCERES":-0.131,"log2FC":0.72,"Drug_status":"Approved","siRNA":"GGACGCUCAAGAUGCAGAU"},
92
+ ],
93
+ "COAD": [
94
+ {"Gene":"KRAS","dCERES":-0.210,"log2FC":0.80,"Drug_status":"Clinical","siRNA":"GCUGGAGCUGGUGGUAGUU"},
95
+ {"Gene":"WEE1","dCERES":-0.180,"log2FC":1.05,"Drug_status":"Clinical","siRNA":"GCAGCUGAAGAAACUGAAU"},
96
+ {"Gene":"CHEK1","dCERES":-0.155,"log2FC":0.90,"Drug_status":"Clinical","siRNA":"CCAAAGAGCUGAAGAACAU"},
97
+ {"Gene":"RFC2","dCERES":-0.130,"log2FC":0.55,"Drug_status":"Novel","siRNA":"GCAUCAAGAUGAAGGAGAU"},
98
+ {"Gene":"PKMYT1","dCERES":-0.122,"log2FC":1.07,"Drug_status":"Clinical","siRNA":"GACGCUCAAGAUGCAGAUU"},
99
+ ],
100
+ }
101
+ CERNA = [
102
+ {"lncRNA":"CYTOR","miRNA":"hsa-miR-138-5p","target":"AKT1","pathway":"TREM2 core signaling"},
103
+ {"lncRNA":"CYTOR","miRNA":"hsa-miR-138-5p","target":"NFKB1","pathway":"Neuroinflammation"},
104
+ {"lncRNA":"GAS5","miRNA":"hsa-miR-21-5p","target":"PTEN","pathway":"Neuroinflammation"},
105
+ {"lncRNA":"GAS5","miRNA":"hsa-miR-222-3p","target":"IL1B","pathway":"Neuroinflammation"},
106
+ {"lncRNA":"HOTAIRM1","miRNA":"hsa-miR-9-5p","target":"TREM2","pathway":"Direct TREM2 regulation"},
107
+ ]
108
+ ASO = [
109
+ {"lncRNA":"GAS5","position":119,"accessibility":0.653,"GC_pct":50,"Tm":47.2,"priority":"HIGH"},
110
+ {"lncRNA":"CYTOR","position":507,"accessibility":0.653,"GC_pct":50,"Tm":46.8,"priority":"HIGH"},
111
+ {"lncRNA":"HOTAIRM1","position":234,"accessibility":0.621,"GC_pct":44,"Tm":44.1,"priority":"MEDIUM"},
112
+ {"lncRNA":"LINC00847","position":89,"accessibility":0.598,"GC_pct":56,"Tm":48.3,"priority":"MEDIUM"},
113
+ {"lncRNA":"ZFAS1","position":312,"accessibility":0.571,"GC_pct":48,"Tm":45.5,"priority":"MEDIUM"},
114
+ ]
115
+ FGFR3 = {
116
+ "P1 (hairpin loop)": [
117
+ {"Compound":"CHEMBL1575701","RNA_score":0.809,"Toxicity":0.01,"Final_score":0.793},
118
+ {"Compound":"CHEMBL15727","RNA_score":0.805,"Toxicity":0.00,"Final_score":0.789},
119
+ {"Compound":"Thioguanine","RNA_score":0.888,"Toxicity":32.5,"Final_score":0.742},
120
+ {"Compound":"Deazaguanine","RNA_score":0.888,"Toxicity":35.0,"Final_score":0.735},
121
+ {"Compound":"CHEMBL441","RNA_score":0.775,"Toxicity":5.2,"Final_score":0.721},
122
+ ],
123
+ "P10 (G-quadruplex)": [
124
+ {"Compound":"CHEMBL15727","RNA_score":0.805,"Toxicity":0.00,"Final_score":0.789},
125
+ {"Compound":"CHEMBL5411515","RNA_score":0.945,"Toxicity":37.1,"Final_score":0.761},
126
+ {"Compound":"CHEMBL90","RNA_score":0.760,"Toxicity":2.1,"Final_score":0.745},
127
+ {"Compound":"CHEMBL102","RNA_score":0.748,"Toxicity":8.4,"Final_score":0.712},
128
+ {"Compound":"Berberine","RNA_score":0.735,"Toxicity":3.2,"Final_score":0.708},
129
+ ],
130
+ }
131
+ VARIANT_DB = {
132
+ "BRCA1:p.R1699Q": {"score":0.03,"cls":"Benign","conf":"High"},
133
+ "BRCA1:p.R1699W": {"score":0.97,"cls":"Pathogenic","conf":"High"},
134
+ "BRCA2:p.D2723A": {"score":0.999,"cls":"Pathogenic","conf":"High"},
135
+ "TP53:p.R248W": {"score":0.998,"cls":"Pathogenic","conf":"High"},
136
+ "TP53:p.R248Q": {"score":0.995,"cls":"Pathogenic","conf":"High"},
137
+ "EGFR:p.L858R": {"score":0.96,"cls":"Pathogenic","conf":"High"},
138
+ "ALK:p.F1174L": {"score":0.94,"cls":"Pathogenic","conf":"High"},
139
+ }
140
+ PLAIN = {
141
+ "Pathogenic": "This variant is likely to cause disease. Clinical follow-up is strongly recommended.",
142
+ "Likely Pathogenic": "This variant is probably harmful. Discuss with your doctor.",
143
+ "Benign": "This variant is likely harmless. Common in the general population.",
144
+ "Likely Benign": "This variant is probably harmless. No strong reason for concern.",
145
+ }
146
+ BM_W = {
147
+ "CTHRC1":0.18,"FHL2":0.15,"LDHA":0.14,"P4HA1":0.13,
148
+ "SERPINH1":0.12,"ABCA8":-0.11,"CA4":-0.10,"CKB":-0.09,
149
+ "NNMT":0.08,"CACNA2D2":-0.07
150
+ }
151
+ 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")
159
+ return df
160
+
161
+ def predict_sirna(cancer):
162
+ df = pd.DataFrame(SIRNA_DB.get(cancer, []))
163
+ log_entry("S1-B · R2a · siRNA", cancer, f"{len(df)} targets")
164
+ return df
165
+
166
+ def get_lncrna():
167
+ log_entry("S1-B · R3a · lncRNA", "load", "ceRNA")
168
+ return pd.DataFrame(CERNA)
169
+
170
+ def get_aso():
171
+ log_entry("S1-B · R3b · ASO", "load", "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()
189
+ if hgvs in VARIANT_DB:
190
+ r = VARIANT_DB[hgvs]; 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 "Likely Pathogenic" if score > 0.4 else "Benign"
198
+ conf = "High" if (sift < 0.01 or sift > 0.9) else "Moderate"
199
+ colour = RED if "Pathogenic" in cls else GRN
200
+ icon = "⚠️ WARNING" if "Pathogenic" in cls else "✅ OK"
201
+ log_entry("S1-A · R1a · OpenVariant", hgvs or f"SIFT={sift}", f"{cls} score={score}")
202
+ return (
203
+ f"<div style=\'background:{CARD};padding:16px;border-radius:8px;font-family:sans-serif;color:{TXT}\'>"
204
+ f"<p style=\'font-size:11px;color:{DIM};margin:0 0 8px\'>S1-A · R1a · OpenVariant</p>"
205
+ f"<h3 style=\'color:{colour};margin:0 0 8px\'>{icon} {cls}</h3>"
206
+ f"<p>Score: <b>{score:.3f}</b> &nbsp;|&nbsp; Confidence: <b>{conf}</b></p>"
207
+ f"<div style=\'background:{BORDER};border-radius:4px;height:14px\'>"
208
+ f"<div style=\'background:{colour};height:14px;border-radius:4px;width:{int(score*100)}%\'></div></div>"
209
+ f"<p style=\'margin-top:12px\'>{PLAIN.get(cls,'')}</p>"
210
+ f"<p style=\'font-size:11px;color:{DIM}\'>Research only. Not clinical advice.</p></div>"
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(
358
+ "Freq_pct" if sort_by == "Frequency" else "Drug_status", ascending=False)
359
+ log_entry("S1-F · R1a · DIPG", sort_by, f"{len(df)} variants")
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)
381
+ log_entry("S1-F · R2a · UVM", "load", f"{len(df)} 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):
438
+ return (
439
+ f"<div style=\'background:{BG};border:1px solid {BORDER};padding:14px 18px;"
440
+ f"border-radius:8px;margin-bottom:12px;\'>"
441
+ f"<div style=\'display:flex;align-items:baseline;gap:10px;\'>"
442
+ f"<span style=\'color:{ACC2};font-size:16px;font-weight:700\'>{code}</span>"
443
+ f"<span style=\'color:{TXT};font-size:14px;font-weight:600\'>{name}</span>"
444
+ f"<span style=\'color:{DIM};font-size:12px\'>{tagline}</span></div>"
445
+ f"<div style=\'margin-top:8px;font-size:12px;color:{DIM}\'>{projects_html}</div>"
446
+ f"</div>"
447
+ )
448
+
449
+ def proj_badge(code, title, metric=""):
450
+ m = (f"<span style=\'background:#0f2a3f;color:{ACC2};padding:1px 7px;border-radius:3px;"
451
+ f"font-size:10px;margin-left:6px\'>{metric}</span>") if metric else ""
452
+ return (
453
+ f"<div style=\'background:{CARD};border-left:3px solid {ACC};"
454
+ f"padding:8px 12px;border-radius:0 6px 6px 0;margin-bottom:8px;\'>"
455
+ f"<span style=\'color:{DIM};font-size:11px\'>{code}</span>{m}<br>"
456
+ f"<span style=\'color:{TXT};font-size:14px;font-weight:600\'>{title}</span>"
457
+ f"</div>"
458
+ )
459
+
460
+ # ========== CSS ==========
461
+ css = f"""
462
+ body, .gradio-container {{ background: {BG} !important; color: {TXT} !important; }}
463
+ .outer-tabs .tab-nav button {{
464
+ color: {TXT} !important;
465
+ background: {CARD} !important;
466
+ font-size: 13px !important;
467
+ font-weight: 600 !important;
468
+ padding: 8px 16px !important;
469
+ border-radius: 6px 6px 0 0 !important;
470
+ }}
471
+ .outer-tabs .tab-nav button.selected {{
472
+ border-bottom: 3px solid {ACC} !important;
473
+ color: {ACC} !important;
474
+ background: {BG} !important;
475
+ }}
476
+ .inner-tabs .tab-nav button {{
477
+ color: {DIM} !important;
478
+ background: {BG} !important;
479
+ font-size: 12px !important;
480
+ font-weight: 500 !important;
481
+ padding: 5px 12px !important;
482
+ border-radius: 4px 4px 0 0 !important;
483
+ border: 1px solid {BORDER} !important;
484
+ border-bottom: none !important;
485
+ margin-right: 3px !important;
486
+ }}
487
+ .inner-tabs .tab-nav button.selected {{
488
+ color: {ACC2} !important;
489
+ background: {CARD} !important;
490
+ border-color: {ACC2} !important;
491
+ border-bottom: none !important;
492
+ }}
493
+ .inner-tabs > .tabitem {{
494
+ background: {CARD} !important;
495
+ border: 1px solid {BORDER} !important;
496
+ border-radius: 0 6px 6px 6px !important;
497
+ padding: 14px !important;
498
+ }}
499
+ h1, h2, h3 {{ color: {ACC} !important; }}
500
+ .gr-button-primary {{ background: {ACC} !important; border: none !important; }}
501
+ footer {{ display: none !important; }}
502
+ """
503
+
504
+ # ========== MAP HTML ==========
505
+ MAP_HTML = f"""
506
+ <div style="background:{CARD};padding:22px;border-radius:8px;font-family:monospace;font-size:13px;line-height:2.0;color:{TXT}">
507
+ <span style="color:{ACC};font-size:16px;font-weight:bold">K R&D Lab · S1 Biomedical</span>
508
+ <span style="color:{DIM};font-size:11px;margin-left:12px">Science Sphere — sub-direction 1</span>
509
+ <br><br>
510
+ <span style="color:{ACC2};font-weight:600">S1-A · PHYLO-GENOMICS</span> — What breaks in DNA<br>
511
+ &nbsp;&nbsp;&nbsp;├─ <b>S1-A · R1a</b> OpenVariant <span style="color:{GRN}"> AUC=0.939 ✅</span><br>
512
+ &nbsp;&nbsp;&nbsp;└─ <b>S1-A · R1b</b> Somatic classifier <span style="color:#f59e0b"> 🔶 In progress</span><br><br>
513
+ <span style="color:{ACC2};font-weight:600">S1-B · PHYLO-RNA</span> — How to silence it via RNA<br>
514
+ &nbsp;&nbsp;&nbsp;├─ <b>S1-B · R1a</b> miRNA silencing <span style="color:{GRN}"> ✅</span><br>
515
+ &nbsp;&nbsp;&nbsp;├─ <b>S1-B · R2a</b> siRNA synthetic lethal <span style="color:{GRN}"> ✅</span><br>
516
+ &nbsp;&nbsp;&nbsp;├─ <b>S1-B · R3a</b> lncRNA-TREM2 ceRNA <span style="color:{GRN}"> ✅</span><br>
517
+ &nbsp;&nbsp;&nbsp;└─ <b>S1-B · R3b</b> ASO designer <span style="color:{GRN}"> ✅</span><br><br>
518
+ <span style="color:{ACC2};font-weight:600">S1-C · PHYLO-DRUG</span> — Which molecule treats it<br>
519
+ &nbsp;&nbsp;&nbsp;├─ <b>S1-C · R1a</b> FGFR3 RNA-directed compounds <span style="color:{GRN}"> ✅</span><br>
520
+ &nbsp;&nbsp;&nbsp;├─ <b>S1-C · R1b</b> Synthetic lethal drug mapping <span style="color:#f59e0b"> 🔶</span><br>
521
+ &nbsp;&nbsp;&nbsp;└─ <b>S1-C · R2a</b> m6A × Ferroptosis × Circadian <span style="color:{DIM}"> 🔴 Frontier</span><br><br>
522
+ <span style="color:{ACC2};font-weight:600">S1-D · PHYLO-LNP</span> — How to deliver the drug<br>
523
+ &nbsp;&nbsp;&nbsp;├─ <b>S1-D · R1a</b> LNP corona (serum) <span style="color:{GRN}"> AUC=0.791 ✅</span><br>
524
+ &nbsp;&nbsp;&nbsp;├─ <b>S1-D · R2a</b> Flow corona — Vroman effect <span style="color:{GRN}"> ✅</span><br>
525
+ &nbsp;&nbsp;&nbsp;├─ <b>S1-D · R3a</b> LNP brain / BBB / ApoE <span style="color:{GRN}"> ✅</span><br>
526
+ &nbsp;&nbsp;&nbsp;├─ <b>S1-D · R4a</b> AutoCorona NLP <span style="color:{GRN}"> F1=0.71 ✅</span><br>
527
+ &nbsp;&nbsp;&nbsp;└─ <b>S1-D · R5a</b> CSF · Vitreous · Bone Marrow <span style="color:{DIM}"> 🔴 0 prior studies</span><br><br>
528
+ <span style="color:{ACC2};font-weight:600">S1-E · PHYLO-BIOMARKERS</span> — Detect without biopsy<br>
529
+ &nbsp;&nbsp;&nbsp;├─ <b>S1-E · R1a</b> Liquid Biopsy classifier <span style="color:{GRN}"> AUC=0.992* ✅</span><br>
530
+ &nbsp;&nbsp;&nbsp;└─ <b>S1-E · R1b</b> Protein panel validator <span style="color:#f59e0b"> 🔶</span><br><br>
531
+ <span style="color:{ACC2};font-weight:600">S1-F · PHYLO-RARE</span> — Where almost nobody has looked yet<br>
532
+ &nbsp;&nbsp;&nbsp;├─ <b>S1-F · R1a</b> DIPG toolkit (H3K27M + CSF LNP + Circadian) <span style="color:#f59e0b"> 🔶</span><br>
533
+ &nbsp;&nbsp;&nbsp;├─ <b>S1-F · R2a</b> UVM toolkit (GNAQ/GNA11 + vitreous + m6A) <span style="color:#f59e0b"> 🔶</span><br>
534
+ &nbsp;&nbsp;&nbsp;└─ <b>S1-F · R3a</b> pAML toolkit (FLT3-ITD + BM niche + ferroptosis) <span style="color:#f59e0b"> 🔶</span><br><br>
535
+ <span style="color:{DIM};font-size:11px">✅ Active · 🔶 In progress · 🔴 Planned</span>
536
+ </div>
537
+ """
538
+
539
+ # ========== ІНТЕРФЕЙС ==========
540
+ with gr.Blocks(css=css, title="K R&D Lab · S1 Biomedical") as demo:
541
+ gr.Markdown(
542
+ "# 🔬 K R&D Lab · Science Sphere — S1 Biomedical\n"
543
+ "**Oksana Kolisnyk** · [KOSATIKS GROUP](https://kosatiks-group.pp.ua) · "
544
+ "*Research only. Not clinical advice.*"
545
+ )
546
+
547
+ with gr.Tabs(elem_classes="outer-tabs"):
548
+ # 🗺️ Lab Map
549
+ with gr.TabItem("🗺️ Lab Map"):
550
+ gr.HTML(MAP_HTML)
551
+
552
+ # 🧬 S1-A · PHYLO-GENOMICS
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")
556
+ gr.Markdown("**Or enter functional scores manually:**")
557
+ with gr.Row():
558
+ sift = gr.Slider(0,1,value=0.5,step=0.01,label="SIFT (0=damaging)")
559
+ pp = gr.Slider(0,1,value=0.5,step=0.01,label="PolyPhen-2")
560
+ gn = gr.Slider(0,0.01,value=0.001,step=0.0001,label="gnomAD AF")
561
+ b_v = gr.Button("Predict Pathogenicity", variant="primary")
562
+ o_v = gr.HTML()
563
+ gr.Examples([["BRCA1:p.R1699Q",0.82,0.05,0.0012],
564
+ ["TP53:p.R248W",0.00,1.00,0.0],
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
+ # with gr.TabItem("S1-A · R1b · Somatic Classifier 🔶"):
570
+ # gr.HTML(proj_badge("S1-A · R1b", "Somatic Mutation Classifier", "🔶 In progress"))
571
+ # gr.Markdown("> This module is in active development. Coming in the next release.")
572
+
573
+ # ... (всі інші вкладки закоментуйте аналогічно)
574
+
575
+ # 📓 Journal
576
+ with gr.TabItem("📓 Journal"):
577
+ gr.Markdown("### Lab Journal")
578
+ with gr.Row():
579
+ note_text = gr.Textbox(label="📝 Observation", placeholder="What did you discover?", lines=3)
580
+ note_tab = gr.Textbox(label="Project code (e.g. S1-A·R1a)", value="General")
581
+ note_last = gr.Textbox(visible=False)
582
+ save_btn = gr.Button("💾 Save", variant="primary")
583
+ save_msg = gr.Markdown()
584
+ journal_df = gr.Dataframe(label="📋 Full History", value=load_journal(), interactive=False)
585
+ refresh_btn = gr.Button("🔄 Refresh")
586
+ refresh_btn.click(load_journal, [], journal_df)
587
+ save_btn.click(save_note, [note_text,note_tab,note_last], [save_msg,journal_df])
588
+
589
+ # 📚 Learning
590
+ with gr.TabItem("📚 Learning"):
591
+ gr.Markdown("""
592
+ ## 🧪 Guided Investigations
593
+ > 🟢 Beginner → 🟡 Intermediate → 🔴 Advanced
594
+
595
+ **S1-A · R1a** OpenVariant – try BRCA1:p.R1699Q vs R1699W
596
+ **S1-D · R1a** Corona – vary PEG% and observe dominant protein
597
+ **S1-D · R2a** Flow Corona – compare flow 0 vs 40 cm/s
598
+ **S1-B · R2a** siRNA – count "Novel" targets across cancer types
599
+ **S1-E · R1a** Liquid Biopsy – find minimal signal for CANCER
600
+ ... (more cases)
601
+ """)
602
+
603
+ gr.Markdown(
604
+ "---\n**K R&D Lab** · MIT License · "
605
+ "[GitHub](https://github.com/K-RnD-Lab) · "
606
+ "[HuggingFace](https://huggingface.co/K-RnD-Lab) · "
607
+ "[KOSATIKS GROUP](https://kosatiks-group.pp.ua)"
608
+ )
609
+
610
+ demo.queue()
611
+ demo.launch(show_api=False)