From 83c28bff2fdaed898c8b4cb28c97f90609676469 Mon Sep 17 00:00:00 2001 From: adish-rmr Date: Sun, 1 Mar 2026 00:29:58 +0100 Subject: [PATCH] added settings --- old/cosing.py | 138 --------------------------------- old/dap.py | 121 ----------------------------- pages/settings_page.py | 169 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 169 insertions(+), 259 deletions(-) delete mode 100644 old/cosing.py delete mode 100644 old/dap.py create mode 100644 pages/settings_page.py diff --git a/old/cosing.py b/old/cosing.py deleted file mode 100644 index bd4227a..0000000 --- a/old/cosing.py +++ /dev/null @@ -1,138 +0,0 @@ -import streamlit as st -import json - -from functions import cosing_request -from functions_ui import download_pdf - -st.title("CosIng Database Viewer") - -st.set_page_config( - page_title="CosIng", - page_icon="πŸ§ͺ", - layout="wide" -) - -def display_ingredient(data: dict, level: int = 0): - """Display ingredient information using containers and metrics.""" - - # Get names - name = data.get("commonName") or data.get("inciName") or "Unknown" - item_type = data.get("itemType", "ingredient") - - # Header - if level == 0: - st.title(f"πŸ§ͺ {name}") - else: - st.markdown(f"### {name}") - - # Type badge and chemical name - col_header1, col_header2 = st.columns([1, 3]) - with col_header1: - if item_type == "substance": - st.caption("πŸ”¬ Substance") - else: - st.caption("🧴 Ingredient") - - # Identifiers container - cas_numbers = data.get("casNo", []) - ec_numbers = data.get("ecNo", []) - ref_no = data.get("refNo", "") - - if cas_numbers or ec_numbers or ref_no: - with st.container(border=True): - cols = st.columns(3) - - with cols[0]: - if cas_numbers: - st.metric("CAS", ", ".join(cas_numbers)) - else: - st.metric("CAS", "β€”") - - with cols[1]: - if ec_numbers: - st.metric("EC", ", ".join(ec_numbers)) - else: - st.metric("EC", "β€”") - # Functions - functions = data.get("functionName", []) - if functions: - with st.container(border=True): - st.markdown("**Functions**") - func_cols = st.columns(len(functions)) - for i, func in enumerate(functions): - with func_cols[i]: - st.success(func.title()) - - # Regulatory info - restrictions = data.get("otherRestrictions", []) - annex = data.get("annexNo", []) - regulations = data.get("otherRegulations", []) - opinions = data.get("sccsOpinion", []) - opinion_urls = data.get("sccsOpinionUrls", []) - - if restrictions or annex or regulations or opinions or opinion_urls: - with st.container(border=True): - st.markdown("**Regulatory Information**") - - if annex: - st.info(f"πŸ“‹ **Annex {', '.join(annex)}**") - - if restrictions: - for r in restrictions: - st.warning(f"⚠️ {r}") - - if regulations: - st.write("Other regulations: " + "; ".join(regulations)) - - if opinions: - for opinion in opinions: - st.write(f"πŸ“„ {opinion}") - - if opinion_urls: - for url in opinion_urls: - st.link_button("View SCCS Opinion", url) - - # Source link - cosing_url = data.get("cosingUrl", "") - if cosing_url: - st.link_button("πŸ”— View on CosIng", cosing_url) - - - # Identified Ingredients (recursive) - identified = data.get("identifiedIngredient", []) - if identified: - st.divider() - - # Check if it's a list of IDs or full objects - if identified and isinstance(identified[0], dict): - st.subheader(f"πŸ”¬ Related Substances ({len(identified)})") - for idx, ing in enumerate(identified): - ing_name = ing.get("commonName") or ing.get("inciName") or f"Substance {idx + 1}" - with st.expander(ing_name): - display_ingredient(ing, level=level + 1) - else: - # List of IDs only - st.subheader(f"πŸ”¬ Related Substances ({len(identified)} IDs)") - with st.expander("Show substance IDs"): - # Display in a grid - id_text = ", ".join(str(i) for i in identified[:20]) - if len(identified) > 20: - id_text += f"... and {len(identified) - 20} more" - st.code(id_text) - - - - -def main(): - if st.session_state.get('selected_cas', None) is None: - st.warning("Nessun CAS Number selezionato. Torna alla pagina principale per effettuare una ricerca.") - st.stop() - else: - cas_number = st.session_state.selected_cas - - DATA = cosing_request(cas_number) - display_ingredient(DATA) - - -if __name__ == "__main__": - main() \ No newline at end of file diff --git a/old/dap.py b/old/dap.py deleted file mode 100644 index 3ae47bf..0000000 --- a/old/dap.py +++ /dev/null @@ -1,121 +0,0 @@ -import streamlit as st -import requests -import json - -st.title("PubChem Data Viewer") - -if st.session_state.get('selected_cas', None) is None: - st.warning("Nessun CAS Number selezionato. Torna alla pagina principale per effettuare una ricerca.") - st.stop() -else: - cas_number = st.session_state.selected_cas - -# Make API request -with st.spinner("Fetching data from PubChem..."): - try: - response = requests.post( - "https://api.cosmoguard.it/api/v1/common/pubchem", - json={"cas": cas_number} - ) - response.raise_for_status() - result = response.json() - except requests.exceptions.RequestException as e: - st.error(f"Error fetching data: {e}") - st.stop() - -# Check if request was successful -if not result.get("success", False): - st.error(f"API Error: {result.get('error', 'Unknown error')}") - st.stop() - -data = result.get("data", {}) - -# Display substance header -st.subheader(f"{data.get('first_pubchem_name', 'Unknown').title()}") - -# Basic info container -with st.container(border=True): - col1, col2, col3 = st.columns(3) - with col1: - st.caption("CAS Number") - st.write(data.get("CAS", "β€”")) - with col2: - st.caption("PubChem CID") - st.write(data.get("CID", "β€”")) - with col3: - if data.get("pubchem_link"): - st.link_button("View on PubChem", data.get("pubchem_link")) - -st.divider() - -# Physical/Chemical Properties -st.subheader("Physical & Chemical Properties") - -with st.container(border=True): - prop_col1, prop_col2, prop_col3, prop_col4 = st.columns(4) - - with prop_col1: - st.metric("Molecular Weight", f"{data.get('MolecularWeight', 'β€”')} g/mol" if data.get('MolecularWeight') else "β€”") - - with prop_col2: - st.metric("XLogP", data.get('XLogP', 'β€”')) - - with prop_col3: - st.metric("Exact Mass", data.get('ExactMass', 'β€”')) - - with prop_col4: - st.metric("TPSA", f"{data.get('TPSA', 'β€”')} Ε²" if data.get('TPSA') else "β€”") - -st.divider() - -# Melting Point -melting_points = data.get("Melting Point", []) -if melting_points: - st.subheader("Melting Point") - - with st.expander(f"View {len(melting_points)} reference(s)", expanded=True): - for idx, mp in enumerate(melting_points): - with st.container(border=True): - st.markdown(f"**Reference {idx + 1}**") - - if mp.get("Value"): - st.info(mp.get("Value")) - - if mp.get("Reference"): - st.caption(f"πŸ“š {mp.get('Reference')}") - - if mp.get("Description"): - st.caption(f"ℹ️ {mp.get('Description')}") - - if mp.get("ReferenceNumber"): - st.caption(f"Ref #: {mp.get('ReferenceNumber')}") - -# Dissociation Constants -dissociation_constants = data.get("Dissociation Constants", []) -if dissociation_constants: - st.divider() - st.subheader("Dissociation Constants (pKa)") - - with st.expander(f"View {len(dissociation_constants)} reference(s)", expanded=True): - for idx, dc in enumerate(dissociation_constants): - with st.container(border=True): - st.markdown(f"**Reference {idx + 1}**") - - if dc.get("Value"): - # Check if it's a dictionary or string - value = dc.get("Value") - if isinstance(value, dict): - st.code(json.dumps(value, indent=2)) - else: - st.info(value) - - if dc.get("Reference"): - st.caption(f"πŸ“š {dc.get('Reference')}") - - if dc.get("ReferenceNumber"): - st.caption(f"Ref #: {dc.get('ReferenceNumber')}") - -# Raw JSON viewer -st.divider() -with st.expander("View Raw JSON Response"): - st.json(result) diff --git a/pages/settings_page.py b/pages/settings_page.py new file mode 100644 index 0000000..f897f94 --- /dev/null +++ b/pages/settings_page.py @@ -0,0 +1,169 @@ +import streamlit as st +import requests +import pandas as pd + +API_BASE = "https://api.cosmoguard.it/api/v1" + +st.title("Impostazioni") + +tab_tox, tab_ingredienti, tab_clienti = st.tabs([ + "Indicatore Tox Custom", + "Inventario Ingredienti", + "Gestione Clienti", +]) + + +# --------------------------------------------------------------------------- +# TAB 1 β€” Indicatore Tox Custom +# --------------------------------------------------------------------------- +with tab_tox: + st.subheader("Aggiungi indicatore tossicologico custom") + + with st.form("form_tox_indicator"): + cas = st.text_input("CAS Number", placeholder="es. 56-81-5") + col1, col2 = st.columns(2) + with col1: + indicator = st.selectbox("Indicatore", ["NOAEL", "LOAEL", "LD50"]) + value = st.number_input("Valore", min_value=0.0, step=0.1) + unit = st.text_input("UnitΓ ", placeholder="es. mg/kg bw/day") + with col2: + route = st.text_input("Via di esposizione", placeholder="es. oral, dermal") + toxicity_type = st.selectbox( + "Tipo tossicitΓ ", + ["", "repeated_dose_toxicity", "acute_toxicity"], + format_func=lambda x: x if x else "β€” non specificato β€”" + ) + ref = st.text_input("Riferimento / Fonte", placeholder="es. Studio interno 2024") + + submitted = st.form_submit_button("Aggiungi indicatore", type="primary") + + if submitted: + if not cas or not unit or not route: + st.error("CAS, unitΓ  e via di esposizione sono obbligatori.") + else: + payload = { + "cas": cas, + "indicator": indicator, + "value": value, + "unit": unit, + "route": route, + "toxicity_type": toxicity_type or None, + "ref": ref or None, + } + try: + resp = requests.post(f"{API_BASE}/ingredients/add-tox-indicator", json=payload, timeout=30) + if resp.status_code == 200: + data = resp.json() + tox = data.get("data", {}).get("toxicity", {}) + best = tox.get("best_case") + st.success(f"Indicatore aggiunto per CAS {cas}.") + if best: + st.info(f"Best case aggiornato: **{best['indicator']}** = {best['value']} {best['unit']} ({best['route']})") + elif resp.status_code == 404: + st.error(f"CAS {cas} non trovato in cache. Esegui prima una ricerca nella pagina Ingredienti.") + else: + st.error(f"Errore {resp.status_code}: {resp.json().get('detail', 'errore sconosciuto')}") + except requests.ConnectionError: + st.error("Impossibile connettersi all'API.") + except Exception as e: + st.error(f"Errore: {e}") + + +# --------------------------------------------------------------------------- +# TAB 2 β€” Inventario Ingredienti +# --------------------------------------------------------------------------- +with tab_ingredienti: + st.subheader("Ingredienti nel database") + + if st.button("Aggiorna", key="refresh_ingredienti"): + st.rerun() + + try: + resp = requests.get(f"{API_BASE}/ingredients/list", timeout=10) + result = resp.json() + + if result.get("success") and result.get("data"): + st.caption(f"{result['total']} ingredienti totali") + df = pd.DataFrame(result["data"]) + df["dap"] = df["dap"].map({True: "OK", False: "-"}) + df["cosing"] = df["cosing"].map({True: "OK", False: "-"}) + df["tox"] = df["tox"].map({True: "OK", False: "-"}) + df = df.rename(columns={ + "cas": "CAS", + "dap": "DAP", + "cosing": "COSING", + "tox": "TOX", + "created_at": "Data Acquisizione", + }) + st.dataframe( + df[["CAS", "DAP", "COSING", "TOX", "Data Acquisizione"]], + use_container_width=True, + hide_index=True, + ) + else: + st.info("Nessun ingrediente trovato nel database.") + + except requests.ConnectionError: + st.error("Impossibile connettersi all'API.") + except Exception as e: + st.error(f"Errore nel caricamento: {e}") + + +# --------------------------------------------------------------------------- +# TAB 3 β€” Gestione Clienti +# --------------------------------------------------------------------------- +with tab_clienti: + st.subheader("Clienti registrati") + + if st.button("Aggiorna", key="refresh_clienti"): + st.rerun() + + try: + resp = requests.get(f"{API_BASE}/ingredients/clients", timeout=10) + result = resp.json() + clienti = result.get("data", []) if result.get("success") else [] + except requests.ConnectionError: + st.error("Impossibile connettersi all'API.") + clienti = [] + except Exception as e: + st.error(f"Errore nel caricamento: {e}") + clienti = [] + + if not clienti: + st.info("Nessun cliente trovato.") + else: + for cliente in clienti: + col_nome, col_id, col_btn = st.columns([4, 1, 1]) + with col_nome: + st.write(cliente["nome_cliente"]) + with col_id: + st.caption(f"id: {cliente['id_cliente']}") + with col_btn: + key = f"del_{cliente['id_cliente']}" + if st.button("Elimina", key=key, type="secondary"): + st.session_state[f"confirm_{cliente['id_cliente']}"] = True + + if st.session_state.get(f"confirm_{cliente['id_cliente']}"): + nome = cliente["nome_cliente"] + st.warning(f"Confermi l'eliminazione di **{nome}**?") + col_si, col_no, _ = st.columns([1, 1, 6]) + with col_si: + if st.button("SΓ¬, elimina", key=f"yes_{cliente['id_cliente']}", type="primary"): + try: + r = requests.delete(f"{API_BASE}/ingredients/clients/{nome}", timeout=10) + if r.status_code == 200: + st.success(f"Cliente '{nome}' eliminato.") + st.session_state.pop(f"confirm_{cliente['id_cliente']}", None) + st.rerun() + elif r.status_code == 409: + st.error(r.json().get("detail", "Il cliente ha ordini collegati.")) + st.session_state.pop(f"confirm_{cliente['id_cliente']}", None) + else: + st.error(f"Errore {r.status_code}: {r.json().get('detail', '')}") + st.session_state.pop(f"confirm_{cliente['id_cliente']}", None) + except requests.ConnectionError: + st.error("Impossibile connettersi all'API.") + with col_no: + if st.button("Annulla", key=f"no_{cliente['id_cliente']}"): + st.session_state.pop(f"confirm_{cliente['id_cliente']}", None) + st.rerun()