TEZv commited on
Commit
0ec2eaf
·
verified ·
1 Parent(s): f5c64e5

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +752 -17
app.py CHANGED
@@ -10,24 +10,759 @@ from PIL import Image
10
  from datetime import datetime
11
  from pathlib import Path
12
 
13
- # ... (тут ваші кольори, логування, бази даних та функції – без змін)
 
 
 
 
 
 
 
 
 
14
 
15
- with gr.Blocks(title="K R&D Lab") as demo:
16
- gr.Markdown("# K R&D Lab")
17
- with gr.Tabs():
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
18
  with gr.TabItem("S1-A · R1a · OpenVariant"):
19
- hgvs = gr.Textbox(label="HGVS")
20
- sift = gr.Slider(0,1,value=0.5,label="SIFT")
21
- pp = gr.Slider(0,1,value=0.5,label="PolyPhen")
22
- gn = gr.Slider(0,0.01,value=0.001,label="gnomAD")
23
- btn = gr.Button("Predict")
24
- out = gr.HTML()
25
- btn.click(predict_variant, [hgvs,sift,pp,gn], out)
26
- with gr.TabItem("S1-B · R1a · miRNA"):
27
- gene = gr.Dropdown(["BRCA2","BRCA1","TP53"], value="BRCA2", label="Gene")
28
- btn2 = gr.Button("Find")
29
- out2 = gr.Dataframe()
30
- btn2.click(predict_mirna, [gene], out2)
31
- # ... інші вкладки аналогічно, але без вкладених Tabs
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
32
 
 
33
  demo.launch()
 
10
  from datetime import datetime
11
  from pathlib import Path
12
 
13
+ # ========== Кольори ==========
14
+ BG = "#0f172a"
15
+ CARD = "#1e293b"
16
+ ACC = "#f97316"
17
+ ACC2 = "#38bdf8"
18
+ TXT = "#f1f5f9"
19
+ GRN = "#22c55e"
20
+ RED = "#ef4444"
21
+ DIM = "#8e9bae"
22
+ BORDER = "#334155"
23
 
24
+ # ========== Логування ==========
25
+ LOG_PATH = Path("/tmp/lab_journal.csv")
26
+
27
+ def log_entry(tab, inputs, result, note=""):
28
+ try:
29
+ write_header = not LOG_PATH.exists()
30
+ with open(LOG_PATH, "a", newline="", encoding="utf-8") as f:
31
+ w = csv.DictWriter(f, fieldnames=["timestamp","tab","inputs","result","note"])
32
+ if write_header: w.writeheader()
33
+ w.writerow({"timestamp": datetime.now().strftime("%Y-%m-%d %H:%M"),
34
+ "tab": tab, "inputs": str(inputs),
35
+ "result": str(result)[:200], "note": note})
36
+ except Exception: pass
37
+
38
+ def load_journal():
39
+ try:
40
+ if not LOG_PATH.exists():
41
+ return pd.DataFrame(columns=["timestamp","tab","inputs","result","note"])
42
+ return pd.read_csv(LOG_PATH)
43
+ except Exception:
44
+ return pd.DataFrame(columns=["timestamp","tab","inputs","result","note"])
45
+
46
+ def save_note(note, tab, last_result):
47
+ log_entry(tab, "", last_result, note)
48
+ return "✅ Saved!", load_journal()
49
+
50
+ # ========== БАЗИ ДАНИХ ==========
51
+ MIRNA_DB = {
52
+ "BRCA2": [
53
+ {"miRNA":"hsa-miR-148a-3p","log2FC":-0.70,"padj":0.013,"targets":"DNMT1, AKT2","pathway":"Epigenetic reprogramming"},
54
+ {"miRNA":"hsa-miR-30e-5p","log2FC":-0.49,"padj":0.032,"targets":"MYC, KRAS","pathway":"Oncogene suppression"},
55
+ {"miRNA":"hsa-miR-551b-3p","log2FC":-0.59,"padj":0.048,"targets":"SMAD4, CDK6","pathway":"TGF-beta / CDK4/6"},
56
+ {"miRNA":"hsa-miR-22-3p","log2FC":-0.43,"padj":0.041,"targets":"HIF1A, PTEN","pathway":"Hypoxia / PI3K"},
57
+ {"miRNA":"hsa-miR-200c-3p","log2FC":-0.38,"padj":0.044,"targets":"ZEB1, ZEB2","pathway":"EMT suppression"},
58
+ ],
59
+ "BRCA1": [
60
+ {"miRNA":"hsa-miR-155-5p","log2FC":-0.81,"padj":0.008,"targets":"SHIP1, SOCS1","pathway":"Immune evasion"},
61
+ {"miRNA":"hsa-miR-146a-5p","log2FC":-0.65,"padj":0.019,"targets":"TRAF6, IRAK1","pathway":"NF-kB signalling"},
62
+ {"miRNA":"hsa-miR-21-5p","log2FC":-0.55,"padj":0.027,"targets":"PTEN, PDCD4","pathway":"Apoptosis"},
63
+ {"miRNA":"hsa-miR-17-5p","log2FC":-0.47,"padj":0.036,"targets":"RB1, E2F1","pathway":"Cell cycle"},
64
+ {"miRNA":"hsa-miR-34a-5p","log2FC":-0.41,"padj":0.049,"targets":"BCL2, CDK6","pathway":"p53 axis"},
65
+ ],
66
+ "TP53": [
67
+ {"miRNA":"hsa-miR-34a-5p","log2FC":-1.10,"padj":0.001,"targets":"BCL2, CDK6","pathway":"p53-miR-34 axis"},
68
+ {"miRNA":"hsa-miR-192-5p","log2FC":-0.90,"padj":0.005,"targets":"MDM2, DHFR","pathway":"p53 feedback"},
69
+ {"miRNA":"hsa-miR-145-5p","log2FC":-0.75,"padj":0.012,"targets":"MYC, EGFR","pathway":"Growth suppression"},
70
+ {"miRNA":"hsa-miR-107","log2FC":-0.62,"padj":0.023,"targets":"CDK6, HIF1B","pathway":"Hypoxia / cell cycle"},
71
+ {"miRNA":"hsa-miR-215-5p","log2FC":-0.51,"padj":0.038,"targets":"DTL, DHFR","pathway":"DNA damage response"},
72
+ ],
73
+ }
74
+ SIRNA_DB = {
75
+ "LUAD": [
76
+ {"Gene":"SPC24","dCERES":-0.175,"log2FC":1.13,"Drug_status":"Novel","siRNA":"GCAGCUGAAGAAACUGAAU"},
77
+ {"Gene":"BUB1B","dCERES":-0.119,"log2FC":1.12,"Drug_status":"Novel","siRNA":"CCAAAGAGCUGAAGAACAU"},
78
+ {"Gene":"CDC45","dCERES":-0.144,"log2FC":1.26,"Drug_status":"Novel","siRNA":"GCAUCAAGAUGAAGGAGAU"},
79
+ {"Gene":"PLK1","dCERES":-0.239,"log2FC":1.03,"Drug_status":"Clinical","siRNA":"GACGCUCAAGAUGCAGAUU"},
80
+ {"Gene":"CDK1","dCERES":-0.201,"log2FC":1.00,"Drug_status":"Clinical","siRNA":"GCAGAAGCACUGAAGAUUU"},
81
+ ],
82
+ "BRCA": [
83
+ {"Gene":"AURKA","dCERES":-0.165,"log2FC":1.20,"Drug_status":"Clinical","siRNA":"GCACUGAAGAUGCAGAAUU"},
84
+ {"Gene":"AURKB","dCERES":-0.140,"log2FC":1.15,"Drug_status":"Clinical","siRNA":"CCUGAAGACGCUCAAGGUU"},
85
+ {"Gene":"CENPW","dCERES":-0.125,"log2FC":0.95,"Drug_status":"Novel","siRNA":"GCAGAAGCACUGAAGAUUU"},
86
+ {"Gene":"RFC2","dCERES":-0.136,"log2FC":0.50,"Drug_status":"Novel","siRNA":"GCAAGAUGCAGAAGCACUU"},
87
+ {"Gene":"TYMS","dCERES":-0.131,"log2FC":0.72,"Drug_status":"Approved","siRNA":"GGACGCUCAAGAUGCAGAU"},
88
+ ],
89
+ "COAD": [
90
+ {"Gene":"KRAS","dCERES":-0.210,"log2FC":0.80,"Drug_status":"Clinical","siRNA":"GCUGGAGCUGGUGGUAGUU"},
91
+ {"Gene":"WEE1","dCERES":-0.180,"log2FC":1.05,"Drug_status":"Clinical","siRNA":"GCAGCUGAAGAAACUGAAU"},
92
+ {"Gene":"CHEK1","dCERES":-0.155,"log2FC":0.90,"Drug_status":"Clinical","siRNA":"CCAAAGAGCUGAAGAACAU"},
93
+ {"Gene":"RFC2","dCERES":-0.130,"log2FC":0.55,"Drug_status":"Novel","siRNA":"GCAUCAAGAUGAAGGAGAU"},
94
+ {"Gene":"PKMYT1","dCERES":-0.122,"log2FC":1.07,"Drug_status":"Clinical","siRNA":"GACGCUCAAGAUGCAGAUU"},
95
+ ],
96
+ }
97
+ CERNA = [
98
+ {"lncRNA":"CYTOR","miRNA":"hsa-miR-138-5p","target":"AKT1","pathway":"TREM2 core signaling"},
99
+ {"lncRNA":"CYTOR","miRNA":"hsa-miR-138-5p","target":"NFKB1","pathway":"Neuroinflammation"},
100
+ {"lncRNA":"GAS5","miRNA":"hsa-miR-21-5p","target":"PTEN","pathway":"Neuroinflammation"},
101
+ {"lncRNA":"GAS5","miRNA":"hsa-miR-222-3p","target":"IL1B","pathway":"Neuroinflammation"},
102
+ {"lncRNA":"HOTAIRM1","miRNA":"hsa-miR-9-5p","target":"TREM2","pathway":"Direct TREM2 regulation"},
103
+ ]
104
+ ASO = [
105
+ {"lncRNA":"GAS5","position":119,"accessibility":0.653,"GC_pct":50,"Tm":47.2,"priority":"HIGH"},
106
+ {"lncRNA":"CYTOR","position":507,"accessibility":0.653,"GC_pct":50,"Tm":46.8,"priority":"HIGH"},
107
+ {"lncRNA":"HOTAIRM1","position":234,"accessibility":0.621,"GC_pct":44,"Tm":44.1,"priority":"MEDIUM"},
108
+ {"lncRNA":"LINC00847","position":89,"accessibility":0.598,"GC_pct":56,"Tm":48.3,"priority":"MEDIUM"},
109
+ {"lncRNA":"ZFAS1","position":312,"accessibility":0.571,"GC_pct":48,"Tm":45.5,"priority":"MEDIUM"},
110
+ ]
111
+ FGFR3 = {
112
+ "P1 (hairpin loop)": [
113
+ {"Compound":"CHEMBL1575701","RNA_score":0.809,"Toxicity":0.01,"Final_score":0.793},
114
+ {"Compound":"CHEMBL15727","RNA_score":0.805,"Toxicity":0.00,"Final_score":0.789},
115
+ {"Compound":"Thioguanine","RNA_score":0.888,"Toxicity":32.5,"Final_score":0.742},
116
+ {"Compound":"Deazaguanine","RNA_score":0.888,"Toxicity":35.0,"Final_score":0.735},
117
+ {"Compound":"CHEMBL441","RNA_score":0.775,"Toxicity":5.2,"Final_score":0.721},
118
+ ],
119
+ "P10 (G-quadruplex)": [
120
+ {"Compound":"CHEMBL15727","RNA_score":0.805,"Toxicity":0.00,"Final_score":0.789},
121
+ {"Compound":"CHEMBL5411515","RNA_score":0.945,"Toxicity":37.1,"Final_score":0.761},
122
+ {"Compound":"CHEMBL90","RNA_score":0.760,"Toxicity":2.1,"Final_score":0.745},
123
+ {"Compound":"CHEMBL102","RNA_score":0.748,"Toxicity":8.4,"Final_score":0.712},
124
+ {"Compound":"Berberine","RNA_score":0.735,"Toxicity":3.2,"Final_score":0.708},
125
+ ],
126
+ }
127
+ VARIANT_DB = {
128
+ "BRCA1:p.R1699Q": {"score":0.03,"cls":"Benign","conf":"High"},
129
+ "BRCA1:p.R1699W": {"score":0.97,"cls":"Pathogenic","conf":"High"},
130
+ "BRCA2:p.D2723A": {"score":0.999,"cls":"Pathogenic","conf":"High"},
131
+ "TP53:p.R248W": {"score":0.998,"cls":"Pathogenic","conf":"High"},
132
+ "TP53:p.R248Q": {"score":0.995,"cls":"Pathogenic","conf":"High"},
133
+ "EGFR:p.L858R": {"score":0.96,"cls":"Pathogenic","conf":"High"},
134
+ "ALK:p.F1174L": {"score":0.94,"cls":"Pathogenic","conf":"High"},
135
+ }
136
+ PLAIN = {
137
+ "Pathogenic": "This variant is likely to cause disease. Clinical follow-up is strongly recommended.",
138
+ "Likely Pathogenic": "This variant is probably harmful. Discuss with your doctor.",
139
+ "Benign": "This variant is likely harmless. Common in the general population.",
140
+ "Likely Benign": "This variant is probably harmless. No strong reason for concern.",
141
+ }
142
+ BM_W = {
143
+ "CTHRC1":0.18,"FHL2":0.15,"LDHA":0.14,"P4HA1":0.13,
144
+ "SERPINH1":0.12,"ABCA8":-0.11,"CA4":-0.10,"CKB":-0.09,
145
+ "NNMT":0.08,"CACNA2D2":-0.07
146
+ }
147
+ PROTEINS = ["albumin","apolipoprotein","fibrinogen","vitronectin",
148
+ "clusterin","igm","iga","igg","complement","transferrin",
149
+ "alpha-2-macroglobulin"]
150
+
151
+ # ========== ФУНКЦІЇ ПРЕДИКЦІЇ ==========
152
+ def predict_mirna(gene):
153
+ df = pd.DataFrame(MIRNA_DB.get(gene, []))
154
+ log_entry("S1-B · R1a · miRNA", gene, f"{len(df)} miRNAs")
155
+ return df
156
+
157
+ def predict_sirna(cancer):
158
+ df = pd.DataFrame(SIRNA_DB.get(cancer, []))
159
+ log_entry("S1-B · R2a · siRNA", cancer, f"{len(df)} targets")
160
+ return df
161
+
162
+ def get_lncrna():
163
+ log_entry("S1-B · R3a · lncRNA", "load", "ceRNA")
164
+ return pd.DataFrame(CERNA)
165
+
166
+ def get_aso():
167
+ log_entry("S1-B · R3b · ASO", "load", "ASO")
168
+ return pd.DataFrame(ASO)
169
+
170
+ def predict_drug(pocket):
171
+ df = pd.DataFrame(FGFR3.get(pocket, []))
172
+ fig, ax = plt.subplots(figsize=(6, 4), facecolor=CARD)
173
+ ax.set_facecolor(CARD)
174
+ ax.barh(df["Compound"], df["Final_score"], color=ACC)
175
+ ax.set_xlabel("Final Score", color=TXT); ax.tick_params(colors=TXT)
176
+ for sp in ax.spines.values(): sp.set_edgecolor(BORDER)
177
+ ax.set_title(f"Top compounds — {pocket}", color=TXT, fontsize=10)
178
+ plt.tight_layout()
179
+ buf = BytesIO(); plt.savefig(buf, format="png", dpi=120, facecolor=CARD); plt.close(); buf.seek(0)
180
+ log_entry("S1-C · R1a · FGFR3", pocket, f"Top: {df.iloc[0]['Compound'] if len(df) else 'none'}")
181
+ return df, Image.open(buf)
182
+
183
+ def predict_variant(hgvs, sift, polyphen, gnomad):
184
+ hgvs = hgvs.strip()
185
+ if hgvs in VARIANT_DB:
186
+ r = VARIANT_DB[hgvs]; cls, conf, score = r["cls"], r["conf"], r["score"]
187
+ else:
188
+ score = 0.0
189
+ if sift < 0.05: score += 0.4
190
+ if polyphen > 0.85: score += 0.35
191
+ if gnomad < 0.0001: score += 0.25
192
+ score = round(score, 3)
193
+ cls = "Pathogenic" if score > 0.6 else "Likely Pathogenic" if score > 0.4 else "Benign"
194
+ conf = "High" if (sift < 0.01 or sift > 0.9) else "Moderate"
195
+ colour = RED if "Pathogenic" in cls else GRN
196
+ icon = "⚠️ WARNING" if "Pathogenic" in cls else "✅ OK"
197
+ log_entry("S1-A · R1a · OpenVariant", hgvs or f"SIFT={sift}", f"{cls} score={score}")
198
+ return (
199
+ f"<div style=\'background:{CARD};padding:16px;border-radius:8px;font-family:sans-serif;color:{TXT}\'>"
200
+ f"<p style=\'font-size:11px;color:{DIM};margin:0 0 8px\'>S1-A · R1a · OpenVariant</p>"
201
+ f"<h3 style=\'color:{colour};margin:0 0 8px\'>{icon} {cls}</h3>"
202
+ f"<p>Score: <b>{score:.3f}</b> &nbsp;|&nbsp; Confidence: <b>{conf}</b></p>"
203
+ f"<div style=\'background:{BORDER};border-radius:4px;height:14px\'>"
204
+ f"<div style=\'background:{colour};height:14px;border-radius:4px;width:{int(score*100)}%\'></div></div>"
205
+ f"<p style=\'margin-top:12px\'>{PLAIN.get(cls,'')}</p>"
206
+ f"<p style=\'font-size:11px;color:{DIM}\'>Research only. Not clinical advice.</p></div>"
207
+ )
208
+
209
+ def predict_corona(size, zeta, peg, lipid):
210
+ score = 0
211
+ if lipid == "Ionizable": score += 2
212
+ elif lipid == "Cationic": score += 1
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 · R1a · Corona", f"size={size},peg={peg}", f"dominant={dominant}")
219
+ return f"**Dominant corona protein:** {dominant}\n\n**Predicted efficacy:** {efficacy}\n\n**Score:** {score}/6"
220
+
221
+ def predict_cancer(c1,c2,c3,c4,c5,c6,c7,c8,c9,c10):
222
+ vals = [c1,c2,c3,c4,c5,c6,c7,c8,c9,c10]
223
+ names, weights = list(BM_W.keys()), list(BM_W.values())
224
+ raw = sum(v*w for v,w in zip(vals, weights))
225
+ prob = 1 / (1 + np.exp(-raw * 2))
226
+ label = "CANCER" if prob > 0.5 else "HEALTHY"
227
+ colour = RED if prob > 0.5 else GRN
228
+ contribs = [v*w for v,w in zip(vals, weights)]
229
+ fig, ax = plt.subplots(figsize=(6, 3.5), facecolor=CARD)
230
+ ax.set_facecolor(CARD)
231
+ ax.barh(names, contribs, color=[ACC if c > 0 else ACC2 for c in contribs])
232
+ ax.axvline(0, color=TXT, linewidth=0.8)
233
+ ax.set_xlabel("Contribution to cancer score", color=TXT)
234
+ ax.tick_params(colors=TXT, labelsize=8)
235
+ for sp in ax.spines.values(): sp.set_edgecolor(BORDER)
236
+ ax.set_title("Protein contributions", color=TXT, fontsize=10)
237
+ plt.tight_layout()
238
+ buf = BytesIO(); plt.savefig(buf, format="png", dpi=120, facecolor=CARD); plt.close(); buf.seek(0)
239
+ log_entry("S1-E · R1a · Liquid Biopsy", f"CTHRC1={c1},FHL2={c2}", f"{label} {prob:.2f}")
240
+ return (
241
+ f"<div style=\'background:{CARD};padding:14px;border-radius:8px;font-family:sans-serif;\'>"
242
+ f"<p style=\'font-size:11px;color:{DIM};margin:0 0 6px\'>S1-E · R1a · Liquid Biopsy</p>"
243
+ f"<span style=\'color:{colour};font-size:24px;font-weight:bold\'>{label}</span><br>"
244
+ f"<span style=\'color:{TXT};font-size:14px\'>Probability: {prob:.2f}</span></div>"
245
+ ), Image.open(buf)
246
+
247
+ def predict_flow(size, zeta, peg, charge, flow_rate):
248
+ csi = round(min((flow_rate/40)*0.6 + (peg/5)*0.2 + (1 if charge=="Cationic" else 0)*0.2, 1.0), 3)
249
+ stability = "High remodeling" if csi > 0.6 else "Medium" if csi > 0.3 else "Stable"
250
+ t = np.linspace(0, 60, 200)
251
+ kf, ks = 0.03*(1+flow_rate/40), 0.038*(1+flow_rate/40)
252
+ fig, ax = plt.subplots(figsize=(6, 3.5), facecolor=CARD)
253
+ ax.set_facecolor(CARD)
254
+ ax.plot(t, 60*np.exp(-0.03*t)+20, color="#60a5fa", ls="--", label="Albumin (static)")
255
+ ax.plot(t, 60*np.exp(-kf*t)+10, color="#60a5fa", label="Albumin (flow)")
256
+ ax.plot(t, 14*(1-np.exp(-0.038*t))+5, color=ACC, ls="--", label="ApoE (static)")
257
+ ax.plot(t, 20*(1-np.exp(-ks*t))+5, color=ACC, label="ApoE (flow)")
258
+ ax.set_xlabel("Time (min)", color=TXT); ax.set_ylabel("% Corona", color=TXT)
259
+ ax.tick_params(colors=TXT); ax.legend(fontsize=7, labelcolor=TXT, facecolor=CARD)
260
+ for sp in ax.spines.values(): sp.set_edgecolor(BORDER)
261
+ ax.set_title("Vroman Effect — flow vs static", color=TXT, fontsize=9)
262
+ plt.tight_layout()
263
+ buf = BytesIO(); plt.savefig(buf, format="png", dpi=120, facecolor=CARD); plt.close(); buf.seek(0)
264
+ log_entry("S1-D · R2a · Flow Corona", f"flow={flow_rate}", f"CSI={csi}")
265
+ return f"**Corona Shift Index: {csi}** — {stability}", Image.open(buf)
266
+
267
+ def predict_bbb(smiles, pka, zeta):
268
+ logp = smiles.count("C")*0.3 - smiles.count("O")*0.5 + 1.5
269
+ apoe_pct = max(0, min(40, (7.0-pka)*8 + abs(zeta)*0.5 + logp*0.8))
270
+ bbb_prob = min(0.95, apoe_pct/30)
271
+ tier = "HIGH (>20%)" if apoe_pct > 20 else "MEDIUM (10-20%)" if apoe_pct > 10 else "LOW (<10%)"
272
+ cats = ["ApoE%","BBB","logP","pKa fit","Zeta"]
273
+ vals = [apoe_pct/40, bbb_prob, min(logp/5,1), (7-abs(pka-6.5))/7, (10-abs(zeta))/10]
274
+ angles = np.linspace(0, 2*np.pi, len(cats), endpoint=False).tolist()
275
+ v2, a2 = vals+[vals[0]], angles+[angles[0]]
276
+ fig, ax = plt.subplots(figsize=(5, 4), subplot_kw={"polar":True}, facecolor=CARD)
277
+ ax.set_facecolor(CARD)
278
+ ax.plot(a2, v2, color=ACC, linewidth=2); ax.fill(a2, v2, color=ACC, alpha=0.2)
279
+ ax.set_xticks(angles); ax.set_xticklabels(cats, color=TXT, fontsize=8)
280
+ ax.tick_params(colors=TXT)
281
+ plt.tight_layout()
282
+ buf = BytesIO(); plt.savefig(buf, format="png", dpi=120, facecolor=CARD); plt.close(); buf.seek(0)
283
+ log_entry("S1-D · R3a · LNP Brain", f"pka={pka},zeta={zeta}", f"ApoE={apoe_pct:.1f}%")
284
+ return f"**Predicted ApoE:** {apoe_pct:.1f}% — {tier}\n\n**BBB Probability:** {bbb_prob:.2f}", Image.open(buf)
285
+
286
+ def extract_corona(text):
287
+ out = {"nanoparticle_composition":"","size_nm":None,"zeta_mv":None,"PDI":None,
288
+ "protein_source":"","corona_proteins":[],"confidence":{}}
289
+ for pat, key in [(r"(\d+\.?\d*)\s*(?:nm|nanometer)","size_nm"),
290
+ (r"([+-]?\d+\.?\d*)\s*mV","zeta_mv"),
291
+ (r"PDI\s*[=:of]*\s*(\d+\.?\d*)","PDI")]:
292
+ m = re.search(pat, text, re.I)
293
+ if m: out[key] = float(m.group(1)); out["confidence"][key] = "HIGH"
294
+ for src in ["human plasma","human serum","fetal bovine serum","FBS","PBS"]:
295
+ if src.lower() in text.lower():
296
+ out["protein_source"] = src; out["confidence"]["protein_source"] = "HIGH"; break
297
+ out["corona_proteins"] = [{"name":p,"confidence":"MEDIUM"} for p in PROTEINS if p in text.lower()]
298
+ for lip in ["DSPC","DOPE","MC3","DLin","cholesterol","PEG","DOTAP"]:
299
+ if lip in text: out["nanoparticle_composition"] += lip + " "
300
+ out["nanoparticle_composition"] = out["nanoparticle_composition"].strip()
301
+ flags = []
302
+ if not out["size_nm"]: flags.append("size_nm not found")
303
+ if not out["zeta_mv"]: flags.append("zeta_mv not found")
304
+ if not out["corona_proteins"]: flags.append("no proteins detected")
305
+ summary = "All key fields extracted" if not flags else " | ".join(flags)
306
+ log_entry("S1-D · R4a · AutoCorona NLP", text[:80], f"proteins={len(out['corona_proteins'])}")
307
+ return json.dumps(out, indent=2), summary
308
+
309
+ # ---------- S1-F RARE ----------
310
+ DIPG_VARIANTS = [
311
+ {"Variant":"H3K27M (H3F3A)","Freq_pct":78,"Pathway":"PRC2 inhibition → global H3K27me3 loss","Drug_status":"ONC201 (clinical)","Circadian_gene":"BMAL1 suppressed"},
312
+ {"Variant":"ACVR1 p.R206H","Freq_pct":21,"Pathway":"BMP/SMAD hyperactivation","Drug_status":"LDN-193189 (preclinical)","Circadian_gene":"PER1 disrupted"},
313
+ {"Variant":"PIK3CA p.H1047R","Freq_pct":15,"Pathway":"PI3K/AKT/mTOR","Drug_status":"Copanlisib (clinical)","Circadian_gene":"CRY1 altered"},
314
+ {"Variant":"TP53 p.R248W","Freq_pct":14,"Pathway":"DNA damage response loss","Drug_status":"APR-246 (clinical)","Circadian_gene":"p53-CLOCK axis"},
315
+ {"Variant":"PDGFRA amp","Freq_pct":13,"Pathway":"RTK/RAS signalling","Drug_status":"Avapritinib (clinical)","Circadian_gene":"REV-ERB altered"},
316
+ ]
317
+ DIPG_CSF_LNP = [
318
+ {"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"},
319
+ {"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"},
320
+ {"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"},
321
+ {"Formulation":"DODAP-DSPC-Chol","Size_nm":118,"Zeta_mV":-2.1,"CSF_protein":"Transferrin","ApoE_pct":7.2,"BBB_est":0.24,"Priority":"LOW"},
322
+ ]
323
+
324
+ UVM_VARIANTS = [
325
+ {"Variant":"GNAQ p.Q209L","Freq_pct":46,"Pathway":"PLCβ → PKC → MAPK","Drug_status":"Darovasertib (clinical)","m6A_writer":"METTL3 upregulated"},
326
+ {"Variant":"GNA11 p.Q209L","Freq_pct":32,"Pathway":"PLCβ → PKC → MAPK","Drug_status":"Darovasertib (clinical)","m6A_writer":"WTAP upregulated"},
327
+ {"Variant":"BAP1 loss","Freq_pct":47,"Pathway":"Chromatin remodeling → metastasis","Drug_status":"No approved (HDAC trials)","m6A_writer":"FTO overexpressed"},
328
+ {"Variant":"SF3B1 p.R625H","Freq_pct":19,"Pathway":"Splicing alteration → neoepitopes","Drug_status":"H3B-8800 (clinical)","m6A_writer":"METTL14 altered"},
329
+ {"Variant":"EIF1AX p.A113_splice","Freq_pct":14,"Pathway":"Translation initiation","Drug_status":"Novel — no drug","m6A_writer":"YTHDF2 suppressed"},
330
+ ]
331
+ UVM_VITREOUS_LNP = [
332
+ {"Formulation":"SM-102-DSPC-Chol-PEG","Vitreal_protein":"Hyaluronan-binding","Size_nm":95,"Zeta_mV":-3.2,"Retention_h":18,"Priority":"HIGH"},
333
+ {"Formulation":"Lipid-H-DOPE-PEG","Vitreal_protein":"Vitronectin dominant","Size_nm":88,"Zeta_mV":-4.0,"Retention_h":22,"Priority":"HIGH"},
334
+ {"Formulation":"DOTAP-DSPC-PEG","Vitreal_protein":"Albumin wash-out","Size_nm":112,"Zeta_mV":+2.1,"Retention_h":6,"Priority":"LOW"},
335
+ {"Formulation":"MC3-DPPC-Chol","Vitreal_protein":"Clusterin-rich","Size_nm":101,"Zeta_mV":-2.8,"Retention_h":14,"Priority":"MEDIUM"},
336
+ ]
337
+
338
+ PAML_VARIANTS = [
339
+ {"Variant":"FLT3-ITD","Freq_pct":25,"Pathway":"RTK constitutive activation → JAK/STAT","Drug_status":"Midostaurin (approved)","Ferroptosis":"GPX4 suppressed"},
340
+ {"Variant":"NPM1 c.860_863dupTCAG","Freq_pct":30,"Pathway":"Nuclear export deregulation","Drug_status":"APR-548 combo (clinical)","Ferroptosis":"SLC7A11 upregulated"},
341
+ {"Variant":"DNMT3A p.R882H","Freq_pct":18,"Pathway":"Epigenetic dysregulation","Drug_status":"Azacitidine (approved)","Ferroptosis":"ACSL4 altered"},
342
+ {"Variant":"CEBPA biallelic","Freq_pct":8,"Pathway":"Myeloid differentiation block","Drug_status":"Novel target","Ferroptosis":"NRF2 pathway"},
343
+ {"Variant":"IDH1/2 mutation","Freq_pct":15,"Pathway":"2-HG oncometabolite → TET2 inhibition","Drug_status":"Enasidenib (approved)","Ferroptosis":"Iron metabolism disrupted"},
344
+ ]
345
+ PAML_BM_LNP = [
346
+ {"Formulation":"ALC-0315-DSPC-Chol-PEG","BM_protein":"ApoE + Clusterin","Size_nm":98,"Zeta_mV":-3.5,"Marrow_uptake_pct":34,"Priority":"HIGH"},
347
+ {"Formulation":"MC3-DOPE-Chol-PEG","BM_protein":"Fibronectin dominant","Size_nm":105,"Zeta_mV":-4.2,"Marrow_uptake_pct":28,"Priority":"HIGH"},
348
+ {"Formulation":"DLin-MC3-DPPC","BM_protein":"Vitronectin-rich","Size_nm":91,"Zeta_mV":-2.9,"Marrow_uptake_pct":19,"Priority":"MEDIUM"},
349
+ {"Formulation":"Cationic-DOTAP-Chol","BM_protein":"Opsonin-heavy","Size_nm":132,"Zeta_mV":+8.1,"Marrow_uptake_pct":8,"Priority":"LOW"},
350
+ ]
351
+
352
+ def dipg_variants(sort_by):
353
+ df = pd.DataFrame(DIPG_VARIANTS).sort_values(
354
+ "Freq_pct" if sort_by == "Frequency" else "Drug_status", ascending=False)
355
+ log_entry("S1-F · R1a · DIPG", sort_by, f"{len(df)} variants")
356
+ return df
357
+
358
+ def dipg_csf(peg, size):
359
+ df = pd.DataFrame(DIPG_CSF_LNP)
360
+ df["Score"] = df["ApoE_pct"]/40 + df["BBB_est"] - abs(df["Size_nm"]-size)/200
361
+ df = df.sort_values("Score", ascending=False)
362
+ fig, ax = plt.subplots(figsize=(6, 3), facecolor=CARD)
363
+ ax.set_facecolor(CARD)
364
+ colors = [GRN if p=="HIGH" else ACC if p=="MEDIUM" else RED for p in df["Priority"]]
365
+ ax.barh(df["Formulation"], df["ApoE_pct"], color=colors)
366
+ ax.set_xlabel("ApoE% in CSF corona", color=TXT)
367
+ ax.tick_params(colors=TXT, labelsize=8)
368
+ for sp in ax.spines.values(): sp.set_edgecolor(BORDER)
369
+ ax.set_title("DIPG — CSF LNP formulations (ApoE%)", color=TXT, fontsize=9)
370
+ plt.tight_layout()
371
+ buf = BytesIO(); plt.savefig(buf, format="png", dpi=120, facecolor=CARD); plt.close(); buf.seek(0)
372
+ log_entry("S1-F · R1a · DIPG CSF", f"peg={peg},size={size}", "formulation ranking")
373
+ return df[["Formulation","Size_nm","Zeta_mV","ApoE_pct","BBB_est","Priority"]], Image.open(buf)
374
+
375
+ def uvm_variants():
376
+ df = pd.DataFrame(UVM_VARIANTS)
377
+ log_entry("S1-F · R2a · UVM", "load", f"{len(df)} variants")
378
+ return df
379
+
380
+ def uvm_vitreous():
381
+ df = pd.DataFrame(UVM_VITREOUS_LNP)
382
+ fig, ax = plt.subplots(figsize=(6, 3), facecolor=CARD)
383
+ ax.set_facecolor(CARD)
384
+ colors = [GRN if p=="HIGH" else ACC if p=="MEDIUM" else RED for p in df["Priority"]]
385
+ ax.barh(df["Formulation"], df["Retention_h"], color=colors)
386
+ ax.set_xlabel("Vitreous retention (hours)", color=TXT)
387
+ ax.tick_params(colors=TXT, labelsize=8)
388
+ for sp in ax.spines.values(): sp.set_edgecolor(BORDER)
389
+ ax.set_title("UVM — LNP retention in vitreous humor", color=TXT, fontsize=9)
390
+ plt.tight_layout()
391
+ buf = BytesIO(); plt.savefig(buf, format="png", dpi=120, facecolor=CARD); plt.close(); buf.seek(0)
392
+ log_entry("S1-F · R2a · UVM Vitreous", "load", "vitreous LNP ranking")
393
+ return df, Image.open(buf)
394
+
395
+ def paml_ferroptosis(variant):
396
+ row = next((r for r in PAML_VARIANTS if variant in r["Variant"]), PAML_VARIANTS[0])
397
+ ferr_map = {"GPX4 suppressed": 0.85, "SLC7A11 upregulated": 0.72,
398
+ "ACSL4 altered": 0.61, "NRF2 pathway": 0.55, "Iron metabolism disrupted": 0.78}
399
+ ferr_score = ferr_map.get(row["Ferroptosis"], 0.5)
400
+ cats = ["Ferroptosis\nsensitivity", "Drug\navailable", "BM niche\ncoverage", "Data\nmaturity", "Target\nnovelty"]
401
+ has_drug = 0.9 if row["Drug_status"] not in ["Novel target"] else 0.3
402
+ vals = [ferr_score, has_drug, 0.6, 0.55, 1-has_drug+0.2]
403
+ angles = np.linspace(0, 2*np.pi, len(cats), endpoint=False).tolist()
404
+ v2, a2 = vals+[vals[0]], angles+[angles[0]]
405
+ fig, ax = plt.subplots(figsize=(5, 4), subplot_kw={"polar":True}, facecolor=CARD)
406
+ ax.set_facecolor(CARD)
407
+ ax.plot(a2, v2, color=ACC2, linewidth=2); ax.fill(a2, v2, color=ACC2, alpha=0.2)
408
+ ax.set_xticks(angles); ax.set_xticklabels(cats, color=TXT, fontsize=8)
409
+ ax.tick_params(colors=TXT)
410
+ ax.set_title(f"pAML · {row['Variant'][:20]}", color=TXT, fontsize=9)
411
+ plt.tight_layout()
412
+ buf = BytesIO(); plt.savefig(buf, format="png", dpi=120, facecolor=CARD); plt.close(); buf.seek(0)
413
+ log_entry("S1-F · R3a · pAML", variant, f"ferr={ferr_score:.2f}")
414
+ _v = row["Variant"]
415
+ _p = row["Pathway"]
416
+ _d = row["Drug_status"]
417
+ _f = row["Ferroptosis"]
418
+ _fs = f"{ferr_score:.2f}"
419
+ summary = (
420
+ f"<div style='background:{CARD};padding:14px;border-radius:8px;font-family:sans-serif;'>"
421
+ f"<p style='color:{DIM};font-size:11px;margin:0 0 6px'>S1-F · R3a · pAML</p>"
422
+ f"<b style='color:{ACC2};font-size:15px'>{_v}</b><br>"
423
+ f"<p style='color:{TXT};margin:6px 0'><b>Pathway:</b> {_p}</p>"
424
+ f"<p style='color:{TXT};margin:0'><b>Drug:</b> {_d}</p>"
425
+ f"<p style='color:{TXT};margin:6px 0'><b>Ferroptosis link:</b> {_f}</p>"
426
+ f"<p style='color:{TXT}'><b>Ferroptosis sensitivity score:</b> "
427
+ f"<span style='color:{ACC};font-size:18px'>{_fs}</span></p>"
428
+ f"<p style='font-size:11px;color:{DIM}'>Research only. Not clinical advice.</p></div>"
429
+ )
430
+ return summary, Image.open(buf)
431
+
432
+ # ========== ДОПОМІЖНІ ФУНКЦІЇ ==========
433
+ def section_header(code, name, tagline, projects_html):
434
+ return (
435
+ f"<div style=\'background:{BG};border:1px solid {BORDER};padding:14px 18px;"
436
+ f"border-radius:8px;margin-bottom:12px;\'>"
437
+ f"<div style=\'display:flex;align-items:baseline;gap:10px;\'>"
438
+ f"<span style=\'color:{ACC2};font-size:16px;font-weight:700\'>{code}</span>"
439
+ f"<span style=\'color:{TXT};font-size:14px;font-weight:600\'>{name}</span>"
440
+ f"<span style=\'color:{DIM};font-size:12px\'>{tagline}</span></div>"
441
+ f"<div style=\'margin-top:8px;font-size:12px;color:{DIM}\'>{projects_html}</div>"
442
+ f"</div>"
443
+ )
444
+
445
+ def proj_badge(code, title, metric=""):
446
+ m = (f"<span style=\'background:#0f2a3f;color:{ACC2};padding:1px 7px;border-radius:3px;"
447
+ f"font-size:10px;margin-left:6px\'>{metric}</span>") if metric else ""
448
+ return (
449
+ f"<div style=\'background:{CARD};border-left:3px solid {ACC};"
450
+ f"padding:8px 12px;border-radius:0 6px 6px 0;margin-bottom:8px;\'>"
451
+ f"<span style=\'color:{DIM};font-size:11px\'>{code}</span>{m}<br>"
452
+ f"<span style=\'color:{TXT};font-size:14px;font-weight:600\'>{title}</span>"
453
+ f"</div>"
454
+ )
455
+
456
+ # ========== CSS ==========
457
+ css = f"""
458
+ body, .gradio-container {{ background: {BG} !important; color: {TXT} !important; }}
459
+ .outer-tabs .tab-nav button {{
460
+ color: {TXT} !important;
461
+ background: {CARD} !important;
462
+ font-size: 13px !important;
463
+ font-weight: 600 !important;
464
+ padding: 8px 16px !important;
465
+ border-radius: 6px 6px 0 0 !important;
466
+ }}
467
+ .outer-tabs .tab-nav button.selected {{
468
+ border-bottom: 3px solid {ACC} !important;
469
+ color: {ACC} !important;
470
+ background: {BG} !important;
471
+ }}
472
+ .inner-tabs .tab-nav button {{
473
+ color: {DIM} !important;
474
+ background: {BG} !important;
475
+ font-size: 12px !important;
476
+ font-weight: 500 !important;
477
+ padding: 5px 12px !important;
478
+ border-radius: 4px 4px 0 0 !important;
479
+ border: 1px solid {BORDER} !important;
480
+ border-bottom: none !important;
481
+ margin-right: 3px !important;
482
+ }}
483
+ .inner-tabs .tab-nav button.selected {{
484
+ color: {ACC2} !important;
485
+ background: {CARD} !important;
486
+ border-color: {ACC2} !important;
487
+ border-bottom: none !important;
488
+ }}
489
+ .inner-tabs > .tabitem {{
490
+ background: {CARD} !important;
491
+ border: 1px solid {BORDER} !important;
492
+ border-radius: 0 6px 6px 6px !important;
493
+ padding: 14px !important;
494
+ }}
495
+ h1, h2, h3 {{ color: {ACC} !important; }}
496
+ .gr-button-primary {{ background: {ACC} !important; border: none !important; }}
497
+ footer {{ display: none !important; }}
498
+ """
499
+
500
+ # ========== MAP HTML ==========
501
+ MAP_HTML = f"""
502
+ <div style="background:{CARD};padding:22px;border-radius:8px;font-family:monospace;font-size:13px;line-height:2.0;color:{TXT}">
503
+ <span style="color:{ACC};font-size:16px;font-weight:bold">K R&D Lab · S1 Biomedical</span>
504
+ <span style="color:{DIM};font-size:11px;margin-left:12px">Science Sphere — sub-direction 1</span>
505
+ <br><br>
506
+ <span style="color:{ACC2};font-weight:600">S1-A · PHYLO-GENOMICS</span> — What breaks in DNA<br>
507
+ &nbsp;&nbsp;&nbsp;├─ <b>S1-A · R1a</b> OpenVariant <span style="color:{GRN}"> AUC=0.939 ✅</span><br>
508
+ &nbsp;&nbsp;&nbsp;└─ <b>S1-A · R1b</b> Somatic classifier <span style="color:#f59e0b"> 🔶 In progress</span><br><br>
509
+ <span style="color:{ACC2};font-weight:600">S1-B · PHYLO-RNA</span> — How to silence it via RNA<br>
510
+ &nbsp;&nbsp;&nbsp;├─ <b>S1-B · R1a</b> miRNA silencing <span style="color:{GRN}"> ✅</span><br>
511
+ &nbsp;&nbsp;&nbsp;├─ <b>S1-B · R2a</b> siRNA synthetic lethal <span style="color:{GRN}"> ✅</span><br>
512
+ &nbsp;&nbsp;&nbsp;├─ <b>S1-B · R3a</b> lncRNA-TREM2 ceRNA <span style="color:{GRN}"> ✅</span><br>
513
+ &nbsp;&nbsp;&nbsp;└─ <b>S1-B · R3b</b> ASO designer <span style="color:{GRN}"> ✅</span><br><br>
514
+ <span style="color:{ACC2};font-weight:600">S1-C · PHYLO-DRUG</span> — Which molecule treats it<br>
515
+ &nbsp;&nbsp;&nbsp;├─ <b>S1-C · R1a</b> FGFR3 RNA-directed compounds <span style="color:{GRN}"> ✅</span><br>
516
+ &nbsp;&nbsp;&nbsp;├─ <b>S1-C · R1b</b> Synthetic lethal drug mapping <span style="color:#f59e0b"> 🔶</span><br>
517
+ &nbsp;&nbsp;&nbsp;└─ <b>S1-C · R2a</b> m6A × Ferroptosis × Circadian <span style="color:{DIM}"> 🔴 Frontier</span><br><br>
518
+ <span style="color:{ACC2};font-weight:600">S1-D · PHYLO-LNP</span> — How to deliver the drug<br>
519
+ &nbsp;&nbsp;&nbsp;├─ <b>S1-D · R1a</b> LNP corona (serum) <span style="color:{GRN}"> AUC=0.791 ✅</span><br>
520
+ &nbsp;&nbsp;&nbsp;├─ <b>S1-D · R2a</b> Flow corona — Vroman effect <span style="color:{GRN}"> ✅</span><br>
521
+ &nbsp;&nbsp;&nbsp;├─ <b>S1-D · R3a</b> LNP brain / BBB / ApoE <span style="color:{GRN}"> ✅</span><br>
522
+ &nbsp;&nbsp;&nbsp;├─ <b>S1-D · R4a</b> AutoCorona NLP <span style="color:{GRN}"> F1=0.71 ✅</span><br>
523
+ &nbsp;&nbsp;&nbsp;└─ <b>S1-D · R5a</b> CSF · Vitreous · Bone Marrow <span style="color:{DIM}"> 🔴 0 prior studies</span><br><br>
524
+ <span style="color:{ACC2};font-weight:600">S1-E · PHYLO-BIOMARKERS</span> — Detect without biopsy<br>
525
+ &nbsp;&nbsp;&nbsp;├─ <b>S1-E · R1a</b> Liquid Biopsy classifier <span style="color:{GRN}"> AUC=0.992* ✅</span><br>
526
+ &nbsp;&nbsp;&nbsp;└─ <b>S1-E · R1b</b> Protein panel validator <span style="color:#f59e0b"> 🔶</span><br><br>
527
+ <span style="color:{ACC2};font-weight:600">S1-F · PHYLO-RARE</span> — Where almost nobody has looked yet<br>
528
+ &nbsp;&nbsp;&nbsp;├─ <b>S1-F · R1a</b> DIPG toolkit (H3K27M + CSF LNP + Circadian) <span style="color:#f59e0b"> 🔶</span><br>
529
+ &nbsp;&nbsp;&nbsp;├─ <b>S1-F · R2a</b> UVM toolkit (GNAQ/GNA11 + vitreous + m6A) <span style="color:#f59e0b"> 🔶</span><br>
530
+ &nbsp;&nbsp;&nbsp;└─ <b>S1-F · R3a</b> pAML toolkit (FLT3-ITD + BM niche + ferroptosis) <span style="color:#f59e0b"> 🔶</span><br><br>
531
+ <span style="color:{DIM};font-size:11px">✅ Active · 🔶 In progress · 🔴 Planned</span>
532
+ </div>
533
+ """
534
+
535
+ # ========== ІНТЕРФЕЙС ==========
536
+ with gr.Blocks(css=css, title="K R&D Lab · S1 Biomedical") as demo:
537
+ gr.Markdown(
538
+ "# 🔬 K R&D Lab · Science Sphere — S1 Biomedical\n"
539
+ "**Oksana Kolisnyk** · [KOSATIKS GROUP](https://kosatiks-group.pp.ua) · "
540
+ "*Research only. Not clinical advice.*"
541
+ )
542
+
543
+ with gr.Tabs(elem_classes="outer-tabs"):
544
+ # 🗺️ Lab Map
545
+ with gr.TabItem("🗺️ Lab Map"):
546
+ gr.HTML(MAP_HTML)
547
+
548
+ # 🧬 S1-A · PHYLO-GENOMICS
549
  with gr.TabItem("S1-A · R1a · OpenVariant"):
550
+ gr.HTML(proj_badge("S1-A · R1a", "OpenVariant — SNV Pathogenicity Classifier", "AUC = 0.939"))
551
+ hgvs = gr.Textbox(label="HGVS notation", placeholder="BRCA1:p.R1699Q")
552
+ gr.Markdown("**Or enter functional scores manually:**")
553
+ with gr.Row():
554
+ sift = gr.Slider(0,1,value=0.5,step=0.01,label="SIFT (0=damaging)")
555
+ pp = gr.Slider(0,1,value=0.5,step=0.01,label="PolyPhen-2")
556
+ gn = gr.Slider(0,0.01,value=0.001,step=0.0001,label="gnomAD AF")
557
+ b_v = gr.Button("Predict Pathogenicity", variant="primary")
558
+ o_v = gr.HTML()
559
+ gr.Examples([["BRCA1:p.R1699Q",0.82,0.05,0.0012],
560
+ ["TP53:p.R248W",0.00,1.00,0.0],
561
+ ["BRCA2:p.D2723A",0.01,0.98,0.0]], inputs=[hgvs,sift,pp,gn], cache_examples=False)
562
+ b_v.click(predict_variant, [hgvs,sift,pp,gn], o_v)
563
+
564
+ with gr.TabItem("S1-A · R1b · Somatic Classifier 🔶"):
565
+ gr.HTML(proj_badge("S1-A · R1b", "Somatic Mutation Classifier", "🔶 In progress"))
566
+ gr.Markdown("> This module is in active development. Coming in the next release.")
567
+
568
+ # 🔬 S1-B · PHYLO-RNA
569
+ with gr.TabItem("S1-B · R1a · BRCA2 miRNA"):
570
+ gr.HTML(proj_badge("S1-B · R1a", "miRNA Silencing — BRCA1/2 · TP53"))
571
+ g1 = gr.Dropdown(["BRCA2","BRCA1","TP53"], value="BRCA2", label="Gene")
572
+ b1 = gr.Button("Find miRNAs", variant="primary")
573
+ o1 = gr.Dataframe(label="Top 5 downregulated miRNAs")
574
+ gr.Examples([["BRCA2"],["BRCA1"],["TP53"]], inputs=[g1])
575
+ b1.click(predict_mirna, [g1], o1)
576
+
577
+ with gr.TabItem("S1-B · R2a · TP53 siRNA"):
578
+ gr.HTML(proj_badge("S1-B · R2a", "siRNA Synthetic Lethal — TP53-null"))
579
+ g2 = gr.Dropdown(["LUAD","BRCA","COAD"], value="LUAD", label="Cancer type")
580
+ b2 = gr.Button("Find Targets", variant="primary")
581
+ o2 = gr.Dataframe(label="Top 5 synthetic lethal targets")
582
+ gr.Examples([["LUAD"],["BRCA"],["COAD"]], inputs=[g2], cache_examples=False)
583
+ b2.click(predict_sirna, [g2], o2)
584
+
585
+ with gr.TabItem("S1-B · R3a · lncRNA-TREM2"):
586
+ gr.HTML(proj_badge("S1-B · R3a", "lncRNA-TREM2 ceRNA Network"))
587
+ b3 = gr.Button("Load Network", variant="primary")
588
+ o3 = gr.Dataframe(label="ceRNA Network")
589
+ b3.click(get_lncrna, [], o3)
590
+
591
+ with gr.TabItem("S1-B · R3b · ASO Designer"):
592
+ gr.HTML(proj_badge("S1-B · R3b", "ASO Candidates"))
593
+ b4 = gr.Button("Show ASOs", variant="primary")
594
+ o4 = gr.Dataframe(label="ASO Candidates")
595
+ b4.click(get_aso, [], o4)
596
+
597
+ # 💊 S1-C · PHYLO-DRUG
598
+ with gr.TabItem("S1-C · R1a · FGFR3 RNA Drug"):
599
+ gr.HTML(proj_badge("S1-C · R1a", "FGFR3 RNA-Directed Drug Discovery", "top score 0.793"))
600
+ g4 = gr.Radio(["P1 (hairpin loop)","P10 (G-quadruplex)"],
601
+ value="P1 (hairpin loop)", label="Target pocket")
602
+ b4 = gr.Button("Screen Compounds", variant="primary")
603
+ o4t = gr.Dataframe(label="Top 5 candidates")
604
+ o4p = gr.Image(label="Binding scores")
605
+ gr.Examples([["P1 (hairpin loop)"],["P10 (G-quadruplex)"]], inputs=[g4])
606
+ b4.click(predict_drug, [g4], [o4t, o4p])
607
+
608
+ with gr.TabItem("S1-C · R1b · SL Drug Mapping 🔶"):
609
+ gr.HTML(proj_badge("S1-C · R1b", "Synthetic Lethal Drug Mapping", "🔶 In progress"))
610
+ gr.Markdown("> Coming soon.")
611
+
612
+ with gr.TabItem("S1-C · R2a · Frontier 🔴"):
613
+ gr.HTML(proj_badge("S1-C · R2a", "m6A × Ferroptosis × Circadian", "🔴 Frontier"))
614
+ gr.Markdown("> Planned for Q3 2026")
615
+
616
+ # 🧪 S1-D · PHYLO-LNP
617
+ with gr.TabItem("S1-D · R1a · LNP Corona"):
618
+ gr.HTML(proj_badge("S1-D · R1a", "LNP Protein Corona (Serum)", "AUC = 0.791"))
619
+ with gr.Row():
620
+ sz = gr.Slider(50,300,value=100,step=1,label="Size (nm)")
621
+ zt = gr.Slider(-40,10,value=-5,step=1,label="Zeta (mV)")
622
+ with gr.Row():
623
+ pg = gr.Slider(0,5,value=1.5,step=0.1,label="PEG mol%")
624
+ lp = gr.Dropdown(["Ionizable","Cationic","Anionic","Neutral"],value="Ionizable",label="Lipid type")
625
+ b6 = gr.Button("Predict", variant="primary")
626
+ o6 = gr.Markdown()
627
+ gr.Examples([[100,-5,1.5,"Ionizable"],[80,5,0.5,"Cationic"]], inputs=[sz,zt,pg,lp])
628
+ b6.click(predict_corona, [sz,zt,pg,lp], o6)
629
+
630
+ with gr.TabItem("S1-D · R2a · Flow Corona"):
631
+ gr.HTML(proj_badge("S1-D · R2a", "Flow Corona — Vroman Effect"))
632
+ with gr.Row():
633
+ s8 = gr.Slider(50,300,value=100,step=1,label="Size (nm)")
634
+ z8 = gr.Slider(-40,10,value=-5,step=1,label="Zeta (mV)")
635
+ pg8 = gr.Slider(0,5,value=1.5,step=0.1,label="PEG mol%")
636
+ with gr.Row():
637
+ ch8 = gr.Dropdown(["Ionizable","Cationic","Anionic","Neutral"],value="Ionizable",label="Charge")
638
+ fl8 = gr.Slider(0,40,value=20,step=1,label="Flow cm/s")
639
+ b8 = gr.Button("Model Vroman Effect", variant="primary")
640
+ o8t = gr.Markdown(); o8p = gr.Image(label="Kinetics")
641
+ gr.Examples([[100,-5,1.5,"Ionizable",40],[150,5,0.5,"Cationic",10]], inputs=[s8,z8,pg8,ch8,fl8])
642
+ b8.click(predict_flow, [s8,z8,pg8,ch8,fl8], [o8t,o8p])
643
+
644
+ with gr.TabItem("S1-D · R3a · LNP Brain"):
645
+ gr.HTML(proj_badge("S1-D · R3a", "LNP Brain Delivery — ApoE% + BBB probability"))
646
+ smi = gr.Textbox(label="Ionizable lipid SMILES",
647
+ value="CC(C)CC(=O)OCC(COC(=O)CC(C)C)OC(=O)CC(C)C")
648
+ with gr.Row():
649
+ pk = gr.Slider(4,8,value=6.5,step=0.1,label="pKa")
650
+ zt9 = gr.Slider(-20,10,value=-3,step=1,label="Zeta (mV)")
651
+ b9 = gr.Button("Predict BBB Crossing", variant="primary")
652
+ o9t = gr.Markdown(); o9p = gr.Image(label="Radar profile")
653
+ gr.Examples([["CC(C)CC(=O)OCC(COC(=O)CC(C)C)OC(=O)CC(C)C",6.5,-3]], inputs=[smi,pk,zt9])
654
+ b9.click(predict_bbb, [smi,pk,zt9], [o9t,o9p])
655
+
656
+ with gr.TabItem("S1-D · R4a · AutoCorona NLP"):
657
+ gr.HTML(proj_badge("S1-D · R4a", "AutoCorona NLP — from abstracts", "F1 = 0.71"))
658
+ txt = gr.Textbox(lines=5,label="Paper abstract",placeholder="Paste abstract here...")
659
+ b10 = gr.Button("Extract Data", variant="primary")
660
+ o10j = gr.Code(label="Extracted JSON", language="json")
661
+ o10f = gr.Textbox(label="Validation flags")
662
+ gr.Examples([["LNPs composed of MC3, DSPC, Cholesterol (50:10:40 mol%) with 1.5% PEG-DMG. Hydrodynamic diameter was 98 nm, zeta potential -3.2 mV, PDI 0.12. Incubated in human plasma. Corona: albumin, apolipoprotein E, fibrinogen."]], inputs=[txt])
663
+ b10.click(extract_corona, txt, [o10j, o10f])
664
+
665
+ with gr.TabItem("S1-D · R5a · CSF/BM 🔴"):
666
+ gr.HTML(proj_badge("S1-D · R5a", "LNP Corona in CSF · Vitreous · Bone Marrow", "🔴 0 prior studies"))
667
+ gr.Markdown("> Planned for Q2–Q3 2026")
668
+
669
+ # 🩸 S1-E · PHYLO-BIOMARKERS
670
+ with gr.TabItem("S1-E · R1a · Liquid Biopsy"):
671
+ gr.HTML(proj_badge("S1-E · R1a", "Liquid Biopsy Classifier", "AUC = 0.992*"))
672
+ with gr.Row():
673
+ p1=gr.Slider(-3,3,value=0,step=0.1,label="CTHRC1")
674
+ p2=gr.Slider(-3,3,value=0,step=0.1,label="FHL2")
675
+ p3=gr.Slider(-3,3,value=0,step=0.1,label="LDHA")
676
+ p4=gr.Slider(-3,3,value=0,step=0.1,label="P4HA1")
677
+ p5=gr.Slider(-3,3,value=0,step=0.1,label="SERPINH1")
678
+ with gr.Row():
679
+ p6=gr.Slider(-3,3,value=0,step=0.1,label="ABCA8")
680
+ p7=gr.Slider(-3,3,value=0,step=0.1,label="CA4")
681
+ p8=gr.Slider(-3,3,value=0,step=0.1,label="CKB")
682
+ p9=gr.Slider(-3,3,value=0,step=0.1,label="NNMT")
683
+ p10=gr.Slider(-3,3,value=0,step=0.1,label="CACNA2D2")
684
+ b7=gr.Button("Classify", variant="primary")
685
+ o7t=gr.HTML(); o7p=gr.Image(label="Feature contributions")
686
+ gr.Examples([[2,2,1.5,1.8,1.6,-1,-1.2,-0.8,1.4,-1.1],[0]*10],
687
+ inputs=[p1,p2,p3,p4,p5,p6,p7,p8,p9,p10])
688
+ b7.click(predict_cancer, [p1,p2,p3,p4,p5,p6,p7,p8,p9,p10], [o7t,o7p])
689
+
690
+ with gr.TabItem("S1-E · R1b · Validator 🔶"):
691
+ gr.HTML(proj_badge("S1-E · R1b", "Protein Panel Validator", "🔶 In progress"))
692
+ gr.Markdown("> Coming next.")
693
+
694
+ # 🧠 S1-F · PHYLO-RARE
695
+ with gr.TabItem("S1-F · R1a · DIPG Toolkit"):
696
+ gr.HTML(proj_badge("S1-F · R1a", "DIPG: H3K27M + CSF LNP + Circadian", "PBTA"))
697
+ sort_d = gr.Radio(["Frequency", "Drug status"], value="Frequency", label="Sort by")
698
+ b_dv = gr.Button("Load DIPG Variants", variant="primary")
699
+ o_dv = gr.Dataframe(label="H3K27M co-mutations")
700
+ b_dv.click(dipg_variants, [sort_d], o_dv)
701
+ gr.Markdown("---")
702
+ with gr.Row():
703
+ d_peg = gr.Slider(0.5, 3.0, value=1.5, step=0.1, label="PEG mol%")
704
+ d_size = gr.Slider(60, 150, value=90, step=5, label="Target size (nm)")
705
+ b_dc = gr.Button("Rank CSF Formulations", variant="primary")
706
+ o_dct = gr.Dataframe(label="CSF LNP ranking")
707
+ o_dcp = gr.Image(label="ApoE% in CSF corona")
708
+ b_dc.click(dipg_csf, [d_peg, d_size], [o_dct, o_dcp])
709
+
710
+ with gr.TabItem("S1-F · R2a · UVM Toolkit"):
711
+ gr.HTML(proj_badge("S1-F · R2a", "UVM: GNAQ/GNA11 + vitreous + m6A", "TCGA-UVM"))
712
+ b_uv = gr.Button("Load UVM Variants", variant="primary")
713
+ o_uv = gr.Dataframe(label="GNAQ/GNA11 map")
714
+ b_uv.click(uvm_variants, [], o_uv)
715
+ b_uw = gr.Button("Rank Vitreous Formulations", variant="primary")
716
+ o_uwt = gr.Dataframe(label="Vitreous LNP retention ranking")
717
+ o_uwp = gr.Image(label="Retention (hours)")
718
+ b_uw.click(uvm_vitreous, [], [o_uwt, o_uwp])
719
+
720
+ with gr.TabItem("S1-F · R3a · pAML Toolkit"):
721
+ gr.HTML(proj_badge("S1-F · R3a", "pAML: FLT3-ITD + BM niche + ferroptosis", "TARGET-AML"))
722
+ var_sel = gr.Dropdown(
723
+ ["FLT3-ITD", "NPM1 c.860_863dupTCAG", "DNMT3A p.R882H",
724
+ "CEBPA biallelic", "IDH1/2 mutation"],
725
+ value="FLT3-ITD", label="Select variant"
726
+ )
727
+ b_pf = gr.Button("Analyze Ferroptosis Profile", variant="primary")
728
+ o_pft = gr.HTML()
729
+ o_pfp = gr.Image(label="Target radar")
730
+ b_pf.click(paml_ferroptosis, var_sel, [o_pft, o_pfp])
731
+
732
+ # 📓 Journal
733
+ with gr.TabItem("📓 Journal"):
734
+ gr.Markdown("### Lab Journal")
735
+ with gr.Row():
736
+ note_text = gr.Textbox(label="📝 Observation", placeholder="What did you discover?", lines=3)
737
+ note_tab = gr.Textbox(label="Project code (e.g. S1-A·R1a)", value="General")
738
+ note_last = gr.Textbox(visible=False)
739
+ save_btn = gr.Button("💾 Save", variant="primary")
740
+ save_msg = gr.Markdown()
741
+ journal_df = gr.Dataframe(label="📋 Full History", value=load_journal(), interactive=False)
742
+ refresh_btn = gr.Button("🔄 Refresh")
743
+ refresh_btn.click(load_journal, [], journal_df)
744
+ save_btn.click(save_note, [note_text,note_tab,note_last], [save_msg,journal_df])
745
+
746
+ # 📚 Learning
747
+ with gr.TabItem("📚 Learning"):
748
+ gr.Markdown("""
749
+ ## 🧪 Guided Investigations
750
+ > 🟢 Beginner → 🟡 Intermediate → 🔴 Advanced
751
+
752
+ **S1-A · R1a** OpenVariant – try BRCA1:p.R1699Q vs R1699W
753
+ **S1-D · R1a** Corona – vary PEG% and observe dominant protein
754
+ **S1-D · R2a** Flow Corona – compare flow 0 vs 40 cm/s
755
+ **S1-B · R2a** siRNA – count "Novel" targets across cancer types
756
+ **S1-E · R1a** Liquid Biopsy – find minimal signal for CANCER
757
+ ... (more cases)
758
+ """)
759
+
760
+ gr.Markdown(
761
+ "---\n**K R&D Lab** · MIT License · "
762
+ "[GitHub](https://github.com/K-RnD-Lab) · "
763
+ "[HuggingFace](https://huggingface.co/K-RnD-Lab) · "
764
+ "[KOSATIKS GROUP](https://kosatiks-group.pp.ua)"
765
+ )
766
 
767
+ demo.queue()
768
  demo.launch()