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}")