lmb-fe/pages/echa.py
2025-12-01 19:03:21 +01:00

352 lines
No EOL
18 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import streamlit as st
import json
import os
from typing import Any
import requests
import pandas as pd
from functions import clean_and_transform_json, api_req
st.set_page_config(page_title="Echa Toxicological Reports", layout="centered")
st.title("Echa Toxicological Reports")
result = {}
# Single-ingredient input
query = st.text_input("Search by CAS", value="50-00-0")
if st.button("Search"):
with st.spinner("Fetching data..."):
try:
result = api_req(query)
if 'error' in result:
st.error(result['error'])
else:
st.success(f"Data fetched successfully! Last updated: {result['last_update']}")
data = clean_and_transform_json(result)
tab1, tab2, tab3 = st.tabs(["Toxicological Information", "Repeated Dose Toxicity", "Acute Toxicity"])
with tab1:
st.header("Toxicological Information")
table_data = []
txi = data['toxicological_information']
for section_name, section_data in txi['sections'].items():
if section_name == "Administrative data":
continue
elif "Additional information" in section_name:
continue
elif "eyes" in section_name.lower():
population = 'Workers' if 'Workers' in section_name else 'General Population'
table_data.append({
'Population': population,
'Exposure Route': 'Eyes',
'Effect Type': 'Local effects',
'Exposure Duration': '-',
'Hazard Assessment': section_data['Local effects'].get('Conclusion', 'N/A')
})
else:
# Determine population and route
population = 'Workers' if 'Workers' in section_name else 'General Population'
if 'inhalation' in section_name.lower():
route = 'Inhalation'
elif 'dermal' in section_name.lower():
route = 'Dermal'
elif 'oral' in section_name.lower():
route = 'Oral'
else:
route = 'Unknown'
# Process nested data
for effect_type, effect_data in section_data.items():
if isinstance(effect_data, dict):
for exposure_duration, exposure_data in effect_data.items():
if isinstance(exposure_data, dict) and 'HazardAssessment' in exposure_data:
table_data.append({
'Population': population,
'Exposure Route': route,
'Effect Type': effect_type,
'Exposure Duration': exposure_duration,
'Hazard Assessment': exposure_data.get('HazardAssessment', 'N/A')
})
# Create DataFrame
df = pd.DataFrame(table_data)
# Display the main table
st.subheader("📊 Complete Hazard Assessment Overview")
st.dataframe(df, use_container_width=True, height=500)
# Display summary metrics
col1, col2, col3, col4 = st.columns(4)
with col1:
st.metric("Total Assessments", len(df))
with col2:
st.metric("Workers Assessments", len(df[df['Population'] == 'Workers']))
with col3:
st.metric("General Population", len(df[df['Population'] == 'General Population']))
with col4:
st.metric("Unique Routes", df['Exposure Route'].nunique())
# Display additional information
st.markdown("---")
st.subheader("📝 Additional Information")
col1, col2 = st.columns(2)
with col1:
st.markdown("**Workers:**")
st.info(txi['sections']['Additional information - workers']['DiscussionWorkers'])
with col2:
st.markdown("**General Population:**")
st.info(txi['sections']['Additional information - General Population']['DiscussionGenPop'])
# Option to download the data as CSV
st.markdown("---")
csv = df.to_csv(index=False)
st.download_button(
label="📥 Download as CSV",
data=csv,
file_name='hazard_assessment_data.csv',
mime='text/csv'
)
with tab2:
st.header("Repeated Dose Toxicity")
table_data = []
key_values = data['repeated_dose_toxicity']['sections']['Key value for assessment']
# Process Sub-chronic toxicity - systemic effects
subchronic = key_values.get('Sub-chronic toxicity systemic effects', {})
for route, route_data in subchronic.items():
if route_data and isinstance(route_data, dict):
table_data.append({
'Toxicity Type': 'Sub-chronic',
'Effect Type': 'Systemic effects',
'Route': route,
'Effect Level': route_data.get('EffectLevelUnit', '-'),
'Value': route_data.get('EffectLevelValue', '-'),
'Species': route_data.get('Species', '-'),
'Study Reference': route_data.get('LinkToRelevantStudyRecord', '-')[:50] + '...' if route_data.get('LinkToRelevantStudyRecord', '') and len(route_data.get('LinkToRelevantStudyRecord', '')) > 50 else route_data.get('LinkToRelevantStudyRecord', '-')
})
# Process Chronic toxicity - systemic effects
chronic = key_values.get('Chronic toxicity systemic effects', {})
for route, route_data in chronic.items():
if route_data and isinstance(route_data, dict):
table_data.append({
'Toxicity Type': 'Chronic',
'Effect Type': 'Systemic effects',
'Route': route,
'Effect Level': route_data.get('EffectLevelUnit', '-'),
'Value': route_data.get('EffectLevelValue', '-'),
'Species': route_data.get('Species', '-'),
'Study Reference': route_data.get('LinkToRelevantStudyRecord', '-')[:50] + '...' if route_data.get('LinkToRelevantStudyRecord', '') and len(route_data.get('LinkToRelevantStudyRecord', '')) > 50 else route_data.get('LinkToRelevantStudyRecord', '-')
})
# Process Repeated dose toxicity - local effects
local_effects = key_values.get('Repeated dose toxicity local effects', {})
for route, route_data in local_effects.items():
if route_data and isinstance(route_data, dict):
table_data.append({
'Toxicity Type': route_data.get('TestType', 'Repeated dose').capitalize() if route_data.get('TestType') else 'Repeated dose',
'Effect Type': 'Local effects',
'Route': route,
'Effect Level': route_data.get('EffectLevelUnit', '-'),
'Value': route_data.get('EffectLevelValue', '-'),
'Species': route_data.get('Species', '-'),
'Study Reference': route_data.get('LinkToRelevantStudyRecord', '-')[:50] + '...' if route_data.get('LinkToRelevantStudyRecord', '') and len(route_data.get('LinkToRelevantStudyRecord', '')) > 50 else route_data.get('LinkToRelevantStudyRecord', '-')
})
# Add endpoint conclusions
conclusions_data = []
if key_values.get('EndpointConclusionSystemicEffectsOralRoute'):
conclusions_data.append({
'Route': 'Oral',
'Effect Type': 'Systemic effects',
'Conclusion': key_values.get('EndpointConclusionSystemicEffectsOralRoute')
})
if key_values.get('EndpointConclusionSystemicEffectsDermal'):
conclusions_data.append({
'Route': 'Dermal',
'Effect Type': 'Systemic effects',
'Conclusion': key_values.get('EndpointConclusionSystemicEffectsDermal')
})
# Create DataFrames
df_toxicity = pd.DataFrame(table_data) if table_data else pd.DataFrame()
df_conclusions = pd.DataFrame(conclusions_data) if conclusions_data else pd.DataFrame()
# Display the main toxicity table
st.subheader("📊 Toxicity Study Results")
if not df_toxicity.empty:
st.dataframe(df_toxicity, use_container_width=True, height=400)
else:
st.info("No toxicity data available")
# Display summary metrics
col1, col2, col3, col4 = st.columns(4)
with col1:
st.metric("Total Studies", len(df_toxicity) if not df_toxicity.empty else 0)
with col2:
unique_routes = df_toxicity['Route'].nunique() if not df_toxicity.empty else 0
st.metric("Routes Tested", unique_routes)
with col3:
unique_species = df_toxicity['Species'].nunique() if not df_toxicity.empty else 0
st.metric("Species Tested", unique_species)
with col4:
st.metric("Classification", "Not required")
# Display endpoint conclusions
if not df_conclusions.empty:
st.markdown("---")
st.subheader("🎯 Endpoint Conclusions")
st.dataframe(df_conclusions, use_container_width=True, hide_index=True)
# Display key information
st.markdown("---")
st.subheader("📋 Key Study Information")
key_info = data['repeated_dose_toxicity']['sections']['Description of key information']['KeyInformation']
# Split the key information into paragraphs for better readability
key_info_paragraphs = key_info.split('.')
key_info_formatted = '. '.join([p.strip() for p in key_info_paragraphs if p.strip()])
st.info(key_info_formatted[:1000] + "..." if len(key_info_formatted) > 1000 else key_info_formatted)
# Display classification justification
st.markdown("---")
st.subheader("⚖️ Classification Justification")
justif = data['repeated_dose_toxicity']['sections']['Justification for classification or non-classification (Specific target organ toxicity-repeated exposure (STOT RE))']
st.success(justif.get('JustifClassif', 'No information available'))
# Display additional discussion (truncated)
st.markdown("---")
st.subheader("📝 Additional Discussion")
discussion = data['repeated_dose_toxicity']['sections']['Additional information'].get('Discussion', '')
if discussion:
# Show first 500 characters of discussion
with st.expander("Click to expand full discussion"):
st.text_area("", discussion, height=300, disabled=True)
# Option to download the data as CSV
st.markdown("---")
if not df_toxicity.empty:
csv = df_toxicity.to_csv(index=False)
st.download_button(
label="📥 Download Toxicity Data as CSV",
data=csv,
file_name='repeated_dose_toxicity_data.csv',
mime='text/csv'
)
with tab3:
st.header("Acute Toxicity")
table_data = []
key_values = data['acute_toxicity']['sections']['Key value for assessment']
# Process each route of exposure
for route_name, route_data in key_values.items():
if route_data and isinstance(route_data, dict) and 'EffectLevelUnit' in route_data:
# Extract route type
if 'oral' in route_name:
route = 'Oral'
species = 'Rat, Mice, Guinea pig' # From key information
elif 'dermal' in route_name:
route = 'Dermal'
species = 'Guinea pig'
elif 'inhalation' in route_name:
route = 'Inhalation'
species = 'Rat'
else:
route = 'Other'
species = '-'
table_data.append({
'Route of Exposure': route,
'Effect Level': route_data.get('EffectLevelUnit', '-'),
'Value': route_data.get('EffectLevelValue', '-'),
'Physical Form': route_data.get('PhysicalForm', '-'),
'Species': species,
'Endpoint Conclusion': route_data.get('EndpointConclusion', '-')
})
# Create DataFrame
df_toxicity = pd.DataFrame(table_data)
# Display the main toxicity table
st.subheader("📊 Acute Toxicity Study Results")
st.dataframe(
df_toxicity,
use_container_width=True,
height=200,
column_config={
"Route of Exposure": st.column_config.TextColumn("Route", width="small"),
"Effect Level": st.column_config.TextColumn("Parameter", width="small"),
"Value": st.column_config.TextColumn("Value", width="medium"),
"Physical Form": st.column_config.TextColumn("Form", width="medium"),
"Species": st.column_config.TextColumn("Test Species", width="medium"),
"Endpoint Conclusion": st.column_config.TextColumn("Conclusion", width="large")
}
)
# Display summary metrics
col1, col2, col3, col4 = st.columns(4)
with col1:
st.metric("Routes Tested", len(df_toxicity))
with col2:
st.metric("Oral LD50", "11,500 mg/kg bw")
with col3:
st.metric("Dermal LD50", "56,750 mg/kg bw")
with col4:
st.metric("Inhalation LC50", ">5.85 mg/L")
# Display key information
st.markdown("---")
st.subheader("📋 Key Study Information")
key_info = data['acute_toxicity']['sections']['Description of key information']['KeyInformation']
# Format the key information for better readability
key_info_formatted = key_info.replace(".", ".\n").replace("The acute", "\n• The acute").replace("In an", "\n• In an")
st.info(key_info_formatted)
# Display classification justification
st.markdown("---")
st.subheader("⚖️ Classification Justification")
justif = data['acute_toxicity']['sections']['Justification for classification or non-classification']
st.success(justif.get('JustifClassif', 'No information available'))
# Display additional discussion
st.markdown("---")
st.subheader("📝 Additional Information & Discussion")
discussion = data['acute_toxicity']['sections']['Additional information'].get('Discussion', '')
if discussion:
# Parse discussion to highlight key points
discussion_parts = discussion.split('.')
# Display main conclusion
st.info("💡 **Main Conclusion:** " + discussion_parts[0] + ".")
# Display full discussion in expander
with st.expander("Click to view full discussion and references"):
# Format the discussion for better readability
formatted_discussion = discussion.replace(".", ".\n\n")
st.text_area("", formatted_discussion, height=400, disabled=True)
# Option to download the data as CSV
st.markdown("---")
csv = df_toxicity.to_csv(index=False)
st.download_button(
label="📥 Download Acute Toxicity Data as CSV",
data=csv,
file_name='acute_toxicity_data.csv',
mime='text/csv'
)
except Exception as e:
st.error(f"An error occurred: {e}")