Merge branch 'main' of github.com:adish-rmr/cosmoguard_frontend
This commit is contained in:
commit
c3e61894b0
4 changed files with 186 additions and 17 deletions
52
app.py
52
app.py
|
|
@ -1,6 +1,6 @@
|
|||
import streamlit as st
|
||||
|
||||
from functions_ui import search_cas_number
|
||||
from functions_ui import search_cas_inci
|
||||
|
||||
# Configure page
|
||||
st.set_page_config(
|
||||
|
|
@ -48,11 +48,16 @@ def home():
|
|||
# Inizializza session_state per il CAS number se non esiste
|
||||
if 'selected_cas' not in st.session_state:
|
||||
st.session_state.selected_cas = None
|
||||
|
||||
input = st.text_input("Enter CAS Number:", "")
|
||||
|
||||
# choose between cas or inci
|
||||
type = st.radio("Search by:", ("CAS Number", "INCI Name"), index=0, key="search_mode")
|
||||
input = st.text_input("Enter input:", "")
|
||||
if input:
|
||||
st.caption(f"Ricerca per {input}: trovati i seguenti ingredienti.")
|
||||
results = search_cas_number(input)
|
||||
if type == "CAS Number":
|
||||
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"
|
||||
|
|
@ -64,30 +69,65 @@ def home():
|
|||
# Salva solo il CAS selezionato nel session_state (estrae la parte prima del " - ")
|
||||
if selected_display and selected_display != "":
|
||||
selected_cas = selected_display.split(" - ")[0]
|
||||
if ";" in selected_cas:
|
||||
selected_cas = st.selectbox(options=selected_cas.split(";"), label="Multiple CAS found, please select one:")
|
||||
elif "/" in selected_cas:
|
||||
selected_cas = st.selectbox(options=selected_cas.split("/"), label="Multiple CAS found, please select one:")
|
||||
st.session_state.selected_cas = selected_cas
|
||||
st.success(f"CAS Number 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 Number"):
|
||||
if st.button("Usa questo CAS Number") and type == "CAS Number":
|
||||
st.session_state.selected_cas = input
|
||||
st.success(f"CAS Number salvato: {input}")
|
||||
else:
|
||||
st.info("INCI non trovato, scegli per CAS o modifica l'input.")
|
||||
|
||||
# Mostra il CAS attualmente selezionato
|
||||
if st.session_state.selected_cas:
|
||||
st.info(f"CAS Number corrente: {st.session_state.selected_cas}")
|
||||
|
||||
# Changelog section
|
||||
st.divider()
|
||||
with st.expander("📝 Registro degli aggiornamenti"):
|
||||
# Placeholder for future versions
|
||||
st.markdown("""
|
||||
### Versione 0.2.0 | 2026-01-15
|
||||
- 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)
|
||||
---
|
||||
""")
|
||||
|
||||
st.markdown("""
|
||||
### Versione 0.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="🧬")
|
||||
#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],
|
||||
"Database": [echa_page, cosing_page]
|
||||
"Database": [echa_page, cosing_page, dap_page]
|
||||
})
|
||||
|
||||
pg.run()
|
||||
20
functions.py
20
functions.py
|
|
@ -29,14 +29,20 @@ def cosing_request(cas_num: str) -> Dict[str, Any]:
|
|||
def generate_pdf_download(cas, origin, link):
|
||||
url = 'https://api.cosmoguard.it/api/v1/common/generate-pdf'
|
||||
name = f'{cas}_{origin}'
|
||||
response = requests.post(
|
||||
url,
|
||||
json = {
|
||||
'link': link,
|
||||
'name': name
|
||||
if link is not None:
|
||||
response = requests.post(
|
||||
url,
|
||||
json = {
|
||||
'link': link,
|
||||
'name': name
|
||||
}
|
||||
)
|
||||
data = response.json()
|
||||
else:
|
||||
data = {
|
||||
'success': False,
|
||||
'error': 'No dossier exists for this origin.'
|
||||
}
|
||||
)
|
||||
data = response.json()
|
||||
if data['success'] == True:
|
||||
url = f'https://api.cosmoguard.it/api/v1/common/download-pdf/{name}'
|
||||
response = requests.get(url)
|
||||
|
|
|
|||
|
|
@ -14,11 +14,13 @@ def open_csv_file(file_path):
|
|||
con.execute(query)
|
||||
return con
|
||||
|
||||
def search_cas_number(cas_number, type = 'cas'):
|
||||
def search_cas_inci(input, type = 'cas'):
|
||||
"""Cerca un numero CAS nei dati forniti e restituisce CAS e INCI."""
|
||||
con = open_csv_file('data.csv')
|
||||
|
||||
query = f"SELECT * FROM index WHERE casNo LIKE '%{cas_number}%'"
|
||||
if type == 'cas':
|
||||
query = f"SELECT * FROM index WHERE casNo LIKE '%{input}%'"
|
||||
else:
|
||||
query = f"SELECT * FROM index WHERE inciName ILIKE '%{input}%'"
|
||||
results = con.execute(query).fetchdf()
|
||||
|
||||
# Restituisce una lista di tuple (casNo, inciName)
|
||||
|
|
@ -45,5 +47,5 @@ def download_pdf(casNo, origin, link):
|
|||
)
|
||||
|
||||
if __name__ == "__main__":
|
||||
data = search_cas_number('102242-62-6')
|
||||
data = search_cas_inci('102242-62-6')
|
||||
print(data)
|
||||
121
pages/dap.py
Normal file
121
pages/dap.py
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
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)
|
||||
Loading…
Reference in a new issue