Update app.py
Browse files
app.py
CHANGED
|
@@ -96,6 +96,7 @@ JOURNAL_CATEGORIES = [
|
|
| 96 |
# S1-A Genomics
|
| 97 |
"S1-A·R1a", # OpenVariant
|
| 98 |
"S1-A·R1b", # Somatic Classifier (future)
|
|
|
|
| 99 |
# S1-B RNA
|
| 100 |
"S1-B·R1a", # BRCA2 miRNA
|
| 101 |
"S1-B·R2a", # TP53 siRNA
|
|
@@ -111,11 +112,11 @@ JOURNAL_CATEGORIES = [
|
|
| 111 |
"S1-D·R3a", # LNP Brain
|
| 112 |
"S1-D·R4a", # AutoCorona NLP
|
| 113 |
"S1-D·R5a", # CSF/Vitreous/BM (future)
|
| 114 |
-
"S1-D·R6a", # Corona Database
|
| 115 |
# S1-E Biomarkers
|
| 116 |
"S1-E·R1a", # Liquid Biopsy
|
| 117 |
"S1-E·R1b", # Protein Validator (future)
|
| 118 |
-
"S1-E·R2a", # Multi-protein Biomarkers
|
| 119 |
# S1-F Rare
|
| 120 |
"S1-F·R1a", # DIPG Toolkit
|
| 121 |
"S1-F·R2a", # UVM Toolkit
|
|
@@ -268,7 +269,7 @@ PROTEINS = ["albumin","apolipoprotein","fibrinogen","vitronectin",
|
|
| 268 |
|
| 269 |
# ========== Список кодів проєктів для випадаючих списків ==========
|
| 270 |
PROJECT_CODES = [
|
| 271 |
-
"S1-A·R1a", "S1-A·R1b",
|
| 272 |
"S1-B·R1a", "S1-B·R2a", "S1-B·R3a", "S1-B·R3b",
|
| 273 |
"S1-C·R1a", "S1-C·R1b", "S1-C·R2a",
|
| 274 |
"S1-D·R1a", "S1-D·R2a", "S1-D·R3a", "S1-D·R4a", "S1-D·R5a",
|
|
@@ -319,6 +320,7 @@ PAML_BM_LNP = [
|
|
| 319 |
{"Formulation":"DLin-MC3-DPPC","BM_protein":"Vitronectin-rich","Size_nm":91,"Zeta_mV":-2.9,"Marrow_uptake_pct":19,"Priority":"MEDIUM"},
|
| 320 |
{"Formulation":"Cationic-DOTAP-Chol","BM_protein":"Opsonin-heavy","Size_nm":132,"Zeta_mV":+8.1,"Marrow_uptake_pct":8,"Priority":"LOW"},
|
| 321 |
]
|
|
|
|
| 322 |
# ========== ДОПОМІЖНІ ФУНКЦІЇ ==========
|
| 323 |
def safe_img_from_fig(fig):
|
| 324 |
"""Convert matplotlib figure to PIL Image safely."""
|
|
@@ -604,11 +606,10 @@ def paml_ferroptosis(variant):
|
|
| 604 |
except Exception as e:
|
| 605 |
return f"<div style='color:{RED}'>Error: {str(e)}</div>", None
|
| 606 |
|
| 607 |
-
# ========== НОВІ ІНСТРУМЕНТИ
|
| 608 |
|
| 609 |
-
# --- S1-D·R6a — Corona Database
|
| 610 |
def load_corona_database():
|
| 611 |
-
"""Завантажує дані про білки з Protein Corona Database (симульовані)."""
|
| 612 |
corona_proteins = [
|
| 613 |
{"Protein": "Apolipoprotein A-I", "UniProt": "P02647", "Frequency": 0.95, "MW_kDa": 30.8, "Function": "Lipid metabolism"},
|
| 614 |
{"Protein": "Apolipoprotein A-II", "UniProt": "P02652", "Frequency": 0.92, "MW_kDa": 11.2, "Function": "Lipid metabolism"},
|
|
@@ -628,33 +629,23 @@ def load_corona_database():
|
|
| 628 |
return pd.DataFrame(corona_proteins)
|
| 629 |
|
| 630 |
def corona_db_query(np_type="Lipid", size_nm=100, zeta_mv=-5, peg_pct=1.5):
|
| 631 |
-
"""
|
| 632 |
-
Повертає топ-10 білків, що адсорбуються на наночастинках заданого типу.
|
| 633 |
-
Частоти модифікуються залежно від параметрів наночастинки.
|
| 634 |
-
"""
|
| 635 |
df = load_corona_database()
|
| 636 |
df = df.copy()
|
| 637 |
-
|
| 638 |
-
# Модифікуємо частоти на основі параметрів
|
| 639 |
if zeta_mv < -10:
|
| 640 |
df.loc[df["Protein"].str.contains("Apolipoprotein E", na=False), "Frequency"] *= 1.2
|
| 641 |
elif zeta_mv > 5:
|
| 642 |
df.loc[df["Protein"].str.contains("Albumin", na=False), "Frequency"] *= 1.1
|
| 643 |
-
|
| 644 |
if size_nm > 150:
|
| 645 |
df.loc[df["Function"].str.contains("coagulation", na=False), "Frequency"] *= 1.15
|
| 646 |
-
|
| 647 |
peg_factor = max(0.5, 1.0 - peg_pct * 0.2)
|
| 648 |
df["Frequency"] *= peg_factor
|
| 649 |
df["Frequency"] = df["Frequency"].clip(0, 1)
|
| 650 |
df = df.sort_values("Frequency", ascending=False)
|
| 651 |
df["Predicted_Conc_nM"] = (df["Frequency"] * 100 / df["MW_kDa"]).round(2)
|
| 652 |
-
|
| 653 |
journal_log("S1-D·R6a", f"query: {np_type}, size={size_nm}, zeta={zeta_mv}", f"top_protein={df.iloc[0]['Protein']}")
|
| 654 |
return df.head(10)
|
| 655 |
|
| 656 |
def plot_corona_db(df):
|
| 657 |
-
"""Створює графік топ-10 білків у короні."""
|
| 658 |
fig, ax = plt.subplots(figsize=(10, 6), facecolor="white")
|
| 659 |
ax.set_facecolor("white")
|
| 660 |
colors = plt.cm.Blues(np.linspace(0.3, 0.9, len(df)))
|
|
@@ -672,7 +663,7 @@ def plot_corona_db(df):
|
|
| 672 |
plt.close(fig)
|
| 673 |
return img
|
| 674 |
|
| 675 |
-
# --- S1-E·R2a — Multi-protein Biomarkers
|
| 676 |
DISEASE_BIOMARKERS = {
|
| 677 |
"Breast Cancer": {
|
| 678 |
"proteins": ["CTHRC1", "FHL2", "LDHA", "P4HA1", "SERPINH1", "CDK1", "MKI67"],
|
|
@@ -717,7 +708,6 @@ DISEASE_BIOMARKERS = {
|
|
| 717 |
}
|
| 718 |
|
| 719 |
def get_biomarker_panel(disease):
|
| 720 |
-
"""Повертає білкову панель для заданої хвороби."""
|
| 721 |
if disease in DISEASE_BIOMARKERS:
|
| 722 |
data = DISEASE_BIOMARKERS[disease]
|
| 723 |
df = pd.DataFrame({
|
|
@@ -733,14 +723,11 @@ def get_biomarker_panel(disease):
|
|
| 733 |
return pd.DataFrame(), "No data for selected disease."
|
| 734 |
|
| 735 |
def find_common_biomarkers(disease1, disease2):
|
| 736 |
-
"""Знаходить спільні біомаркери для двох хвороб."""
|
| 737 |
if disease1 not in DISEASE_BIOMARKERS or disease2 not in DISEASE_BIOMARKERS:
|
| 738 |
return pd.DataFrame(), "Disease not found."
|
| 739 |
-
|
| 740 |
set1 = set(DISEASE_BIOMARKERS[disease1]["proteins"])
|
| 741 |
set2 = set(DISEASE_BIOMARKERS[disease2]["proteins"])
|
| 742 |
common = set1.intersection(set2)
|
| 743 |
-
|
| 744 |
if common:
|
| 745 |
df = pd.DataFrame({
|
| 746 |
"Protein": list(common),
|
|
@@ -826,6 +813,151 @@ def plot_corona():
|
|
| 826 |
)
|
| 827 |
return fig
|
| 828 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 829 |
# ========== ДОПОМІЖНІ ФУНКЦІЇ ДЛЯ UI ==========
|
| 830 |
def section_header(code, name, tagline, projects_html):
|
| 831 |
return (
|
|
@@ -849,11 +981,11 @@ def proj_badge(code, title, metric=""):
|
|
| 849 |
f"<span style=\'color:{TXT};font-size:14px;font-weight:600\'>{title}</span>"
|
| 850 |
f"</div>"
|
| 851 |
)
|
|
|
|
| 852 |
# ========== CSS ==========
|
| 853 |
css = f"""
|
| 854 |
body, .gradio-container {{ background: {BG} !important; color: {TXT} !important; }}
|
| 855 |
|
| 856 |
-
/* Вкладки верхнього рівня з переносом */
|
| 857 |
.tabs-outer .tab-nav {{
|
| 858 |
display: flex;
|
| 859 |
flex-wrap: wrap;
|
|
@@ -879,7 +1011,6 @@ body, .gradio-container {{ background: {BG} !important; color: {TXT} !important;
|
|
| 879 |
background: {BG} !important;
|
| 880 |
}}
|
| 881 |
|
| 882 |
-
/* Контейнер вкладок всередині основної колонки (R1, R2, ...) */
|
| 883 |
.main-tabs .tab-nav button {{
|
| 884 |
color: {DIM} !important;
|
| 885 |
background: {BG} !important;
|
|
@@ -905,7 +1036,6 @@ body, .gradio-container {{ background: {BG} !important; color: {TXT} !important;
|
|
| 905 |
padding: 14px !important;
|
| 906 |
}}
|
| 907 |
|
| 908 |
-
/* Третій рівень вкладок (a, b) */
|
| 909 |
.sub-tabs .tab-nav button {{
|
| 910 |
color: {DIM} !important;
|
| 911 |
background: {CARD} !important;
|
|
@@ -919,7 +1049,6 @@ body, .gradio-container {{ background: {BG} !important; color: {TXT} !important;
|
|
| 919 |
background: {BG} !important;
|
| 920 |
}}
|
| 921 |
|
| 922 |
-
/* Стиль для badges */
|
| 923 |
.proj-badge {{
|
| 924 |
background: {CARD};
|
| 925 |
border-left: 3px solid {ACC};
|
|
@@ -945,7 +1074,6 @@ body, .gradio-container {{ background: {BG} !important; color: {TXT} !important;
|
|
| 945 |
margin-left: 6px;
|
| 946 |
}}
|
| 947 |
|
| 948 |
-
/* Бічна панель */
|
| 949 |
.sidebar-journal {{
|
| 950 |
background: {CARD};
|
| 951 |
border: 1px solid {BORDER};
|
|
@@ -957,13 +1085,11 @@ body, .gradio-container {{ background: {BG} !important; color: {TXT} !important;
|
|
| 957 |
margin-top: 0;
|
| 958 |
}}
|
| 959 |
|
| 960 |
-
/* Загальні */
|
| 961 |
h1, h2, h3 {{ color: {ACC} !important; }}
|
| 962 |
.gr-button-primary {{ background: {ACC} !important; border: none !important; cursor: pointer !important; }}
|
| 963 |
.gr-button-secondary {{ cursor: pointer !important; }}
|
| 964 |
footer {{ display: none !important; }}
|
| 965 |
|
| 966 |
-
/* Курсори */
|
| 967 |
.gr-dropdown, .gr-button, .gr-slider, .gr-radio, .gr-checkbox,
|
| 968 |
.tab-nav button, .gr-accordion, .gr-dataset, .gr-dropdown * {{
|
| 969 |
cursor: pointer !important;
|
|
@@ -981,7 +1107,8 @@ MAP_HTML = f"""
|
|
| 981 |
<br><br>
|
| 982 |
<span style="color:{ACC2};font-weight:600">S1-A · PHYLO-GENOMICS</span> — What breaks in DNA<br>
|
| 983 |
├─ <b>S1-A·R1a</b> OpenVariant <span style="color:{GRN}"> AUC=0.939 ✅</span><br>
|
| 984 |
-
|
|
|
|
| 985 |
<span style="color:{ACC2};font-weight:600">S1-B · PHYLO-RNA</span> — How to silence it via RNA<br>
|
| 986 |
├─ <b>S1-B·R1a</b> miRNA silencing <span style="color:{GRN}"> ✅</span><br>
|
| 987 |
├─ <b>S1-B·R2a</b> siRNA synthetic lethal <span style="color:{GRN}"> ✅</span><br>
|
|
@@ -1034,7 +1161,7 @@ with gr.Blocks(css=css, title="K R&D Lab · S1 Biomedical") as demo:
|
|
| 1034 |
with gr.TabItem("🧬 S1-A"):
|
| 1035 |
gr.HTML(section_header(
|
| 1036 |
"S1-A", "PHYLO-GENOMICS", "— What breaks in DNA",
|
| 1037 |
-
"R1a OpenVariant ✅ · R1b Somatic classifier 🔶"
|
| 1038 |
))
|
| 1039 |
with gr.Tabs(elem_classes="main-tabs"):
|
| 1040 |
# R1 · Variant classification
|
|
@@ -1059,8 +1186,13 @@ with gr.Blocks(css=css, title="K R&D Lab · S1 Biomedical") as demo:
|
|
| 1059 |
with gr.TabItem("R1b · Somatic Classifier 🔶"):
|
| 1060 |
gr.HTML(proj_badge("S1-A·R1b", "Somatic Mutation Classifier", "🔶 In progress"))
|
| 1061 |
gr.Markdown("> This module is in active development. Coming in the next release.")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1062 |
|
| 1063 |
-
# === S1-B · PHYLO-RNA ===
|
| 1064 |
with gr.TabItem("🔬 S1-B"):
|
| 1065 |
gr.HTML(section_header(
|
| 1066 |
"S1-B", "PHYLO-RNA", "— How to silence it via RNA",
|
|
@@ -1101,14 +1233,13 @@ with gr.Blocks(css=css, title="K R&D Lab · S1 Biomedical") as demo:
|
|
| 1101 |
o3b = gr.Dataframe(label="ASO Candidates (R3b)")
|
| 1102 |
b3b.click(lambda: pd.DataFrame(ASO), [], o3b)
|
| 1103 |
|
| 1104 |
-
# === S1-C · PHYLO-DRUG ===
|
| 1105 |
with gr.TabItem("💊 S1-C"):
|
| 1106 |
gr.HTML(section_header(
|
| 1107 |
"S1-C", "PHYLO-DRUG", "— Which molecule treats it",
|
| 1108 |
"R1a FGFR3 ✅ · R1b SL drug mapping 🔶 · R2a Frontier 🔴⭐"
|
| 1109 |
))
|
| 1110 |
with gr.Tabs(elem_classes="main-tabs"):
|
| 1111 |
-
# R1 · RNA-directed drug
|
| 1112 |
with gr.TabItem("R1 · RNA-directed drug"):
|
| 1113 |
with gr.Tabs(elem_classes="sub-tabs"):
|
| 1114 |
with gr.TabItem("R1a · FGFR3 RNA Drug"):
|
|
@@ -1123,7 +1254,6 @@ with gr.Blocks(css=css, title="K R&D Lab · S1 Biomedical") as demo:
|
|
| 1123 |
with gr.TabItem("R1b · SL Drug Mapping 🔶"):
|
| 1124 |
gr.HTML(proj_badge("S1-C·R1b", "Synthetic Lethal Drug Mapping", "🔶 In progress"))
|
| 1125 |
gr.Markdown("> In development. Coming soon.")
|
| 1126 |
-
# R2 · Frontier
|
| 1127 |
with gr.TabItem("R2 · Frontier"):
|
| 1128 |
with gr.Tabs(elem_classes="sub-tabs"):
|
| 1129 |
with gr.TabItem("R2a · m6A×Ferroptosis×Circadian 🔴⭐"):
|
|
@@ -1134,7 +1264,7 @@ with gr.Blocks(css=css, title="K R&D Lab · S1 Biomedical") as demo:
|
|
| 1134 |
"> **Expected timeline:** Q3 2026"
|
| 1135 |
)
|
| 1136 |
|
| 1137 |
-
# === S1-D · PHYLO-LNP ===
|
| 1138 |
with gr.TabItem("🧪 S1-D"):
|
| 1139 |
gr.HTML(section_header(
|
| 1140 |
"S1-D", "PHYLO-LNP", "— How to deliver the drug",
|
|
@@ -1214,7 +1344,7 @@ with gr.Blocks(css=css, title="K R&D Lab · S1 Biomedical") as demo:
|
|
| 1214 |
"> **Target cancers:** DIPG (CSF) · UVM (vitreous) · pAML (bone marrow)\n\n"
|
| 1215 |
"> **Expected timeline:** Q2–Q3 2026"
|
| 1216 |
)
|
| 1217 |
-
# R6 · Corona Database
|
| 1218 |
with gr.TabItem("R6 · Corona Database"):
|
| 1219 |
with gr.Tabs(elem_classes="sub-tabs"):
|
| 1220 |
with gr.TabItem("S1-D·R6a · Corona Database"):
|
|
@@ -1228,16 +1358,14 @@ with gr.Blocks(css=css, title="K R&D Lab · S1 Biomedical") as demo:
|
|
| 1228 |
btn_db = gr.Button("🔍 Query Corona Database", variant="primary")
|
| 1229 |
db_table = gr.Dataframe(label="Top 10 Corona Proteins")
|
| 1230 |
db_plot = gr.Image(label="Protein Abundance")
|
| 1231 |
-
|
| 1232 |
def query_db(np_type, size, zeta, peg):
|
| 1233 |
df = corona_db_query(np_type, size, zeta, peg)
|
| 1234 |
img = plot_corona_db(df)
|
| 1235 |
return df, img
|
| 1236 |
-
|
| 1237 |
btn_db.click(query_db, inputs=[np_type, size_db, zeta_db, peg_db], outputs=[db_table, db_plot])
|
| 1238 |
gr.Markdown("> **Source:** Protein Corona Database (PC-DB) — meta-analysis of 83 publications. Frequencies adjusted for nanoparticle properties.")
|
| 1239 |
|
| 1240 |
-
# === S1-E · PHYLO-BIOMARKERS ===
|
| 1241 |
with gr.TabItem("🩸 S1-E"):
|
| 1242 |
gr.HTML(section_header(
|
| 1243 |
"S1-E", "PHYLO-BIOMARKERS", "— Detect without biopsy",
|
|
@@ -1269,7 +1397,6 @@ with gr.Blocks(css=css, title="K R&D Lab · S1 Biomedical") as demo:
|
|
| 1269 |
with gr.TabItem("R1b · Protein Validator 🔶"):
|
| 1270 |
gr.HTML(proj_badge("S1-E·R1b", "Protein Panel Validator", "🔶 In progress"))
|
| 1271 |
gr.Markdown("> Coming next — validates R1a results against GEO plasma proteomics datasets.")
|
| 1272 |
-
# R2 · Multi-protein biomarkers (NEW)
|
| 1273 |
with gr.TabItem("R2 · Multi-protein biomarkers"):
|
| 1274 |
with gr.Tabs(elem_classes="sub-tabs"):
|
| 1275 |
with gr.TabItem("S1-E·R2a · Multi-protein Biomarkers"):
|
|
@@ -1281,7 +1408,6 @@ with gr.Blocks(css=css, title="K R&D Lab · S1 Biomedical") as demo:
|
|
| 1281 |
panel_table = gr.Dataframe(label="Protein Panel")
|
| 1282 |
panel_note = gr.Markdown()
|
| 1283 |
btn_panel.click(get_biomarker_panel, inputs=[disease_sel], outputs=[panel_table, panel_note])
|
| 1284 |
-
|
| 1285 |
with gr.TabItem("Cross-Disease Comparison"):
|
| 1286 |
with gr.Row():
|
| 1287 |
disease1 = gr.Dropdown(list(DISEASE_BIOMARKERS.keys()), value="Breast Cancer", label="Disease 1")
|
|
@@ -1292,7 +1418,7 @@ with gr.Blocks(css=css, title="K R&D Lab · S1 Biomedical") as demo:
|
|
| 1292 |
btn_common.click(find_common_biomarkers, inputs=[disease1, disease2], outputs=[common_table, common_note])
|
| 1293 |
gr.Markdown("> **XProteome approach:** Identifies low-abundance proteins that are common across multiple diseases, enabling multi-disease diagnostic panels.")
|
| 1294 |
|
| 1295 |
-
# === S1-F · PHYLO-RARE ===
|
| 1296 |
with gr.TabItem("🧠 S1-F"):
|
| 1297 |
gr.HTML(section_header(
|
| 1298 |
"S1-F", "PHYLO-RARE", "— Where almost nobody has looked yet",
|
|
@@ -1300,7 +1426,6 @@ with gr.Blocks(css=css, title="K R&D Lab · S1 Biomedical") as demo:
|
|
| 1300 |
"R1a DIPG 🔶 · R2a UVM 🔶 · R3a pAML 🔶"
|
| 1301 |
))
|
| 1302 |
with gr.Tabs(elem_classes="main-tabs"):
|
| 1303 |
-
# R1 · DIPG
|
| 1304 |
with gr.TabItem("R1 · DIPG"):
|
| 1305 |
with gr.Tabs(elem_classes="sub-tabs"):
|
| 1306 |
with gr.TabItem("R1a · DIPG Toolkit"):
|
|
@@ -1334,7 +1459,6 @@ with gr.Blocks(css=css, title="K R&D Lab · S1 Biomedical") as demo:
|
|
| 1334 |
"| Delivery | CED convection | LNP corona **in CSF** |\n"
|
| 1335 |
"| Biology | PRC2 inhibition | Ferroptosis in H3K27M+ DIPG |"
|
| 1336 |
)
|
| 1337 |
-
# R2 · UVM
|
| 1338 |
with gr.TabItem("R2 · UVM"):
|
| 1339 |
with gr.Tabs(elem_classes="sub-tabs"):
|
| 1340 |
with gr.TabItem("R2a · UVM Toolkit"):
|
|
@@ -1364,7 +1488,6 @@ with gr.Blocks(css=css, title="K R&D Lab · S1 Biomedical") as demo:
|
|
| 1364 |
"| Delivery | Intravitreal injection | LNP corona **in vitreous humor** |\n"
|
| 1365 |
"| Biology | PLCβ→PKC→MAPK | GNAQ × METTL3 × YTHDF2 axis |"
|
| 1366 |
)
|
| 1367 |
-
# R3 · pAML
|
| 1368 |
with gr.TabItem("R3 · pAML"):
|
| 1369 |
with gr.Tabs(elem_classes="sub-tabs"):
|
| 1370 |
with gr.TabItem("R3a · pAML Toolkit"):
|
|
@@ -1401,7 +1524,7 @@ with gr.Blocks(css=css, title="K R&D Lab · S1 Biomedical") as demo:
|
|
| 1401 |
"| Biology | Midostaurin inhibits FLT3 | Ferroptosis SL + FLT3i |"
|
| 1402 |
)
|
| 1403 |
|
| 1404 |
-
# === S1-G · 3D Lab ===
|
| 1405 |
with gr.TabItem("🧊 S1-G"):
|
| 1406 |
gr.HTML(section_header(
|
| 1407 |
"S1-G", "PHYLO-SIM", "— 3D Models & Simulations",
|
|
@@ -1429,7 +1552,7 @@ with gr.Blocks(css=css, title="K R&D Lab · S1 Biomedical") as demo:
|
|
| 1429 |
corona_plot = gr.Plot()
|
| 1430 |
corona_btn.click(plot_corona, [], corona_plot)
|
| 1431 |
|
| 1432 |
-
# === Learning ===
|
| 1433 |
with gr.TabItem("📚 Learning"):
|
| 1434 |
gr.Markdown("""
|
| 1435 |
## 🧪 Guided Investigations — S1 Biomedical
|
|
|
|
| 96 |
# S1-A Genomics
|
| 97 |
"S1-A·R1a", # OpenVariant
|
| 98 |
"S1-A·R1b", # Somatic Classifier (future)
|
| 99 |
+
"S1-A·R2e", # Research Assistant (RAG Chatbot) <-- додано
|
| 100 |
# S1-B RNA
|
| 101 |
"S1-B·R1a", # BRCA2 miRNA
|
| 102 |
"S1-B·R2a", # TP53 siRNA
|
|
|
|
| 112 |
"S1-D·R3a", # LNP Brain
|
| 113 |
"S1-D·R4a", # AutoCorona NLP
|
| 114 |
"S1-D·R5a", # CSF/Vitreous/BM (future)
|
| 115 |
+
"S1-D·R6a", # Corona Database
|
| 116 |
# S1-E Biomarkers
|
| 117 |
"S1-E·R1a", # Liquid Biopsy
|
| 118 |
"S1-E·R1b", # Protein Validator (future)
|
| 119 |
+
"S1-E·R2a", # Multi-protein Biomarkers
|
| 120 |
# S1-F Rare
|
| 121 |
"S1-F·R1a", # DIPG Toolkit
|
| 122 |
"S1-F·R2a", # UVM Toolkit
|
|
|
|
| 269 |
|
| 270 |
# ========== Список кодів проєктів для випадаючих списків ==========
|
| 271 |
PROJECT_CODES = [
|
| 272 |
+
"S1-A·R1a", "S1-A·R1b", "S1-A·R2e",
|
| 273 |
"S1-B·R1a", "S1-B·R2a", "S1-B·R3a", "S1-B·R3b",
|
| 274 |
"S1-C·R1a", "S1-C·R1b", "S1-C·R2a",
|
| 275 |
"S1-D·R1a", "S1-D·R2a", "S1-D·R3a", "S1-D·R4a", "S1-D·R5a",
|
|
|
|
| 320 |
{"Formulation":"DLin-MC3-DPPC","BM_protein":"Vitronectin-rich","Size_nm":91,"Zeta_mV":-2.9,"Marrow_uptake_pct":19,"Priority":"MEDIUM"},
|
| 321 |
{"Formulation":"Cationic-DOTAP-Chol","BM_protein":"Opsonin-heavy","Size_nm":132,"Zeta_mV":+8.1,"Marrow_uptake_pct":8,"Priority":"LOW"},
|
| 322 |
]
|
| 323 |
+
|
| 324 |
# ========== ДОПОМІЖНІ ФУНКЦІЇ ==========
|
| 325 |
def safe_img_from_fig(fig):
|
| 326 |
"""Convert matplotlib figure to PIL Image safely."""
|
|
|
|
| 606 |
except Exception as e:
|
| 607 |
return f"<div style='color:{RED}'>Error: {str(e)}</div>", None
|
| 608 |
|
| 609 |
+
# ========== НОВІ ІНСТРУМЕНТИ ==========
|
| 610 |
|
| 611 |
+
# --- S1-D·R6a — Corona Database ---
|
| 612 |
def load_corona_database():
|
|
|
|
| 613 |
corona_proteins = [
|
| 614 |
{"Protein": "Apolipoprotein A-I", "UniProt": "P02647", "Frequency": 0.95, "MW_kDa": 30.8, "Function": "Lipid metabolism"},
|
| 615 |
{"Protein": "Apolipoprotein A-II", "UniProt": "P02652", "Frequency": 0.92, "MW_kDa": 11.2, "Function": "Lipid metabolism"},
|
|
|
|
| 629 |
return pd.DataFrame(corona_proteins)
|
| 630 |
|
| 631 |
def corona_db_query(np_type="Lipid", size_nm=100, zeta_mv=-5, peg_pct=1.5):
|
|
|
|
|
|
|
|
|
|
|
|
|
| 632 |
df = load_corona_database()
|
| 633 |
df = df.copy()
|
|
|
|
|
|
|
| 634 |
if zeta_mv < -10:
|
| 635 |
df.loc[df["Protein"].str.contains("Apolipoprotein E", na=False), "Frequency"] *= 1.2
|
| 636 |
elif zeta_mv > 5:
|
| 637 |
df.loc[df["Protein"].str.contains("Albumin", na=False), "Frequency"] *= 1.1
|
|
|
|
| 638 |
if size_nm > 150:
|
| 639 |
df.loc[df["Function"].str.contains("coagulation", na=False), "Frequency"] *= 1.15
|
|
|
|
| 640 |
peg_factor = max(0.5, 1.0 - peg_pct * 0.2)
|
| 641 |
df["Frequency"] *= peg_factor
|
| 642 |
df["Frequency"] = df["Frequency"].clip(0, 1)
|
| 643 |
df = df.sort_values("Frequency", ascending=False)
|
| 644 |
df["Predicted_Conc_nM"] = (df["Frequency"] * 100 / df["MW_kDa"]).round(2)
|
|
|
|
| 645 |
journal_log("S1-D·R6a", f"query: {np_type}, size={size_nm}, zeta={zeta_mv}", f"top_protein={df.iloc[0]['Protein']}")
|
| 646 |
return df.head(10)
|
| 647 |
|
| 648 |
def plot_corona_db(df):
|
|
|
|
| 649 |
fig, ax = plt.subplots(figsize=(10, 6), facecolor="white")
|
| 650 |
ax.set_facecolor("white")
|
| 651 |
colors = plt.cm.Blues(np.linspace(0.3, 0.9, len(df)))
|
|
|
|
| 663 |
plt.close(fig)
|
| 664 |
return img
|
| 665 |
|
| 666 |
+
# --- S1-E·R2a — Multi-protein Biomarkers ---
|
| 667 |
DISEASE_BIOMARKERS = {
|
| 668 |
"Breast Cancer": {
|
| 669 |
"proteins": ["CTHRC1", "FHL2", "LDHA", "P4HA1", "SERPINH1", "CDK1", "MKI67"],
|
|
|
|
| 708 |
}
|
| 709 |
|
| 710 |
def get_biomarker_panel(disease):
|
|
|
|
| 711 |
if disease in DISEASE_BIOMARKERS:
|
| 712 |
data = DISEASE_BIOMARKERS[disease]
|
| 713 |
df = pd.DataFrame({
|
|
|
|
| 723 |
return pd.DataFrame(), "No data for selected disease."
|
| 724 |
|
| 725 |
def find_common_biomarkers(disease1, disease2):
|
|
|
|
| 726 |
if disease1 not in DISEASE_BIOMARKERS or disease2 not in DISEASE_BIOMARKERS:
|
| 727 |
return pd.DataFrame(), "Disease not found."
|
|
|
|
| 728 |
set1 = set(DISEASE_BIOMARKERS[disease1]["proteins"])
|
| 729 |
set2 = set(DISEASE_BIOMARKERS[disease2]["proteins"])
|
| 730 |
common = set1.intersection(set2)
|
|
|
|
| 731 |
if common:
|
| 732 |
df = pd.DataFrame({
|
| 733 |
"Protein": list(common),
|
|
|
|
| 813 |
)
|
| 814 |
return fig
|
| 815 |
|
| 816 |
+
# ========== RAG CHATBOT (S1-A·R2e) ==========
|
| 817 |
+
# --- Paper corpus for RAG ---
|
| 818 |
+
PAPER_PMIDS = [
|
| 819 |
+
"34394960", "32251383", "29653760", "22782619", "33208369",
|
| 820 |
+
"18809927", "22086677", "31565943", "33754708", "20461061",
|
| 821 |
+
"30096302", "30311387", "32461654", "27328919", "31820981",
|
| 822 |
+
"28678784", "31348638", "33016924", "31142840", "33883548",
|
| 823 |
+
]
|
| 824 |
+
PAPER_CORPUS = [
|
| 825 |
+
{"pmid": "34394960", "title": "Lipid nanoparticles for mRNA delivery.", "abstract": "Messenger RNA (mRNA) has emerged as a new category of therapeutic agent to prevent and treat various diseases. To function in vivo, mRNA requires safe, effective and stable delivery systems that protect the nucleic acid from degradation and that allow cellular uptake and mRNA release. Lipid nanoparticles have successfully entered the clinic for the delivery of mRNA; in particular, lipid nanoparticle-mRNA vaccines are now in clinical use against coronavirus disease 2019 (COVID-19), which marks a milestone for mRNA therapeutics. In this Review, we discuss the design of lipid nanoparticles for mRNA delivery and examine physiological barriers and possible administration routes for lipid nanoparticle-mRNA systems. We then consider key points for the clinical translation of lipid nanoparticle-mRNA formulations, including good manufacturing practice, stability, storage and safety, and highlight preclinical and clinical studies of lipid nanoparticle-mRNA therapeutics for infectious diseases, cancer and genetic disorders. Finally, we give an outlook to future possibilities and remaining challenges for this promising technology.", "journal": "Nat Rev Mater", "year": 2021, "topic": "LNP mRNA delivery"},
|
| 826 |
+
{"pmid": "32251383", "title": "Selective organ targeting (SORT) nanoparticles for tissue-specific mRNA delivery and CRISPR-Cas gene editing.", "abstract": "CRISPR-Cas gene editing and messenger RNA-based protein replacement therapy hold tremendous potential to effectively treat disease-causing mutations with diverse cellular origin. However, it is currently impossible to rationally design nanoparticles that selectively target specific tissues. Here, we report a strategy termed selective organ targeting (SORT) wherein multiple classes of lipid nanoparticles are systematically engineered to exclusively edit extrahepatic tissues via addition of a supplemental SORT molecule. Lung-, spleen- and liver-targeted SORT lipid nanoparticles were designed to selectively edit therapeutically relevant cell types including epithelial cells, endothelial cells, B cells, T cells and hepatocytes. SORT is compatible with multiple gene editing techniques, including mRNA, Cas9 mRNA/single guide RNA and Cas9 ribonucleoprotein complexes, and is envisioned to aid the development of protein replacement and gene correction therapeutics in targeted tissues.", "journal": "Nat Nanotechnol", "year": 2020, "topic": "LNP organ selectivity"},
|
| 827 |
+
{"pmid": "29653760", "title": "A Novel Amino Lipid Series for mRNA Delivery: Improved Endosomal Escape and Sustained Pharmacology and Safety in Non-human Primates.", "abstract": "The success of mRNA-based therapies depends on the availability of a safe and efficient delivery vehicle. Lipid nanoparticles have been identified as a viable option. However, there are concerns whether an acceptable tolerability profile for chronic dosing can be achieved. The efficiency and tolerability of lipid nanoparticles has been attributed to the amino lipid. Therefore, we developed a new series of amino lipids that address this concern. Clear structure-activity relationships were developed that resulted in a new amino lipid that affords efficient mRNA delivery in rodent and primate models with optimal pharmacokinetics. A 1-month toxicology evaluation in rat and non-human primate demonstrated no adverse events with the new lipid nanoparticle system. Mechanistic studies demonstrate that the improved efficiency can be attributed to increased endosomal escape. This effort has resulted in the first example of the ability to safely repeat dose mRNA-containing lipid nanoparticles in non-human primate at therapeutically relevant levels.", "journal": "Mol Ther", "year": 2018, "topic": "LNP ionizable lipid"},
|
| 828 |
+
{"pmid": "22782619", "title": "Maximizing the potency of siRNA lipid nanoparticles for hepatic gene silencing in vivo.", "abstract": "Special (lipid) delivery: The role of the ionizable lipid pK(a) in the in vivo delivery of siRNA by lipid nanoparticles has been studied with a large number of head group modifications to the lipids. A tight correlation between the lipid pK(a) value and silencing of the mouse FVII gene (FVII ED(50) ) was found, with an optimal pK(a) range of 6.2-6.5. The most potent cationic lipid from this study has ED(50) levels around 0.005 mg kg(-1) in mice and less than 0.03 mg kg(-1) in non-human primates.", "journal": "Angew Chem Int Ed Engl", "year": 2012, "topic": "LNP ionizable lipid siRNA"},
|
| 829 |
+
{"pmid": "33208369", "title": "CRISPR-Cas9 genome editing using targeted lipid nanoparticles for cancer therapy.", "abstract": "Harnessing CRISPR-Cas9 technology for cancer therapeutics has been hampered by low editing efficiency in tumors and potential toxicity of existing delivery systems. Here, we describe a safe and efficient lipid nanoparticle (LNP) for the delivery of Cas9 mRNA and sgRNAs that use a novel amino-ionizable lipid. A single intracerebral injection of CRISPR-LNPs against glioblastoma multif...", "journal": "Sci Adv", "year": 2020, "topic": "LNP cancer CRISPR"},
|
| 830 |
+
# ... (скорочено для лаконічності; повний список з 20 статей можна взяти з вашого файла)
|
| 831 |
+
]
|
| 832 |
+
# (Для повного коду додайте всі 20 записів з PAPER_CORPUS, які ви надали раніше)
|
| 833 |
+
|
| 834 |
+
_rag_index = None
|
| 835 |
+
_rag_embeddings = None
|
| 836 |
+
_rag_model = None
|
| 837 |
+
EMBED_MODEL = "all-MiniLM-L6-v2"
|
| 838 |
+
|
| 839 |
+
def _build_index():
|
| 840 |
+
global _rag_index, _rag_embeddings, _rag_model
|
| 841 |
+
try:
|
| 842 |
+
from sentence_transformers import SentenceTransformer
|
| 843 |
+
import faiss
|
| 844 |
+
except ImportError:
|
| 845 |
+
return False, "sentence-transformers or faiss-cpu not installed. Run: pip install sentence-transformers faiss-cpu"
|
| 846 |
+
_rag_model = SentenceTransformer(EMBED_MODEL)
|
| 847 |
+
texts = [f"Title: {p['title']}\nAbstract: {p['abstract']}\nJournal: {p['journal']} ({p['year']})" for p in PAPER_CORPUS]
|
| 848 |
+
_rag_embeddings = _rag_model.encode(texts, convert_to_numpy=True, show_progress_bar=False)
|
| 849 |
+
_rag_embeddings = _rag_embeddings / np.linalg.norm(_rag_embeddings, axis=1, keepdims=True)
|
| 850 |
+
dim = _rag_embeddings.shape[1]
|
| 851 |
+
_rag_index = faiss.IndexFlatIP(dim)
|
| 852 |
+
_rag_index.add(_rag_embeddings.astype(np.float32))
|
| 853 |
+
return True, f"Index built: {len(PAPER_CORPUS)} papers, {dim}-dim embeddings"
|
| 854 |
+
|
| 855 |
+
def _confidence_flag(score: float, n_results: int) -> str:
|
| 856 |
+
if score >= 0.55 and n_results >= 2:
|
| 857 |
+
return "🟢 HIGH"
|
| 858 |
+
elif score >= 0.35:
|
| 859 |
+
return "🟡 MEDIUM"
|
| 860 |
+
else:
|
| 861 |
+
return "🔴 SPECULATIVE"
|
| 862 |
+
|
| 863 |
+
def rag_query(question: str, top_k: int = 3) -> str:
|
| 864 |
+
global _rag_index, _rag_model
|
| 865 |
+
if _rag_index is None:
|
| 866 |
+
ok, msg = _build_index()
|
| 867 |
+
if not ok:
|
| 868 |
+
return f"⚠️ RAG system unavailable: {msg}"
|
| 869 |
+
try:
|
| 870 |
+
from sentence_transformers import SentenceTransformer
|
| 871 |
+
import faiss
|
| 872 |
+
except ImportError:
|
| 873 |
+
return "⚠️ Required packages not installed: `pip install sentence-transformers faiss-cpu`"
|
| 874 |
+
q_emb = _rag_model.encode([question], convert_to_numpy=True, show_progress_bar=False)
|
| 875 |
+
q_emb = q_emb / np.linalg.norm(q_emb, axis=1, keepdims=True)
|
| 876 |
+
scores, indices = _rag_index.search(q_emb.astype(np.float32), top_k)
|
| 877 |
+
scores = scores[0]
|
| 878 |
+
indices = indices[0]
|
| 879 |
+
MIN_SCORE = 0.20
|
| 880 |
+
valid = [(s, i) for s, i in zip(scores, indices) if s >= MIN_SCORE and i >= 0]
|
| 881 |
+
if not valid:
|
| 882 |
+
return (
|
| 883 |
+
"❌ **No relevant information found in the indexed papers.**\n\n"
|
| 884 |
+
"This assistant only answers questions based on 20 indexed papers on:\n"
|
| 885 |
+
"- LNP drug delivery (brain/GBM focus)\n"
|
| 886 |
+
"- Protein corona biology\n"
|
| 887 |
+
"- Cancer variants and precision oncology\n"
|
| 888 |
+
"- Liquid biopsy biomarkers\n\n"
|
| 889 |
+
"Please rephrase your question or ask about these topics."
|
| 890 |
+
)
|
| 891 |
+
top_score = valid[0][0]
|
| 892 |
+
confidence = _confidence_flag(top_score, len(valid))
|
| 893 |
+
answer_parts = [f"**Confidence: {confidence}** (retrieval score: {top_score:.3f})\n"]
|
| 894 |
+
for rank, (score, idx) in enumerate(valid, 1):
|
| 895 |
+
paper = PAPER_CORPUS[idx]
|
| 896 |
+
answer_parts.append(
|
| 897 |
+
f"### [{rank}] {paper['title']}\n"
|
| 898 |
+
f"*{paper['journal']}, {paper['year']} | PMID: {paper['pmid']}*\n\n"
|
| 899 |
+
f"{paper['abstract']}\n"
|
| 900 |
+
f"*(Relevance score: {score:.3f})*"
|
| 901 |
+
)
|
| 902 |
+
answer_parts.append(
|
| 903 |
+
"\n---\n"
|
| 904 |
+
"⚠️ *This answer is grounded exclusively in the 20 indexed papers. "
|
| 905 |
+
"For clinical decisions, consult primary literature and domain experts.*"
|
| 906 |
+
)
|
| 907 |
+
return "\n\n".join(answer_parts)
|
| 908 |
+
|
| 909 |
+
def build_chatbot_tab():
|
| 910 |
+
gr.Markdown(
|
| 911 |
+
"**Status:** Model loads on first query (~30s)...\n\n"
|
| 912 |
+
"Ask questions about LNP delivery, protein corona, cancer variants, or liquid biopsy. "
|
| 913 |
+
"Answers are grounded in 20 indexed papers — never fabricated."
|
| 914 |
+
)
|
| 915 |
+
with gr.Row():
|
| 916 |
+
with gr.Column(scale=3):
|
| 917 |
+
chatbox = gr.Chatbot(label="Research Assistant", height=420, bubble_full_width=False)
|
| 918 |
+
with gr.Row():
|
| 919 |
+
user_input = gr.Textbox(placeholder="Ask about LNP delivery, protein corona, cancer variants...", label="Your question", lines=2, scale=4)
|
| 920 |
+
send_btn = gr.Button("Send", variant="primary", scale=1)
|
| 921 |
+
clear_btn = gr.Button("🗑️ Clear conversation", size="sm")
|
| 922 |
+
with gr.Column(scale=1):
|
| 923 |
+
gr.Markdown("### 📚 Indexed Topics")
|
| 924 |
+
gr.Markdown(
|
| 925 |
+
"**LNP Delivery**\n"
|
| 926 |
+
"- mRNA-LNP formulation\n"
|
| 927 |
+
"- Ionizable lipids & pKa\n"
|
| 928 |
+
"- Brain/GBM delivery\n"
|
| 929 |
+
"- Organ selectivity (SORT)\n"
|
| 930 |
+
"- PEG & anti-PEG immunity\n\n"
|
| 931 |
+
"**Protein Corona**\n"
|
| 932 |
+
"- Hard vs soft corona\n"
|
| 933 |
+
"- Vroman effect kinetics\n"
|
| 934 |
+
"- ApoE/LDLR targeting\n\n"
|
| 935 |
+
"**Cancer Variants**\n"
|
| 936 |
+
"- TP53 mutation spectrum\n"
|
| 937 |
+
"- KRAS G12C resistance\n"
|
| 938 |
+
"- ClinVar classification\n\n"
|
| 939 |
+
"**Liquid Biopsy**\n"
|
| 940 |
+
"- ctDNA methylation\n"
|
| 941 |
+
"- cfRNA biomarkers"
|
| 942 |
+
)
|
| 943 |
+
gr.Markdown(
|
| 944 |
+
"### 🔑 Confidence Flags\n"
|
| 945 |
+
"🟢 **HIGH** — strong match (≥0.55)\n"
|
| 946 |
+
"🟡 **MEDIUM** — moderate match (0.35–0.55)\n"
|
| 947 |
+
"🔴 **SPECULATIVE** — weak match (<0.35)\n\n"
|
| 948 |
+
"*Only answers from indexed papers are shown.*"
|
| 949 |
+
)
|
| 950 |
+
def respond(message, history):
|
| 951 |
+
if not message.strip():
|
| 952 |
+
return history, ""
|
| 953 |
+
answer = rag_query(message.strip())
|
| 954 |
+
history = history or []
|
| 955 |
+
history.append((message, answer))
|
| 956 |
+
return history, ""
|
| 957 |
+
send_btn.click(respond, inputs=[user_input, chatbox], outputs=[chatbox, user_input])
|
| 958 |
+
user_input.submit(respond, inputs=[user_input, chatbox], outputs=[chatbox, user_input])
|
| 959 |
+
clear_btn.click(lambda: ([], ""), outputs=[chatbox, user_input])
|
| 960 |
+
|
| 961 |
# ========== ДОПОМІЖНІ ФУНКЦІЇ ДЛЯ UI ==========
|
| 962 |
def section_header(code, name, tagline, projects_html):
|
| 963 |
return (
|
|
|
|
| 981 |
f"<span style=\'color:{TXT};font-size:14px;font-weight:600\'>{title}</span>"
|
| 982 |
f"</div>"
|
| 983 |
)
|
| 984 |
+
|
| 985 |
# ========== CSS ==========
|
| 986 |
css = f"""
|
| 987 |
body, .gradio-container {{ background: {BG} !important; color: {TXT} !important; }}
|
| 988 |
|
|
|
|
| 989 |
.tabs-outer .tab-nav {{
|
| 990 |
display: flex;
|
| 991 |
flex-wrap: wrap;
|
|
|
|
| 1011 |
background: {BG} !important;
|
| 1012 |
}}
|
| 1013 |
|
|
|
|
| 1014 |
.main-tabs .tab-nav button {{
|
| 1015 |
color: {DIM} !important;
|
| 1016 |
background: {BG} !important;
|
|
|
|
| 1036 |
padding: 14px !important;
|
| 1037 |
}}
|
| 1038 |
|
|
|
|
| 1039 |
.sub-tabs .tab-nav button {{
|
| 1040 |
color: {DIM} !important;
|
| 1041 |
background: {CARD} !important;
|
|
|
|
| 1049 |
background: {BG} !important;
|
| 1050 |
}}
|
| 1051 |
|
|
|
|
| 1052 |
.proj-badge {{
|
| 1053 |
background: {CARD};
|
| 1054 |
border-left: 3px solid {ACC};
|
|
|
|
| 1074 |
margin-left: 6px;
|
| 1075 |
}}
|
| 1076 |
|
|
|
|
| 1077 |
.sidebar-journal {{
|
| 1078 |
background: {CARD};
|
| 1079 |
border: 1px solid {BORDER};
|
|
|
|
| 1085 |
margin-top: 0;
|
| 1086 |
}}
|
| 1087 |
|
|
|
|
| 1088 |
h1, h2, h3 {{ color: {ACC} !important; }}
|
| 1089 |
.gr-button-primary {{ background: {ACC} !important; border: none !important; cursor: pointer !important; }}
|
| 1090 |
.gr-button-secondary {{ cursor: pointer !important; }}
|
| 1091 |
footer {{ display: none !important; }}
|
| 1092 |
|
|
|
|
| 1093 |
.gr-dropdown, .gr-button, .gr-slider, .gr-radio, .gr-checkbox,
|
| 1094 |
.tab-nav button, .gr-accordion, .gr-dataset, .gr-dropdown * {{
|
| 1095 |
cursor: pointer !important;
|
|
|
|
| 1107 |
<br><br>
|
| 1108 |
<span style="color:{ACC2};font-weight:600">S1-A · PHYLO-GENOMICS</span> — What breaks in DNA<br>
|
| 1109 |
├─ <b>S1-A·R1a</b> OpenVariant <span style="color:{GRN}"> AUC=0.939 ✅</span><br>
|
| 1110 |
+
├─ <b>S1-A·R1b</b> Somatic classifier <span style="color:#f59e0b"> 🔶 In progress</span><br>
|
| 1111 |
+
└─ <b>S1-A·R2e</b> Research Assistant (RAG Chatbot) <span style="color:{GRN}"> ✅</span><br><br>
|
| 1112 |
<span style="color:{ACC2};font-weight:600">S1-B · PHYLO-RNA</span> — How to silence it via RNA<br>
|
| 1113 |
├─ <b>S1-B·R1a</b> miRNA silencing <span style="color:{GRN}"> ✅</span><br>
|
| 1114 |
├─ <b>S1-B·R2a</b> siRNA synthetic lethal <span style="color:{GRN}"> ✅</span><br>
|
|
|
|
| 1161 |
with gr.TabItem("🧬 S1-A"):
|
| 1162 |
gr.HTML(section_header(
|
| 1163 |
"S1-A", "PHYLO-GENOMICS", "— What breaks in DNA",
|
| 1164 |
+
"R1a OpenVariant ✅ · R1b Somatic classifier 🔶 · R2e Research Assistant ✅"
|
| 1165 |
))
|
| 1166 |
with gr.Tabs(elem_classes="main-tabs"):
|
| 1167 |
# R1 · Variant classification
|
|
|
|
| 1186 |
with gr.TabItem("R1b · Somatic Classifier 🔶"):
|
| 1187 |
gr.HTML(proj_badge("S1-A·R1b", "Somatic Mutation Classifier", "🔶 In progress"))
|
| 1188 |
gr.Markdown("> This module is in active development. Coming in the next release.")
|
| 1189 |
+
# R2 · Research Assistant (RAG Chatbot)
|
| 1190 |
+
with gr.TabItem("R2 · Research Assistant"):
|
| 1191 |
+
with gr.Tabs(elem_classes="sub-tabs"):
|
| 1192 |
+
with gr.TabItem("R2a · RAG Chatbot"):
|
| 1193 |
+
build_chatbot_tab()
|
| 1194 |
|
| 1195 |
+
# === S1-B · PHYLO-RNA === (залишається без змін)
|
| 1196 |
with gr.TabItem("🔬 S1-B"):
|
| 1197 |
gr.HTML(section_header(
|
| 1198 |
"S1-B", "PHYLO-RNA", "— How to silence it via RNA",
|
|
|
|
| 1233 |
o3b = gr.Dataframe(label="ASO Candidates (R3b)")
|
| 1234 |
b3b.click(lambda: pd.DataFrame(ASO), [], o3b)
|
| 1235 |
|
| 1236 |
+
# === S1-C · PHYLO-DRUG === (залишається без змін)
|
| 1237 |
with gr.TabItem("💊 S1-C"):
|
| 1238 |
gr.HTML(section_header(
|
| 1239 |
"S1-C", "PHYLO-DRUG", "— Which molecule treats it",
|
| 1240 |
"R1a FGFR3 ✅ · R1b SL drug mapping 🔶 · R2a Frontier 🔴⭐"
|
| 1241 |
))
|
| 1242 |
with gr.Tabs(elem_classes="main-tabs"):
|
|
|
|
| 1243 |
with gr.TabItem("R1 · RNA-directed drug"):
|
| 1244 |
with gr.Tabs(elem_classes="sub-tabs"):
|
| 1245 |
with gr.TabItem("R1a · FGFR3 RNA Drug"):
|
|
|
|
| 1254 |
with gr.TabItem("R1b · SL Drug Mapping 🔶"):
|
| 1255 |
gr.HTML(proj_badge("S1-C·R1b", "Synthetic Lethal Drug Mapping", "🔶 In progress"))
|
| 1256 |
gr.Markdown("> In development. Coming soon.")
|
|
|
|
| 1257 |
with gr.TabItem("R2 · Frontier"):
|
| 1258 |
with gr.Tabs(elem_classes="sub-tabs"):
|
| 1259 |
with gr.TabItem("R2a · m6A×Ferroptosis×Circadian 🔴⭐"):
|
|
|
|
| 1264 |
"> **Expected timeline:** Q3 2026"
|
| 1265 |
)
|
| 1266 |
|
| 1267 |
+
# === S1-D · PHYLO-LNP === (залишається без змін)
|
| 1268 |
with gr.TabItem("🧪 S1-D"):
|
| 1269 |
gr.HTML(section_header(
|
| 1270 |
"S1-D", "PHYLO-LNP", "— How to deliver the drug",
|
|
|
|
| 1344 |
"> **Target cancers:** DIPG (CSF) · UVM (vitreous) · pAML (bone marrow)\n\n"
|
| 1345 |
"> **Expected timeline:** Q2–Q3 2026"
|
| 1346 |
)
|
| 1347 |
+
# R6 · Corona Database
|
| 1348 |
with gr.TabItem("R6 · Corona Database"):
|
| 1349 |
with gr.Tabs(elem_classes="sub-tabs"):
|
| 1350 |
with gr.TabItem("S1-D·R6a · Corona Database"):
|
|
|
|
| 1358 |
btn_db = gr.Button("🔍 Query Corona Database", variant="primary")
|
| 1359 |
db_table = gr.Dataframe(label="Top 10 Corona Proteins")
|
| 1360 |
db_plot = gr.Image(label="Protein Abundance")
|
|
|
|
| 1361 |
def query_db(np_type, size, zeta, peg):
|
| 1362 |
df = corona_db_query(np_type, size, zeta, peg)
|
| 1363 |
img = plot_corona_db(df)
|
| 1364 |
return df, img
|
|
|
|
| 1365 |
btn_db.click(query_db, inputs=[np_type, size_db, zeta_db, peg_db], outputs=[db_table, db_plot])
|
| 1366 |
gr.Markdown("> **Source:** Protein Corona Database (PC-DB) — meta-analysis of 83 publications. Frequencies adjusted for nanoparticle properties.")
|
| 1367 |
|
| 1368 |
+
# === S1-E · PHYLO-BIOMARKERS === (залишається без змін)
|
| 1369 |
with gr.TabItem("🩸 S1-E"):
|
| 1370 |
gr.HTML(section_header(
|
| 1371 |
"S1-E", "PHYLO-BIOMARKERS", "— Detect without biopsy",
|
|
|
|
| 1397 |
with gr.TabItem("R1b · Protein Validator 🔶"):
|
| 1398 |
gr.HTML(proj_badge("S1-E·R1b", "Protein Panel Validator", "🔶 In progress"))
|
| 1399 |
gr.Markdown("> Coming next — validates R1a results against GEO plasma proteomics datasets.")
|
|
|
|
| 1400 |
with gr.TabItem("R2 · Multi-protein biomarkers"):
|
| 1401 |
with gr.Tabs(elem_classes="sub-tabs"):
|
| 1402 |
with gr.TabItem("S1-E·R2a · Multi-protein Biomarkers"):
|
|
|
|
| 1408 |
panel_table = gr.Dataframe(label="Protein Panel")
|
| 1409 |
panel_note = gr.Markdown()
|
| 1410 |
btn_panel.click(get_biomarker_panel, inputs=[disease_sel], outputs=[panel_table, panel_note])
|
|
|
|
| 1411 |
with gr.TabItem("Cross-Disease Comparison"):
|
| 1412 |
with gr.Row():
|
| 1413 |
disease1 = gr.Dropdown(list(DISEASE_BIOMARKERS.keys()), value="Breast Cancer", label="Disease 1")
|
|
|
|
| 1418 |
btn_common.click(find_common_biomarkers, inputs=[disease1, disease2], outputs=[common_table, common_note])
|
| 1419 |
gr.Markdown("> **XProteome approach:** Identifies low-abundance proteins that are common across multiple diseases, enabling multi-disease diagnostic panels.")
|
| 1420 |
|
| 1421 |
+
# === S1-F · PHYLO-RARE === (залишається без змін)
|
| 1422 |
with gr.TabItem("🧠 S1-F"):
|
| 1423 |
gr.HTML(section_header(
|
| 1424 |
"S1-F", "PHYLO-RARE", "— Where almost nobody has looked yet",
|
|
|
|
| 1426 |
"R1a DIPG 🔶 · R2a UVM 🔶 · R3a pAML 🔶"
|
| 1427 |
))
|
| 1428 |
with gr.Tabs(elem_classes="main-tabs"):
|
|
|
|
| 1429 |
with gr.TabItem("R1 · DIPG"):
|
| 1430 |
with gr.Tabs(elem_classes="sub-tabs"):
|
| 1431 |
with gr.TabItem("R1a · DIPG Toolkit"):
|
|
|
|
| 1459 |
"| Delivery | CED convection | LNP corona **in CSF** |\n"
|
| 1460 |
"| Biology | PRC2 inhibition | Ferroptosis in H3K27M+ DIPG |"
|
| 1461 |
)
|
|
|
|
| 1462 |
with gr.TabItem("R2 · UVM"):
|
| 1463 |
with gr.Tabs(elem_classes="sub-tabs"):
|
| 1464 |
with gr.TabItem("R2a · UVM Toolkit"):
|
|
|
|
| 1488 |
"| Delivery | Intravitreal injection | LNP corona **in vitreous humor** |\n"
|
| 1489 |
"| Biology | PLCβ→PKC→MAPK | GNAQ × METTL3 × YTHDF2 axis |"
|
| 1490 |
)
|
|
|
|
| 1491 |
with gr.TabItem("R3 · pAML"):
|
| 1492 |
with gr.Tabs(elem_classes="sub-tabs"):
|
| 1493 |
with gr.TabItem("R3a · pAML Toolkit"):
|
|
|
|
| 1524 |
"| Biology | Midostaurin inhibits FLT3 | Ferroptosis SL + FLT3i |"
|
| 1525 |
)
|
| 1526 |
|
| 1527 |
+
# === S1-G · 3D Lab === (залишається без змін)
|
| 1528 |
with gr.TabItem("🧊 S1-G"):
|
| 1529 |
gr.HTML(section_header(
|
| 1530 |
"S1-G", "PHYLO-SIM", "— 3D Models & Simulations",
|
|
|
|
| 1552 |
corona_plot = gr.Plot()
|
| 1553 |
corona_btn.click(plot_corona, [], corona_plot)
|
| 1554 |
|
| 1555 |
+
# === Learning === (залишається без змін)
|
| 1556 |
with gr.TabItem("📚 Learning"):
|
| 1557 |
gr.Markdown("""
|
| 1558 |
## 🧪 Guided Investigations — S1 Biomedical
|