"""Streamlit application entry point.
This module configures the main dashboard UI, including accessibility features,
security sanitization for Streamlit messages, and the multi-page navigation
structure.
"""
from __future__ import annotations
from typing import Any
import streamlit as st
from imednet_streamlit.auth import render_auth_sidebar
st.set_page_config(
page_title="iMednet EDC Dashboard",
page_icon="๐ฅ",
layout="wide",
initial_sidebar_state="expanded",
)
# High contrast mode logic
if "high_contrast" not in st.session_state:
st.session_state["high_contrast"] = (
st.query_params.get("high_contrast", "false").lower() == "true"
)
[docs]def toggle_high_contrast() -> None:
"""Toggle the high contrast accessibility mode in session state and URL params."""
st.session_state["high_contrast"] = not st.session_state["high_contrast"]
if st.session_state["high_contrast"]:
st.query_params["high_contrast"] = "true"
elif "high_contrast" in st.query_params:
del st.query_params["high_contrast"]
st.sidebar.toggle(
"High Contrast Mode", value=st.session_state["high_contrast"], on_change=toggle_high_contrast
)
if st.session_state["high_contrast"]:
st.markdown(
"""
<style>
/* High contrast mode CSS ensuring minimum contrast ratio 7:1 */
html, body, [class*="st-"] {
background-color: #000000 !important;
color: #FFFFFF !important;
border-color: #FFFFFF !important;
}
h1, h2, h3, h4, h5, h6, p, span, div {
color: #FFFFFF !important;
}
button {
background-color: #FFFFFF !important;
color: #000000 !important;
border: 2px solid #FFFFFF !important;
}
/* Ensure inputs and interactive elements are clearly visible */
input, select, textarea {
background-color: #000000 !important;
color: #FFFFFF !important;
border: 2px solid #FFFFFF !important;
}
/* Highly visible focus state for keyboard navigation */
*:focus {
outline: 3px solid #FFFF00 !important;
outline-offset: 2px !important;
}
/* SVG charts */
svg text {
fill: #FFFFFF !important;
}
</style>
""",
unsafe_allow_html=True,
)
from imednet.spi.utils import redact_sensitive_text
original_st_error = st.error
original_st_exception = st.exception
original_st_warning = st.warning
original_st_info = st.info
def _sanitize_body(body: Any) -> Any:
"""Redact sensitive information from Streamlit message bodies before rendering."""
if isinstance(body, Exception):
try:
body_str = str(body)
except Exception:
body_str = "<unprintable exception>"
redacted_msg = redact_sensitive_text(body_str)
try:
safe_ex = type(body)(redacted_msg)
safe_ex.__traceback__ = body.__traceback__
return safe_ex
except Exception:
safe_ex = Exception(f"{type(body).__name__}: {redacted_msg}")
safe_ex.__traceback__ = body.__traceback__
return safe_ex
elif isinstance(body, str):
return redact_sensitive_text(body)
return body
[docs]def secure_st_error(body: Any, *args: Any, **kwargs: Any) -> Any:
"""Render a sanitized error message."""
return original_st_error(_sanitize_body(body), *args, **kwargs)
[docs]def secure_st_exception(exception: Any, *args: Any, **kwargs: Any) -> Any:
"""Render a sanitized exception trace."""
return original_st_exception(_sanitize_body(exception), *args, **kwargs)
[docs]def secure_st_warning(body: Any, *args: Any, **kwargs: Any) -> Any:
"""Render a sanitized warning message."""
return original_st_warning(_sanitize_body(body), *args, **kwargs)
[docs]def secure_st_info(body: Any, *args: Any, **kwargs: Any) -> Any:
"""Render a sanitized info message."""
return original_st_info(_sanitize_body(body), *args, **kwargs)
st.error = secure_st_error
st.exception = secure_st_exception
st.warning = secure_st_warning
st.info = secure_st_info
is_connected = render_auth_sidebar()
home_page = st.Page("pages/home.py", title="Home", icon="๐ ", default=True)
queries_page = st.Page("pages/queries.py", title="Query Status", icon="๐")
enrollment_page = st.Page("pages/enrollment.py", title="Subject Enrollment", icon="๐ฅ")
reporting_dashboard_page = st.Page(
"pages/reporting_dashboard.py", title="Reporting Dashboard", icon="๐"
)
sites_page = st.Page("pages/sites.py", title="Site Performance", icon="๐ฅ")
records_page = st.Page("pages/records.py", title="Data Completeness", icon="๐")
setup_wizard_page = st.Page("pages/setup_wizard.py", title="Setup Wizard", icon="๐งญ")
review_workbench_page = st.Page("pages/review_workbench.py", title="Review Workbench", icon="๐งช")
publisher_wizard_page = st.Page("pages/publisher_wizard.py", title="Publisher Wizard", icon="๐๏ธ")
data_lineage_page = st.Page("pages/data_lineage.py", title="Data Lineage", icon="๐ญ")
component_gallery_page = st.Page("pages/component_gallery.py", title="Component Gallery", icon="๐จ")
admin_page = st.Page("pages/admin.py", title="Enterprise Admin", icon="๐ข")
conformance_portal = st.Page("pages/conformance.py", title="Accessibility Portal", icon="โฟ")
if is_connected:
nav = st.navigation(
[
home_page,
queries_page,
enrollment_page,
reporting_dashboard_page,
sites_page,
records_page,
setup_wizard_page,
review_workbench_page,
publisher_wizard_page,
data_lineage_page,
component_gallery_page,
admin_page,
conformance_portal,
]
)
else:
nav = st.navigation([home_page, admin_page, conformance_portal])
nav.run()