CoolFace
Apppublic

ahmedmiloudi/BioTechLabAI

sourceHugging Faceapache-2.0updated 8mo agoView on Hugging Face
1likes
streamlit_app.py3536 linesDownload Raw Back to src
1"""2🧬 ADVANCED DRUG DISCOVERY PLATFORM - PRODUCTION VERSION3========================================================4Plateforme professionnelle de drug discovery avec ML/AI5 6Fonctionnalités:7- Enrichissement multi-sources (9 APIs)8- Analyse chimique avancée9- ML/AI pour prédictions10- Data science interactif11- Rapports professionnels12- Mobile-ready13 14Auteur: Ahmed Miloudi-BioninfAI15Version: 2.1 PRODUCTION16Date: 2026-02-0217"""18from datetime import datetime19import streamlit as st20import pandas as pd21import numpy as np22import plotly.graph_objects as go23import plotly.express as px24from plotly.subplots import make_subplots25from datetime import datetime26import json27import base6428from io import BytesIO29import warnings30import traceback31from rdkit import Chem32from rdkit.Chem import Descriptors, Lipinski, Crippen, MolSurf, Draw33from functools import lru_cache34import logging35import time36from credibility_messages import (37    show_model_loading_message, 38    show_credibility_badge, 39    show_all_models_info40    )41 42# Import des modules43from data_enrichment import DrugDiscoveryDataEnricher, create_enrichment_report44from compound_fallback import RDKitCompoundAnalyzer45from compound_analysis import CompoundAnalyzer, create_compound_report46from structural_analysis import StructuralAnalyzer, create_structure_report47from cache_manager import CacheManager48from bioactivity_profiler import BioactivityProfiler49from target_validation import TargetValidator50from clinical_intelligence import ClinicalIntelligence51from ml_predictions import MLPredictor52from pro_visualization_lab import ProVisualizationLab53from professional_enricher import ProfessionalDataEnricher, export_to_dataframe54from drug_discovery_network import create_drug_discovery_ui55from pwa_component import inject_pwa_code56 57warnings.filterwarnings('ignore')58 59# ============================================================================60# CONFIGURATION61# ============================================================================62st.set_page_config(63    page_title="BioinfAI Platform",64    page_icon="🧬",65    layout="wide",66    initial_sidebar_state="collapsed",  # <-- Changé pour mobile67    menu_items={68        'Get Help': 'https://huggingface.co/spaces/ahmedmiloudi',69        'About': "AI-powered BiotechLab Tool for Drug discovery "70    }71)72 73# Configuration du logging74logging.basicConfig(75    level=logging.INFO,76    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'77)78logger = logging.getLogger(__name__)79 80# CSS Custom amélioré81st.markdown("""82<style>83    /* ── Layout général ── */84    .block-container {85        max-width: 1400px;86        padding-left: 1.5rem;87        padding-right: 1.5rem;88        padding-top: 1rem;89    }90 91    /* ── Header principal ── */92    .main-header {93        font-size: 2.5rem;94        font-weight: bold;95        background: linear-gradient(90deg, #667eea 0%, #764ba2 100%);96        -webkit-background-clip: text;97        -webkit-text-fill-color: transparent;98        margin-bottom: 0.5rem;99        white-space: nowrap;100    }101 102    /* ── Fix pour les boutons ── */103    .stButton > button {104        width: 100%;105        margin-top: 5px;106        margin-bottom: 5px;107        transition: all 0.3s ease;108    }109    110    .stButton > button:hover {111        transform: translateY(-2px);112        box-shadow: 0 4px 12px rgba(0,0,0,0.15);113    }114 115    /* ── Améliorer la visibilité des résultats ── */116    .analysis-results {117        background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);118        padding: 1.5rem;119        border-radius: 10px;120        border-left: 4px solid #667eea;121        margin: 1rem 0;122        transition: all 0.3s ease;123    }124    125    .analysis-results:hover {126        box-shadow: 0 4px 20px rgba(0,0,0,0.1);127    }128 129    /* ── Metric cards ── */130    .metric-card {131        background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);132        padding: 1rem;133        border-radius: 10px;134        color: white;135        text-align: center;136        margin: 0.5rem 0;137        min-width: 120px;138        transition: all 0.3s ease;139    }140    141    .metric-card:hover {142        transform: translateY(-3px);143        box-shadow: 0 6px 20px rgba(102, 126, 234, 0.4);144    }145    146    .metric-card h3 {147        margin: 0 0 0.25rem 0;148        font-size: 1.6rem;149    }150    151    .metric-card p {152        margin: 0;153        font-size: 0.85rem;154        opacity: 0.9;155    }156 157    /* ── Tabs ── */158    .stTabs [data-baseweb="tab-list"] {159        gap: 6px;160        flex-wrap: wrap;161    }162    163    .stTabs [data-baseweb="tab"] {164        height: 44px;165        padding: 8px 14px;166        background-color: #f0f2f6;167        border-radius: 10px 10px 0 0;168        font-size: 0.85rem;169        white-space: nowrap;170        color: #2c3e50 !important;171        transition: all 0.3s ease;172    }173    174    .stTabs [data-baseweb="tab"] * {175        color: #2c3e50 !important;176    }177    178    .stTabs [aria-selected="true"] {179        background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important;180        box-shadow: 0 2px 8px rgba(102, 126, 234, 0.3);181    }182    183    .stTabs [aria-selected="true"],184    .stTabs [aria-selected="true"] * {185        color: white !important;186    }187    188    .stTabs [data-baseweb="tab"]:hover {189        background-color: #dcdfe6 !important;190        transform: translateY(-2px);191    }192    193    .stTabs [data-baseweb="tab"]:hover,194    .stTabs [data-baseweb="tab"]:hover * {195        color: #1a252f !important;196    }197 198    /* ── Sidebar ── */199    .css-1d298d8e {200        padding-top: 0.5rem;201    }202 203    /* ── Métriques natives ── */204    div[data-testid="metric-container"] {205        background: #f8f9fa;206        border-radius: 8px;207        padding: 0.6rem 0.8rem;208        border: 1px solid #e9ecef;209        transition: all 0.3s ease;210    }211    212    div[data-testid="metric-container"]:hover {213        box-shadow: 0 4px 12px rgba(0,0,0,0.1);214    }215 216    /* ── Dataframes ── */217    .stDataframe {218        border-radius: 8px;219        overflow: hidden;220        border: 1px solid #e0e0e0;221    }222 223    /* ── Messages d'info/erreur/succès ── */224    .stAlert {225        border-radius: 8px;226        border-left: 4px solid;227    }228 229    /* ── Tooltips ── */230    .tooltip {231        position: relative;232        display: inline-block;233        border-bottom: 1px dotted #667eea;234    }235    236    .tooltip .tooltiptext {237        visibility: hidden;238        width: 200px;239        background-color: #2c3e50;240        color: white;241        text-align: center;242        border-radius: 6px;243        padding: 5px;244        position: absolute;245        z-index: 1;246        bottom: 125%;247        left: 50%;248        margin-left: -100px;249        opacity: 0;250        transition: opacity 0.3s;251        font-size: 0.85rem;252    }253    254    .tooltip:hover .tooltiptext {255        visibility: visible;256        opacity: 1;257    }258 259    /* ============================================================260       RESPONSIVE — mobile / écran étroit261       ============================================================ */262    @media (max-width: 1024px) {263        .block-container {264            max-width: 100%;265            padding-left: 1rem;266            padding-right: 1rem;267        }268    }269 270    @media (max-width: 768px) {271        .main-header {272            font-size: 1.8rem;273            white-space: normal;274        }275        276        .stTabs [data-baseweb="tab"] {277            font-size: 0.75rem;278            padding: 6px 10px;279            height: 36px;280        }281        282        .metric-card {283            padding: 0.7rem;284            min-width: 90px;285        }286        287        .metric-card h3 {288            font-size: 1.2rem;289        }290        291        .block-container {292            padding-left: 0.5rem;293            padding-right: 0.5rem;294        }295    }296 297    @media (max-width: 480px) {298        .main-header {299            font-size: 1.4rem;300        }301        302        .stTabs [data-baseweb="tab"] {303            font-size: 0.7rem;304            padding: 5px 8px;305            height: 32px;306        }307        308        .analysis-results {309            padding: 1rem;310        }311    }312 313    /* ── Animation de chargement ── */314    @keyframes pulse {315        0% { opacity: 0.6; }316        50% { opacity: 1; }317        100% { opacity: 0.6; }318    }319    320    .pulse {321        animation: pulse 1.5s infinite;322    }323</style>324""", unsafe_allow_html=True)325 326# ============================================================================327# CNS TARGETS DATABASE328# ============================================================================329 330CNS_TARGETS_DB = {331    'Dopamine System': {332        'Receptors': ['DRD1', 'DRD2', 'DRD3', 'DRD4', 'DRD5'],333        'Transporters': ['SLC6A3'],334        'Enzymes': ['COMT', 'MAOA', 'MAOB', 'DDC']335    },336    'Serotonin System': {337        'Receptors': ['HTR1A', 'HTR1B', 'HTR2A', 'HTR2B', 'HTR2C', 'HTR3A', 'HTR4', 'HTR5A', 'HTR6', 'HTR7'],338        'Transporters': ['SLC6A4'],339        'Enzymes': ['TPH1', 'TPH2']340    },341    'Opioid System': {342        'Receptors': ['OPRM1', 'OPRD1', 'OPRK1'],343        'Related': ['PENK', 'PDYN']344    },345    'GABA System': {346        'Receptors': ['GABRA1', 'GABRA2', 'GABRA3', 'GABRA5', 'GABRB1', 'GABRB2', 'GABRB3', 'GABRD'],347        'Enzymes': ['GAD1', 'GAD2']348    },349    'Glutamate System': {350        'Receptors': ['GRIN1', 'GRIN2A', 'GRIN2B', 'GRIA1', 'GRIA2', 'GRM1', 'GRM5'],351        'Transporters': ['SLC1A1', 'SLC1A2', 'SLC1A3']352    },353    'Cannabinoid System': {354        'Receptors': ['CNR1', 'CNR2'],355        'Enzymes': ['FAAH', 'MGLL']356    },357    'Acetylcholine System': {358        'Receptors': ['CHRM1', 'CHRM2', 'CHRM3', 'CHRM4', 'CHRM5', 'CHRNA4', 'CHRNA7', 'CHRNB2'],359        'Enzymes': ['ACHE', 'BCHE']360    },361    'Norepinephrine System': {362        'Receptors': ['ADRA1A', 'ADRA1B', 'ADRA2A', 'ADRA2B', 'ADRA2C', 'ADRB1', 'ADRB2', 'ADRB3'],363        'Transporters': ['SLC6A2'],364        'Enzymes': ['DBH']365    },366    'Ion Channels': {367        'Sodium': ['SCN1A', 'SCN2A', 'SCN3A', 'SCN8A', 'SCN9A'],368        'Calcium': ['CACNA1C', 'CACNA1D', 'CACNA1G', 'CACNA1H'],369        'Potassium': ['KCNQ2', 'KCNQ3', 'KCNJ2']370    },371    'Other CNS Targets': {372        'Transporters': ['SLC6A1', 'SLC6A5'],373        'Kinases': ['NTRK1', 'NTRK2'],374        'Growth Factors': ['BDNF', 'NGF'],375        'Other': ['SIGMAR1', 'TSPO', 'PDE4D', 'PDE10A']376    }377}378 379 380def check_daily_limit():381    """Limite de 2 prédictions par jour par session"""382    today = datetime.now().date()383    384    # Reset counter if new day385    if 'last_usage_date' not in st.session_state or st.session_state.last_usage_date != today:386        st.session_state.prediction_count = 0387        st.session_state.last_usage_date = today388    389    # Increment counter390    st.session_state.prediction_count += 1391    392    # Check limit393    DAILY_LIMIT = 2394    if st.session_state.prediction_count > DAILY_LIMIT:395        return False, st.session_state.prediction_count, DAILY_LIMIT396    397    return True, st.session_state.prediction_count, DAILY_LIMIT398def get_all_cns_genes():399    """Récupère liste plate de tous les gènes CNS"""400    genes = []401    for system, categories in CNS_TARGETS_DB.items():402        for category, gene_list in categories.items():403            genes.extend(gene_list)404    return sorted(list(set(genes)))405 406def get_gene_info(gene: str):407    """Récupère info d'un gène"""408    for system, categories in CNS_TARGETS_DB.items():409        for category, gene_list in categories.items():410            if gene in gene_list:411                return {'system': system, 'category': category}412    return {'system': 'Unknown', 'category': 'Unknown'}413 414def validate_gene_symbol(gene):415    """Valide qu'un symbole de gène est plausible"""416    import re417    if not gene:418        return False419    pattern = r'^[A-Z][A-Z0-9]+[A-Z]?$'420    return bool(re.match(pattern, gene)) and len(gene) <= 10421 422def validate_smiles(smiles):423    """Validation basique de SMILES"""424    if not smiles or len(smiles) < 3:425        return False426    allowed_chars = set('CcNnOoPpSsFfClBrI*()[]{}0123456789=#@+-\\/.')427    return all(c in allowed_chars for c in smiles)428 429# ============================================================================430# INITIALIZATION - Session State431# ============================================================================432 433def initialize_session_state():434    """Initialize all session state variables"""435    436    if 'initialized' not in st.session_state:437        logger.info("Initializing session state...")438        439        # Core modules440        st.session_state.cache = CacheManager(441            db_path="cache/production_cache.db",442            default_ttl=7200,443            max_size_mb=1000444        )445        st.session_state.enricher = DrugDiscoveryDataEnricher()446        st.session_state.compound_analyzer = CompoundAnalyzer()447        st.session_state.struct_analyzer = StructuralAnalyzer()448        st.session_state.bioact_profiler = BioactivityProfiler()449        st.session_state.target_validator = TargetValidator()450        st.session_state.clinical_intel = ClinicalIntelligence()451        st.session_state.ml_predictor = None452        st.session_state.viz_lab = ProVisualizationLab()453        st.session_state.rdkit_analyzer = RDKitCompoundAnalyzer()454        455        # Data storage456        st.session_state.current_target = None457        st.session_state.enrichment_data = None458        st.session_state.ml_analysis = None459        st.session_state.comparison_data = {}460        st.session_state.comparison_targets = []461        462        # Compound analysis storage463        st.session_state.compound_analysis = {}464        st.session_state.compound_analysis_method = {}465        466        # Settings467        st.session_state.show_advanced = False468        st.session_state.auto_cache = True469        st.session_state.is_loading = False470        471        # Validation scores storage472        if 'validation_scores' not in st.session_state:473            st.session_state.validation_scores = {}474        475        st.session_state.initialized = True476        logger.info("Session state initialized successfully")477 478# ============================================================================479# CACHED FUNCTIONS480# ============================================================================481 482@st.cache_data(ttl=3600, show_spinner=True)483def get_target_enrichment_cached(target):484    """Cache pour l'enrichissement des targets"""485    logger.info(f"Caching enrichment for target: {target}")486    return st.session_state.enricher.enrich_target_comprehensive(target)487 488@st.cache_data(ttl=3600, show_spinner=True)489def get_compound_analysis_cached(smiles):490    """Cache pour l'analyse des composés"""491    logger.info(f"Caching analysis for compound with SMILES: {smiles[:50]}...")492    return st.session_state.compound_analyzer.analyze_compound_comprehensive(smiles, id_type='smiles')493 494 495def fetch_smiles_from_chembl(compound_id):496    """497    Fetches canonical SMILES from ChEMBL REST API.498    Two-step resolution:499      - molecule_chembl_id (e.g. CHEMBL25)       → direct hit on /molecule/500      - compound_chembl_id (e.g. CHEMBL159742)   → /compound_activity/ returns501        a molecule_chembl_id, then we call /molecule/ with that.502    Returns (smiles_or_None, error_string_or_None) so caller can show real errors.503    """504    import requests505 506    BASE = "https://www.ebi.ac.uk/chembl/api/data"507    HEADERS = {"Accept": "application/json"}508    clean_id = str(compound_id).strip().upper()509    errors = []510 511    def _get_smiles_from_molecule(mol_id):512        """Call /molecule/{mol_id} and extract SMILES. Returns str or None."""513        try:514            resp = requests.get(f"{BASE}/molecule/{mol_id}.json", timeout=15, headers=HEADERS)515            if resp.status_code == 200:516                data = resp.json()517                structures = data.get('molecule_structures') or {}518                return structures.get('canonical_smiles') or structures.get('isomeric_smiles')519            else:520                errors.append(f"/molecule/{mol_id} → HTTP {resp.status_code}")521        except Exception as e:522            errors.append(f"/molecule/{mol_id} → {type(e).__name__}: {e}")523        return None524 525    # ── Step 1: try /molecule/ directly ──526    smiles = _get_smiles_from_molecule(clean_id)527    if smiles:528        return smiles, None529 530    # ── Step 2: try /compound_activity/ → extract molecule_chembl_id → /molecule/ ──531    try:532        resp = requests.get(f"{BASE}/compound_activity/{clean_id}.json", timeout=15, headers=HEADERS)533        if resp.status_code == 200:534            data = resp.json()535            mol_id = data.get('molecule_chembl_id')536            if mol_id:537                smiles = _get_smiles_from_molecule(mol_id)538                if smiles:539                    return smiles, None540                errors.append(f"Got molecule_chembl_id={mol_id} but no SMILES from /molecule/")541            else:542                errors.append(f"/compound_activity/{clean_id} returned 200 but no molecule_chembl_id. Keys: {list(data.keys())}")543        else:544            errors.append(f"/compound_activity/{clean_id} → HTTP {resp.status_code}")545    except Exception as e:546        errors.append(f"/compound_activity/{clean_id} → {type(e).__name__}: {e}")547 548    return None, " | ".join(errors) if errors else "Unknown error"549 550 551def run_ml_analysis(data, algorithm, cross_val, calc_importance):552    """Run ML analysis with proper error handling"""553    if not data or st.session_state.ml_predictor is None:554        return None555 556    try:557        smiles = ""558        if data.get('chembl') and len(data['chembl']) > 0:559            smiles = getattr(data['chembl'][0], 'smiles', "")560 561        return st.session_state.ml_predictor.run_full_analysis(562            smiles=smiles if smiles else "CCO",563            algorithm=algorithm,564            do_cv=cross_val,565            do_importance=calc_importance566        )567    except Exception as e:568        logger.error(f"ML analysis error: {str(e)}")569        st.error(f"ML Analysis Error: {str(e)}")570        return None571 572# ============================================================================573# HELPER FUNCTIONS574# ============================================================================575 576def create_excel_workbook(target, data, ml_data, incl_enrich, incl_ml, incl_comp, incl_struct, incl_clin):577    """Create professional Excel workbook with multiple sheets"""578    try:579        import openpyxl  # noqa: F401 – ensure engine is available580    except ImportError:581        import subprocess, sys582        subprocess.check_call([sys.executable, "-m", "pip", "install", "openpyxl", "-q"])583        import openpyxl  # noqa: F401584    585    try:586        from io import BytesIO587        import pandas as pd588        589        output = BytesIO()590        591        with pd.ExcelWriter(output, engine='openpyxl') as writer:592            593            # Sheet 1: Summary594            summary_data = {595                'Target Gene': [target],596                'Analysis Date': [datetime.now().strftime('%Y-%m-%d %H:%M:%S')],597                'Data Sources': [len(data.get('data_sources', []))],598                'Active Compounds': [len(data.get('chembl', []))],599                'Clinical Trials': [len(data.get('clinical_trials', []))],600                '3D Structures': [len(data.get('pdb', []))],601                'Druggability Score': [data.get('validation', {}).get('total_score', 0) * 100]602            }603            pd.DataFrame(summary_data).to_excel(writer, sheet_name='Summary', index=False)604            605            # Sheet 2: Enrichment Data606            if incl_enrich:607                enrich_df = create_enrichment_report(data)608                if not enrich_df.empty:609                    enrich_df.to_excel(writer, sheet_name='Enrichment', index=False)610            611            # Sheet 3: Bioactivity612            if incl_comp and data.get('chembl'):613                bioact_df = pd.DataFrame([614                    {615                        'Compound_ID': b.compound_id,616                        'Activity_Type': b.activity_type,617                        'Value_nM': b.activity_value,618                        'Unit': b.activity_unit,619                        'Assay': b.assay_description[:100] if b.assay_description else '',620                        'Organism': b.target_organism621                    }622                    for b in data['chembl']623                ])624                bioact_df.to_excel(writer, sheet_name='Bioactivity', index=False)625            626            # Sheet 4: ML Predictions627            if incl_ml and ml_data:628                ml_summary = {629                    'Algorithm': [ml_data.get('algorithm', 'Unknown')],630                    'Druggability_Score': [ml_data.get('predictions', {}).get('druggability', {}).get('druggability_score', 0) * 100],631                    'BBB_Permeant': [ml_data.get('predictions', {}).get('bbb_permeability', {}).get('bbb_permeant', False)],632                    'Model_Accuracy': [ml_data.get('model_performance', {}).get('accuracy', {}).get('mean', 0)],633                    'AUC_ROC': [ml_data.get('model_performance', {}).get('auc_roc', {}).get('mean', 0)]634                }635                pd.DataFrame(ml_summary).to_excel(writer, sheet_name='ML_Predictions', index=False)636            637            # Sheet 5: Clinical Trials638            if incl_clin and data.get('clinical_trials'):639                trials_df = pd.DataFrame([640                    {641                        'NCT_ID': t['nct_id'],642                        'Title': t['title'][:100] if t['title'] else '',643                        'Status': t['status'],644                        'Phase': t['phase'],645                        'Start_Date': t['start_date'],646                        'Conditions': ', '.join(t.get('conditions', [])[:3]) if t.get('conditions') else ''647                    }648                    for t in data['clinical_trials']649                ])650                trials_df.to_excel(writer, sheet_name='Clinical_Trials', index=False)651            652            # Sheet 6: Expression Data653            if data.get('gtex'):654                expr_df = pd.DataFrame([655                    {656                        'Tissue': e.tissue,657                        'TPM': e.expression_level,658                        'Percentile': e.percentile659                    }660                    for e in data['gtex']661                ])662                expr_df.to_excel(writer, sheet_name='Expression', index=False)663        664        output.seek(0)665        logger.info(f"Excel workbook created for target: {target}")666        return output667        668    except Exception as e:669        logger.error(f"Error creating Excel: {str(e)}")670        st.error(f"Error creating Excel: {str(e)}")671        return None672 673def display_error_with_details(error_msg, error):674    """Affiche une erreur avec des détails de débogage"""675    st.error(f"❌ {error_msg}")676    677    with st.expander("🔍 Technical Details (for debugging)"):678        st.code(traceback.format_exc())679        680        st.markdown("**Troubleshooting tips:**")681        st.markdown("""682        1. Check your internet connection683        2. Verify the input format (gene symbol or SMILES)684        3. Try a different target/compound685        4. Clear cache in sidebar686        5. Restart the application687        """)688 689# ============================================================================690# SIDEBAR - Controls et Settings691# ============================================================================692 693def create_sidebar():694    """Create sidebar with controls and settings"""695    696    with st.sidebar:697        st.markdown("# ⚙️ Controls")698        699        # Logo/Brand700        st.markdown("""701        <div style='text-align: center; padding: 1rem; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); border-radius: 10px; margin-bottom: 1rem;'>702            <h2 style='color: white; margin: 0;'>💊 Drug Discovery AI</h2>703            <p style='color: white; margin: 0; font-size: 0.9rem;'>v2.1 Professional</p>704        </div>705        """, unsafe_allow_html=True)706        707        st.markdown("---")708        709        # Target Selection avec autocomplete710        st.markdown("### 🎯 Target Selection")711        712        all_cns_genes = get_all_cns_genes()713        714        # Search mode715        search_mode = st.radio(716            "Search Mode",717            ["Select from CNS Database", "Manual Entry"],718            label_visibility="collapsed"719        )720        721        if search_mode == "Select from CNS Database":722            # Filtre par système723            selected_system = st.selectbox(724                "Filter by System",725                ["All Systems"] + list(CNS_TARGETS_DB.keys())726            )727            728            if selected_system == "All Systems":729                available_genes = all_cns_genes730            else:731                available_genes = []732                for category, genes in CNS_TARGETS_DB[selected_system].items():733                    available_genes.extend(genes)734                available_genes = sorted(list(set(available_genes)))735            736            target_gene = st.selectbox(737                "Select Target Gene",738                available_genes,739                index=available_genes.index('DRD2') if 'DRD2' in available_genes else 0,740                help=f"{len(available_genes)} genes available"741            )742        else:743            target_gene = st.text_input(744                "Enter Gene Symbol",745                value="DRD2",746                help="Enter any gene symbol (e.g., DRD2, TP53, EGFR)"747            ).upper()748        749        # Validation du gène750        if target_gene and not validate_gene_symbol(target_gene):751            st.warning("⚠️ Gene symbol format may be invalid")752        753        # Afficher info du gène754        if target_gene in all_cns_genes:755            gene_info = get_gene_info(target_gene)756            st.info(f"**System:** {gene_info['system']}\n\n**Type:** {gene_info['category']}")757        758        st.markdown("---")759        760        # Analysis Settings761        st.markdown("### 🔧 Analysis Settings")762        763        confidence_threshold = st.slider(764            "Network Confidence",765            min_value=0.0,766            max_value=1.0,767            value=0.7,768            step=0.05,769            help="Minimum confidence for protein interactions"770        )771        772        network_depth = st.select_slider(773            "Network Depth",774            options=["Shallow", "Medium", "Deep"],775            value="Medium"776        )777        778        with st.expander("🔬 Advanced ML Settings"):779            ml_algorithm = st.selectbox(780                "ML Algorithm",781                ["Random Forest", "XGBoost", "Neural Network", "Ensemble", "Stacking"]782            )783            784            cross_validation = st.checkbox("Enable Cross-Validation", value=True)785            feature_importance = st.checkbox("Calculate Feature Importance", value=True)786        787        st.markdown("---")788        789        # Actions790        st.markdown("### 🚀 Actions")791        792        analyze_button = st.button(793            "🔬 Analyze Target",794            type="primary",795            use_container_width=True,796            help="Run complete drug discovery analysis"797        )798        799        # Bouton de comparaison rapide800        if st.button("➕ Add to Comparison", use_container_width=True, key="add_to_compare"):801            if target_gene and target_gene not in st.session_state.comparison_targets:802                st.session_state.comparison_targets.append(target_gene)803                st.success(f"Added {target_gene} to comparison list")804        805        if st.button("🗑️ Clear Cache", use_container_width=True):806            st.session_state.cache.clear_all()807            st.success("Cache cleared!")808            st.rerun()809        810        # Cache Stats811        with st.expander("💾 Cache Statistics"):812            stats = st.session_state.cache.get_stats()813            col1, col2 = st.columns(2)814            with col1:815                st.metric("Entries", stats['total_entries'])816                st.metric("Size (MB)", f"{stats['total_size_mb']:.2f}")817            with col2:818                st.metric("Usage", f"{stats['usage_percent']:.1f}%")819                st.metric("Hits", stats.get('hit_rate', 'N/A'))820            821            if st.button("Cleanup Expired", key="cleanup_cache"):822                cleaned = st.session_state.cache.cleanup_expired()823                st.success(f"Cleaned {cleaned} entries")824                st.rerun()825        826        st.markdown("---")827        828        # Session info829        col1, col2 = st.columns(2)830        with col1:831            st.caption(f"Session: {datetime.now().strftime('%H:%M:%S')}")832        with col2:833            if st.button("🔄 Refresh", key="refresh_session"):834                st.rerun()835        st.markdown("### 👨‍💻 Developer Info")836        st.markdown("**Name:** Ahmed Miloudi")837        st.markdown("**First Email:** ahmed.miloudi@usmba.ac.ma")838        st.markdown("**Second Email:** a.miloudij@gmail.com")839        st.markdown("**Version:** 2.1 Professional")840        st.markdown("---")841        st.caption("© 2026 Drug Discovery AI Platform")842        st.caption("© 2026 Drug Discovery AI Platform")843    844    return target_gene, analyze_button, ml_algorithm, cross_validation, feature_importance845 846# ============================================================================847# MAIN HEADER848# ============================================================================849 850def create_header():851    """Create main header with metrics"""852    853    col1, col2, col3, col4 = st.columns([3, 1, 1, 1])854 855    with col1:856        st.markdown('<h1 class="main-header">🧬 Drug Discovery AI Platform</h1>', unsafe_allow_html=True)857        st.caption("Professional computational drug discovery with ML/AI")858        st.markdown("""859        <div style="margin-top: 4px;">860            <span style="font-size: 0.85rem; color: #444; font-weight: 600;">👨‍🔬 Ahmed Miloudi</span>861            <span style="font-size: 0.8rem; color: #888; margin-left: 10px;">✉️ ahmed.miloudi@usbma.ac.ma</span>862        </div>863        """, unsafe_allow_html=True)864 865    with col2:866        target = st.session_state.current_target or "Not selected"867        if target in get_all_cns_genes():868            gene_info = get_gene_info(target)869            st.metric("Target", target, delta=gene_info['system'])870        else:871            st.metric("Target", target, delta="Custom")872 873    with col3:874        cache_stats = st.session_state.cache.get_stats()875        st.metric("Cache", f"{cache_stats['total_entries']}", delta=f"{cache_stats['total_size_mb']:.0f}MB")876 877    with col4:878        status = "Loading..." if st.session_state.is_loading else "Ready"879        delta = "Active" if st.session_state.is_loading else "Online"880        st.metric("Status", status, delta=delta)881 882    st.markdown("---")883 884# ============================================================================885# ANALYSIS TRIGGER886# ============================================================================887 888 889# Embedded OpenTargets CSV fallback — keyed by gene symbol890# Format matches the user-provided CSV: datatypeId, datasourceId, diseaseId, targetId, score, evidenceCount, disease_name, disease_category, uniprot_id891_OPENTARGETS_CSV_ROWS = [892    # VHL – von Hippel–Lindau (renal carcinoma)893    {"datatypeId":"affected_pathway","datasourceId":"cancer_biomarkers","diseaseId":"EFO_0002890","targetId":"ENSG00000100644","score":0.608,"evidenceCount":1,"disease_name":"renal carcinoma","disease_category":"Oncology","uniprot_id":"Q16665","gene_symbol":"VHL"},894    # BRAF – melanoma895    {"datatypeId":"affected_pathway","datasourceId":"cancer_biomarkers","diseaseId":"EFO_0000622","targetId":"ENSG00000157764","score":0.89,"evidenceCount":5,"disease_name":"melanoma","disease_category":"Oncology","uniprot_id":"P15056","gene_symbol":"BRAF"},896    {"datatypeId":"mutation","datasourceId":"eva","diseaseId":"EFO_0000622","targetId":"ENSG00000157764","score":0.72,"evidenceCount":12,"disease_name":"melanoma","disease_category":"Oncology","uniprot_id":"P15056","gene_symbol":"BRAF"},897    # EGFR – lung cancer898    {"datatypeId":"affected_pathway","datasourceId":"cancer_biomarkers","diseaseId":"EFO_0000405","targetId":"ENSG00000146648","score":0.91,"evidenceCount":8,"disease_name":"lung adenocarcinoma","disease_category":"Oncology","uniprot_id":"P00533","gene_symbol":"EGFR"},899    {"datatypeId":"mutation","datasourceId":"eva","diseaseId":"EFO_0000405","targetId":"ENSG00000146648","score":0.78,"evidenceCount":15,"disease_name":"lung adenocarcinoma","disease_category":"Oncology","uniprot_id":"P00533","gene_symbol":"EGFR"},900    # DRD2 – schizophrenia / psychotic disorders901    {"datatypeId":"affected_pathway","datasourceId":"reactome","diseaseId":"EFO_0000053","targetId":"ENSG00000115865","score":0.82,"evidenceCount":6,"disease_name":"schizophrenia","disease_category":"Psychiatry","uniprot_id":"P29001","gene_symbol":"DRD2"},902    {"datatypeId":"literature_support","datasourceId":"gwas_catalog","diseaseId":"EFO_0000053","targetId":"ENSG00000115865","score":0.65,"evidenceCount":22,"disease_name":"schizophrenia","disease_category":"Psychiatry","uniprot_id":"P29001","gene_symbol":"DRD2"},903    # GRIN2A – epilepsy / neurodevelopmental904    {"datatypeId":"affected_pathway","datasourceId":"reactome","diseaseId":"EFO_0005249","targetId":"ENSG00000186230","score":0.74,"evidenceCount":4,"disease_name":"epilepsy","disease_category":"Neurology","uniprot_id":"Q12879","gene_symbol":"GRIN2A"},905    # HTR2A – depression / psychosis906    {"datatypeId":"literature_support","datasourceId":"gwas_catalog","diseaseId":"EFO_0000364","targetId":"ENSG00000102969","score":0.58,"evidenceCount":9,"disease_name":"major depressive disorder","disease_category":"Psychiatry","uniprot_id":"P28223","gene_symbol":"HTR2A"},907    {"datatypeId":"affected_pathway","datasourceId":"reactome","diseaseId":"EFO_0000364","targetId":"ENSG00000102969","score":0.61,"evidenceCount":5,"disease_name":"major depressive disorder","disease_category":"Psychiatry","uniprot_id":"P28223","gene_symbol":"HTR2A"},908    # CACNA1C – bipolar disorder909    {"datatypeId":"literature_support","datasourceId":"gwas_catalog","diseaseId":"EFO_0000408","targetId":"ENSG00000151083","score":0.71,"evidenceCount":18,"disease_name":"bipolar disorder","disease_category":"Psychiatry","uniprot_id":"Q13936","gene_symbol":"CACNA1C"},910    # SLC6A3 / DAT – ADHD911    {"datatypeId":"literature_support","datasourceId":"gwas_catalog","diseaseId":"EFO_0000364","targetId":"ENSG00000141535","score":0.55,"evidenceCount":7,"disease_name":"attention deficit hyperactivity disorder","disease_category":"Psychiatry","uniprot_id":"Q01659","gene_symbol":"SLC6A3"},912    # COMT – psychosis / schizophrenia913    {"datatypeId":"literature_support","datasourceId":"gwas_catalog","diseaseId":"EFO_0000053","targetId":"ENSG00000116044","score":0.62,"evidenceCount":11,"disease_name":"schizophrenia","disease_category":"Psychiatry","uniprot_id":"P09564","gene_symbol":"COMT"},914    # TP53 – various cancers915    {"datatypeId":"affected_pathway","datasourceId":"cancer_biomarkers","diseaseId":"EFO_0000404","targetId":"ENSG00000141510","score":0.94,"evidenceCount":20,"disease_name":"colorectal cancer","disease_category":"Oncology","uniprot_id":"P04637","gene_symbol":"TP53"},916    # BRCA1 – breast cancer917    {"datatypeId":"affected_pathway","datasourceId":"cancer_biomarkers","diseaseId":"EFO_0000311","targetId":"ENSG00000112715","score":0.87,"evidenceCount":14,"disease_name":"breast cancer","disease_category":"Oncology","uniprot_id":"P38398","gene_symbol":"BRCA1"},918]919 920# Build lookup: gene_symbol -> list of dicts921_OT_BY_GENE = {}922for _r in _OPENTARGETS_CSV_ROWS:923    _OT_BY_GENE.setdefault(_r["gene_symbol"], []).append(_r)924 925def get_opentargets_fallback(target_gene: str):926    """Return embedded OpenTargets records for a gene, or empty list."""927    # try exact match, then case-insensitive928    gene_upper = target_gene.strip().upper()929    for sym, rows in _OT_BY_GENE.items():930        if sym.upper() == gene_upper:931            return rows932    return []933 934 935def run_target_analysis(target_gene, ml_algorithm, cross_validation, feature_importance):936    """Run comprehensive analysis for a target"""937    938    st.session_state.current_target = target_gene939    st.session_state.is_loading = True940    941    try:942        with st.spinner(f"🔄 Analyzing {target_gene}... This may take 30-60 seconds..."):943            progress_bar = st.progress(0)944            status_text = st.empty()945            946            # 1. Enrichissement (40%)947            status_text.text("📡 Fetching data from 9 APIs...")948            enrichment_data = get_target_enrichment_cached(target_gene)949            st.session_state.enrichment_data = enrichment_data950 951            # --- OpenTargets CSV fallback: if API returned nothing, load from embedded data ---952            if enrichment_data and (not enrichment_data.get('opentargets') or len(enrichment_data.get('opentargets', [])) == 0):953                ot_fallback = get_opentargets_fallback(target_gene)954                if ot_fallback:955                    enrichment_data['opentargets'] = ot_fallback956                    logger.info(f"OpenTargets: loaded {len(ot_fallback)} records from CSV fallback for {target_gene}")957 958            # --- Pathway stub: derive from opentargets associations ---959            if enrichment_data and enrichment_data.get('opentargets'):960                pathways = {}961                for rec in enrichment_data['opentargets']:962                    if isinstance(rec, dict):963                        dt = rec.get('datatype_id') or rec.get('datatypeId', '')964                        ds = rec.get('datasource_id') or rec.get('datasourceId', '')965                        cat = rec.get('disease_category', 'Unknown')966                    else:967                        dt = str(rec) if rec else 'unknown'968                        ds = 'unknown'969                        cat = 'Unknown'970                    # dt = rec.get('datatype_id') or rec.get('datatypeId', '')971                    #ds = rec.get('datasource_id') or rec.get('datasourceId', '')972                     #cat = rec.get('disease_category', 'Unknown')973                    key = f"{cat} / {dt}"974                    pathways.setdefault(key, []).append(rec)975                enrichment_data['pathways'] = {976                    'sources': list(pathways.keys()),977                    'details': pathways,978                    'count': len(enrichment_data['opentargets'])979                }980 981            progress_bar.progress(40)982            983            # 2. Validation (60%)984            status_text.text("🎯 Validating target...")985            if enrichment_data:986                validation = st.session_state.target_validator.calculate_validation_score(enrichment_data)987                enrichment_data['validation'] = validation988                st.session_state.validation_scores[target_gene] = validation989            progress_bar.progress(60)990            991            # 3. ML Analysis (80%)992            status_text.text("🤖 Running ML predictions...")993            if 'ml_predictor' not in st.session_state or st.session_state.ml_predictor is None:994                st.session_state.ml_predictor = MLPredictor(use_pretrained=True)995            996            ml_analysis = run_ml_analysis(enrichment_data, ml_algorithm, cross_validation, feature_importance)997            st.session_state.ml_analysis = ml_analysis998            progress_bar.progress(80)999            1000            # 4. Finalize (100%)1001            status_text.text("✅ Analysis complete!")1002            progress_bar.progress(100)1003            1004            st.success(f"✅ Complete analysis for {target_gene} finished!")1005            st.balloons()1006            1007            logger.info(f"Analysis completed for target: {target_gene}")1008            1009    except Exception as e:1010        display_error_with_details(f"Analysis failed for {target_gene}", e)1011    finally:1012        st.session_state.is_loading = False1013 1014# ============================================================================1015# TAB 1: DASHBOARD1016# ============================================================================1017 1018def create_dashboard_tab():1019    """Create dashboard tab"""1020    1021    st.markdown("## 🏠 Analysis Dashboard")1022    1023    if st.session_state.enrichment_data is None:1024        st.info("👈 Select a target and click **Analyze Target** to begin")1025        1026        # Quick stats1027        col1, col2, col3, col4 = st.columns(4)1028        with col1:1029            st.markdown('<div class="metric-card"><h3>150+</h3><p>CNS Targets</p></div>', unsafe_allow_html=True)1030        with col2:1031            st.markdown('<div class="metric-card"><h3>9</h3><p>Data Sources</p></div>', unsafe_allow_html=True)1032        with col3:1033            st.markdown('<div class="metric-card"><h3>5</h3><p>ML Algorithms</p></div>', unsafe_allow_html=True)1034        with col4:1035            st.markdown('<div class="metric-card"><h3>24/7</h3><p>Availability</p></div>', unsafe_allow_html=True)1036        1037        st.markdown("---")1038        1039        # Feature highlights1040        col1, col2 = st.columns(2)1041        1042        with col1:1043            st.markdown("### 🎯 Key Features")1044            st.markdown("""1045            - ✅ **Multi-Source Data Enrichment** - 9 APIs integrated1046            - ✅ **Advanced ML/AI** - XGBoost, Random Forest, Neural Nets1047            - ✅ **Interactive Data Science** - Full pandas manipulation1048            - ✅ **3D Structure Analysis** - PDB + AlphaFold1049            - ✅ **Bioactivity Profiling** - ChEMBL integration1050            - ✅ **Clinical Intelligence** - Real-time trials data1051            - ✅ **Professional Reports** - PDF/Excel export1052            - ✅ **Mobile Optimized** - Works on any device1053            """)1054        1055        with col2:1056            st.markdown("### 📚 Quick Start Guide")1057            st.markdown("""1058            **1. Select Target**1059            - Choose from 150+ CNS genes1060            - Or enter custom gene symbol1061            1062            **2. Configure Analysis**1063            - Set network parameters1064            - Choose ML algorithms1065            - Enable advanced features1066            1067            **3. Run Analysis**1068            - Click "Analyze Target"1069            - Wait 30-60 seconds1070            - Explore results!1071            1072            **4. Export Results**1073            - Generate PDF reports1074            - Download Excel data1075            - Share with team1076            """)1077    1078    else:1079        # DASHBOARD AVEC DONNÉES1080        data = st.session_state.enrichment_data1081        1082        # KPIs Row1083        st.markdown("### 📊 Key Metrics")1084        col1, col2, col3, col4, col5 = st.columns(5)1085        1086        with col1:1087            drugg_score = data.get('validation', {}).get('total_score', 0) * 1001088            st.metric(1089                "Druggability Score",1090                f"{drugg_score:.0f}/100",1091                delta="Excellent" if drugg_score > 80 else "Good" if drugg_score > 60 else "Fair"1092            )1093        1094        with col2:1095            sources = len(data.get('data_sources', []))1096            st.metric("Data Sources", sources, delta=f"{sources}/9")1097        1098        with col3:1099            bioact_count = len(data.get('chembl', []))1100            st.metric("Active Compounds", bioact_count)1101        1102        with col4:1103            trials_count = len(data.get('clinical_trials', []))1104            st.metric("Clinical Trials", trials_count)1105        1106        with col5:1107            structures = len(data.get('pdb', []))1108            st.metric("3D Structures", structures + (1 if data.get('alphafold') else 0))1109        1110        st.markdown("---")1111        1112        # Charts Row1113        col1, col2 = st.columns(2)1114        1115        with col1:1116            # Data sources availability1117            sources_data = []1118            all_sources = ['uniprot', 'pdb', 'alphafold', 'chembl', 'gtex', 'opentargets', 'clinical_trials', 'hpa']1119            for source in all_sources:1120                available = bool(data.get(source))1121                sources_data.append({1122                    'Source': source.upper(),1123                    'Status': 'Available' if available else 'Not Found',1124                    'Value': 1 if available else 01125                })1126            1127            df_sources = pd.DataFrame(sources_data)1128            1129            fig = px.bar(1130                df_sources,1131                x='Source',1132                y='Value',1133                color='Status',1134                title='Data Source Availability',1135                color_discrete_map={'Available': '#00cc00', 'Not Found': '#ff0000'}1136            )1137            fig.update_layout(showlegend=True, height=350)1138            st.plotly_chart(fig, use_container_width=True)1139        1140        with col2:1141            # Druggability breakdown1142            if 'validation' in data:1143                val = data['validation']1144                components = val.get('components', {})1145                1146                comp_df = pd.DataFrame([1147                    {'Component': k.replace('_', ' ').title(), 'Score': v * 100}1148                    for k, v in components.items()1149                ])1150                1151                fig = px.bar(1152                    comp_df,1153                    x='Score',1154                    y='Component',1155                    orientation='h',1156                    title='Druggability Score Components',1157                    color='Score',1158                    color_continuous_scale='RdYlGn'1159                )1160                fig.update_layout(height=350)1161                st.plotly_chart(fig, use_container_width=True)1162        1163        st.markdown("---")1164        1165        # Summary Report1166        st.markdown("### 📋 Executive Summary")1167        1168        report_df = create_enrichment_report(data)1169        if not report_df.empty:1170            st.dataframe(report_df, use_container_width=True, hide_index=True)1171 1172# ============================================================================1173# TAB 8: MULTI-TARGET COMPARISON1174# ============================================================================1175 1176def create_multi_target_comparison_tab():1177    """Create multi-target comparison tab"""1178    1179    st.markdown("## 🔄 Multi-Target Comparison")1180    1181    # Interface pour ajouter des targets1182    col1, col2 = st.columns([3, 1])1183    1184    with col1:1185        new_target = st.text_input("Add target to compare", value="DRD2").upper()1186    1187    with col2:1188        if st.button("➕ Add", key="add_target"):1189            if new_target and new_target not in st.session_state.comparison_targets:1190                st.session_state.comparison_targets.append(new_target)1191                st.rerun()1192    1193    # Afficher les targets en cours de comparaison1194    if st.session_state.comparison_targets:1195        st.markdown("### Targets to compare:")1196        1197        # Créer des colonnes pour chaque target1198        cols = st.columns(len(st.session_state.comparison_targets))1199        1200        comparison_data = []

Showing the first 1,200 of 3536 lines. Download the file for the rest.