lmb-fe/app.py
2026-02-22 19:40:07 +01:00

240 lines
No EOL
11 KiB
Python

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
with st.container(border=True):
col_left, col_right = st.columns([2, 1])
with col_left:
type = st.radio("Cerca per:", ("CAS", "INCI"), index=0, key="search_mode", horizontal=True)
input = st.text_input("Inserisci:", "")
with col_right:
real_time = st.checkbox("Ricerca online", value=False, key="real_time_search")
force_refresh = st.checkbox("Aggiorna Ingrediente", value=False)
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:
display_options = [f"{cas} - {inci}" for cas, inci in results]
selected_display = st.selectbox("Risultati", options=[""] + display_options, key="cas_selectbox")
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:
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.")
if st.session_state.selected_cas:
st.info(f"CAS corrente: {st.session_state.selected_cas}")
if st.button("Vai a Ingredienti →", type="primary"):
st.switch_page(ingredients_page)
if real_time:
if st.button("Vai a ECHA →"):
st.switch_page(echa_page)
st.session_state.force_refresh = force_refresh
# Guide section
st.divider()
with st.expander("📖 Guida all'utilizzo"):
st.markdown("""
### Ricerca ingredienti
La barra di ricerca in questa pagina permette di cercare un ingrediente per **CAS** o per **INCI**.
Il risultato viene poi visualizzato nella pagina **Ingrediente**, che contiene:
- I dati per determinare il **DAP** (caratteristiche fisico-chimiche da PubChem)
- Le **restrizioni normative** da CosIng (annex, restrizioni cosmetiche, opinioni SCCS)
- I dati di **tossicologia** con il miglior indicatore disponibile (NOAEL/LOAEL ecc.)
- La possibilità di **scaricare direttamente le fonti** (PDF ECHA e CosIng)
> **Ricerca online** — mostra anche la pagina **ECHA** con il dettaglio completo dei dossier.
> È un metodo di visualizzazione più vecchio; si consiglia di usare la pagina **Ingrediente**.
> **Aggiorna Ingrediente** — forza il ricalcolo e l'aggiornamento dell'ingrediente nel database,
> utile per verificare eventuali cambi di restrizioni normative.
---
### Ordini PIF (Punti 6, 7, 8)
Dalla sezione **PIF** è possibile:
- **Nuovo PIF** — inserire i dati del prodotto e la tabella QQ degli ingredienti per avviare
il calcolo automatico del **Margin of Safety (MoS)**. Il sistema seleziona automaticamente
gli indicatori tossicologici migliori per ciascun ingrediente e produce un **file Excel**
con SED, MoS e tabella QQ pronti per il PIF.
Per prodotti simili a uno già esistente, si può inserire il **numero d'ordine precedente**
per pre-compilare la tabella QQ.
- **Ordini PIF** — visualizza tutti gli ordini effettuati con il relativo stato. Da qui si può:
- Scaricare l'**Excel** (SED, MoS, tabella QQ)
- Scaricare lo **ZIP delle fonti** (tutti i PDF ECHA e CosIng dell'ordine)
- **Cancellare** un ordine (sconsigliato salvo necessità)
Tutti gli ordini vengono salvati nel database, inclusa la lista degli ingredienti, così da
poter individuare facilmente eventuali PIF da rivedere in caso di modifiche alle restrizioni.
È possibile filtrare gli ordini per **stato**, **nome cliente** e **data**.
---
### Preset di esposizione
Prima di calcolare il MoS è necessario avere almeno un **preset di esposizione** configurato
nella pagina **Esposizione**. Il preset definisce i parametri del prodotto (superficie esposta,
quantità giornaliera, fattore di ritenzione, vie di esposizione, ecc.).
- Scegliere un nome **chiaro ed esplicativo** (es. *Crema viso leave-on*)
- Non possono esistere due preset con lo stesso nome
- I preset possono essere cancellati se non più necessari
- I preset sono riutilizzabili per più ordini dello stesso tipo di prodotto
---
> Ogni pagina dell'applicazione contiene una propria guida dettagliata sul suo funzionamento.
""")
# Changelog section
with st.expander("📝 Registro degli aggiornamenti"):
# Placeholder for future versions
st.markdown("""
### v0.8
*v0.8.0 - 2026-02-22
- Versione iniziale (v0.8.0) rilasciata per test interno e feedback
- Tutte le funzionalità principali implementate, ma in fase di test e ottimizzazione
- Modificato il flusso di ricerca, ora tutte le informazioni si trovano su Ingrediente
- Aggiustati numerosi bug minori e migliorata l'usabilità generale
* Si può visionare grado di ionizzazione e altri dati DAP direttamente su Ingrediente
* Ora si può scaricare direttamente il PDF del CosIng ufficiale (se esiste) dalla pagina Ingrediente
* Il migliore indicatore tossicologico viene automaticamente individuato
* Si può forzare l'aggiornamento dei dati di un ingrediente in caso di modifiche normative o nuovi dati tossicologici
- Aggiunta la funzionalità di creare ordini per PIF e scaricare i risultati in Excel
- Aggiunta la funzionalità di creare preset di esposizione personalizzati e cancellarli
- Aggiunta la funzionalità di visualizzare tutti i PIF creati e il loro stato di avanzamento (con possibilità di cancellarli)
- Aggiunta la funzionalità di scaricare un file ZIP con tutte le fonti (PDF ECHA e CosIng) per ogni ordine
### 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="Cerca", 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="Ingrediente", icon="📋")
order_page = st.Page("pages/order_page.py", title="Nuovo PIF", icon="🛒")
list_orders = st.Page("pages/list_orders.py", title="Ordini PIF", icon="📦")
pg = st.navigation({
"Ricerca": [home_page, ingredients_page],
"PIF": [list_orders, order_page, exposition_page],
"Online": [echa_page],
})
pg.run()