import re import streamlit as st from functions_ui import search_cas_inci # Configure page st.set_page_config( page_title="LMB App", page_icon="🔬", layout="wide" ) # Password protection def check_password(): """Returns `True` if the user had the correct password.""" def password_entered(): """Checks whether a password entered by the user is correct.""" if st.session_state["password"] == st.secrets["passwords"]["app_password"]: st.session_state["password_correct"] = True del st.session_state["password"] # Don't store password else: st.session_state["password_correct"] = False # First run, show input for password if "password_correct" not in st.session_state: st.text_input( "Password", type="password", on_change=password_entered, key="password" ) return False # Password not correct, show input + error elif not st.session_state["password_correct"]: st.text_input( "Password", type="password", on_change=password_entered, key="password" ) st.error("😕 Password incorrect") return False # Password correct else: return True if not check_password(): st.stop() # Define home page function def home(): st.title("LMB App: PIF & Database Tossicologico") # Inizializza session_state per il CAS number se non esiste if 'selected_cas' not in st.session_state: st.session_state.selected_cas = None # choose between cas or inci type = st.radio("Cerca per:", ("CAS", "INCI"), index=0, key="search_mode") input = st.text_input("Inserisci:", "") if input: st.caption(f"Ricerca per {input}: trovati i seguenti ingredienti.") if type == "CAS": results = search_cas_inci(input, type='cas') else: results = search_cas_inci(input, type='inci') if results: # Crea le stringhe per la selectbox: "CAS - INCI" display_options = [f"{cas} - {inci}" for cas, inci in results] # Selectbox con i risultati formattati selected_display = st.selectbox("Risultati", options=[""] + display_options, key="cas_selectbox") # Salva solo il CAS selezionato nel session_state (estrae la parte prima del " - ") if selected_display and selected_display != "": cas_pattern = r'\b\d{2,7}-\d{2}-\d\b' found_cas = re.findall(cas_pattern, selected_display) unique_cas = list(dict.fromkeys(found_cas)) if not unique_cas: st.warning("Nessun pattern CAS valido trovato nella stringa.") selected_cas = None elif len(unique_cas) == 1: selected_cas = unique_cas[0] st.info(f"CAS rilevato: {selected_cas}") else: selected_cas = st.selectbox( label="Sono stati rilevati più CAS. Selezionane uno:", options=unique_cas ) if selected_cas: st.session_state.selected_cas = selected_cas st.success(f"CAS selezionato: {selected_cas}") else: # Nessun risultato trovato: permetti di usare l'input manuale st.warning("Nessun risultato trovato nel database.") if st.button("Usa questo CAS") and type == "CAS": st.session_state.selected_cas = input.strip() st.success(f"CAS salvato: {input}") else: st.info("INCI non trovato, cerca per CAS o modifica l'input.") # Mostra il CAS attualmente selezionato if st.session_state.selected_cas: st.info(f"CAS corrente: {st.session_state.selected_cas}") # Changelog section st.divider() with st.expander("📝 Registro degli aggiornamenti"): # Placeholder for future versions st.markdown(""" ### v0.3 *v0.3.0 - 2026-02-08 - Aggiunta pagina per la gestione dei preset dei parametri di esposizione * Permette di creare, modificare e salvare preset personalizzati per i calcoli di esposizione * In futuro per calcolare il MoS bisognerà prima selezionare un preset di esposizione - Aggiunta pagina per la creazione degli ingredienti a partire da un CAS: * Facendo una ricerca in 'Ingredienti' e selezionando un CAS, è possibile cliccare su 'Ricerca' per generare un nuovo ingrediente nel database a partire da quel CAS. * Gli ingredienti creati in questo modo hanno già i dati di base, tossicologici e quelli normativi da CosIng * E' consigliato cercare nuovi ingredienti tramite questa nuova funzione ### v0.2 *v0.2.1 - 2026-01-13* - Fix minore su ricerca CosIng **v0.2.0 - 2026-01-05** - Aggiunta ricerca per nome INCI - Possibilità di filtrare per singoli CAS in caso di multipli per stesso INCI - Verifica se il link al download esiste prima di generare il PDF - Aggiunta pagina per verificare i valori per determinare il DAP (da PubChem) - La ricerca per ingrediente su ECHA non va più in errore se almeno uno dei tre dossier esiste - Filtrati i dossier ECHA se sono di tipo 'full' e sono di tipo 'Lead' (sempre Active) --- ### v0.1 **v0.1.0 - 2025-12-18** - Release iniziale - Funzionalità di ricerca per Numero CAS - Integrazione con ECHA - Integrazione con CosIng - Protezione con password - Sistema di navigazione multi-pagina - Download del PDF ECHA dossier - Visualizzazione dati tossicologici ECHA e CosIng """) # Navigation home_page = st.Page(home, title="Home", icon="🏠", default=True) echa_page = st.Page("pages/echa.py", title="ECHA Database", icon="🧪") cosing_page = st.Page("pages/cosing.py", title="CosIng", icon="💄") dap_page = st.Page("pages/dap.py", title="DAP", icon="🧬") exposition_page = st.Page("pages/exposition_page.py", title="Esposizione", icon="☀️") ingredients_page = st.Page("pages/ingredients_page.py", title="Ingredienti", icon="📋") #pubchem_page = st.Page("pages/pubchem.py", title="PubChem", icon="🧬") #cir_page = st.Page("pages/cir.py", title="CIR", icon="📊") pg = st.navigation({ "Ricerca": [home_page], "Calcolatore MoS": [exposition_page, ingredients_page], "Ricerche Online": [echa_page, cosing_page, dap_page] }) pg.run()