added settings

This commit is contained in:
adish-rmr 2026-03-01 00:29:58 +01:00
parent 44bcb38488
commit 83c28bff2f
3 changed files with 169 additions and 259 deletions

View file

@ -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()

View file

@ -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)

169
pages/settings_page.py Normal file
View file

@ -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()