Files
2026-05-21 08:40:24 -04:00

167 lines
7.4 KiB
Python

"""
Entry point and primary orchestrator for the Streamlit dashboard application.
This script establishes the global execution environment before rendering any specialized
report components. It acts as the central dependency provider, executing three critical
initialization phases to ensure child classes operate within a safe, predictable state:
1. Security & Access Control: Initializes the session state with user credentials and roles.
It establishes the login UI and conditionally injects restricted routes (like the
`AdminPanelPage`) into the navigation tree based on specific role-based permissions.
2. Configuration State Management: Loads and strictly validates the `DashboardConfig`
YAML file, injecting it into the Streamlit session state. This fail-fast initialization
guarantees that all downstream report classes (e.g., `NaicsReportPage`, `TrainingsEventCountsPage`)
can reliably resolve their required Neoserra data endpoints based on user-selected fiscal
periods without performing redundant file I/O.
3. Global Telemetry: Configures a centralized rotating file and console logging architecture.
By defining this at the root level, any initialization or rendering errors caught within
the isolated execution boundaries of individual report pages are captured consistently.
"""
import logging
from logging.handlers import RotatingFileHandler
import sys
import streamlit as st
from streamlit_authenticator import Authenticate
from auth_config_loader import load_auth_config
from utility_classes.dashboard_config_parser import DashboardConfig
from streamlit_constants import CONFIG_FILE_PATH, ADMIN_ROLE_NAME, DASHBOARD_CONFIG_FILENAME, DASHBOARD_CONFIG_OBJECT_KEY
try:
# Setup authentication with auth module:
# https://github.com/mkhorasani/Streamlit-Authenticator
authenticator:Authenticate = load_auth_config(CONFIG_FILE_PATH) #pyright:ignore
# Stash the authenticator so we can use it on other pages
st.session_state['authenticator'] = authenticator
try:
if authenticator:
authenticator.login('sidebar')
except Exception as e:
print(e, file=sys.stderr)
st.error(e)
except Exception:
authenticator = None
# Load the dashboard's configuration file containing all of the export module information from Neoserra
# Should have the following format:
#export_module_urls:
# clients_list:
# current_fy: "url here"
# prev_fy: "url here"
# new_business_starts:
# current_fy: "url here"
# prev_fy: "url here"
# capital_funding:
# current_fy: "url here"
# prev_fy: "url here"
# trainings:
# current_fy: "url here"
# prev_fy: "url here"
dashboard_config_parser = DashboardConfig(DASHBOARD_CONFIG_FILENAME)
try:
dashboard_config_parser.load()
except KeyError as e:
err_msg = f"""Failed to load dashboard config file, was the format correct? See:
{e}
"""
print(err_msg, file=sys.stderr)
st.error(err_msg)
st.session_state[DASHBOARD_CONFIG_OBJECT_KEY] = dashboard_config_parser
# Setting up logging
# Format is: time - log level - path to file where the log happended - name of module : message
shared_format = logging.Formatter('[%(asctime)s][%(levelname)s][%(pathname)s][%(name)s]: %(message)s')
console_handler = logging.StreamHandler(sys.stdout)
console_handler.setFormatter(shared_format)
log_level = dashboard_config_parser.get_log_level()
if log_level is None:
log_level = logging.INFO
console_handler.setLevel(log_level)
# Rotate files every 8MB, keep 5 of these files
log_path = dashboard_config_parser.get_log_path()
if log_path:
file_handler = RotatingFileHandler(
filename=log_path,
maxBytes=8_388_608,
backupCount=5,
)
file_handler.setFormatter(shared_format)
file_handler.setLevel(log_level)
logging.getLogger().addHandler(file_handler)
logging.getLogger().addHandler(console_handler)
logging.getLogger().setLevel(log_level)
logger = logging.getLogger(__name__)
if st.session_state.get('authentication_status'):
user_roles = st.session_state.get('roles', [])
st_home_page = st.Page("streamlit_pages/home_streamlit_page.py", title="Home", icon="🏠")
st_comparer_page = st.Page("streamlit_pages/comparer_streamlit_page.py", title="Report Comparer", icon="🆚")
st_naics_page = st.Page("streamlit_pages/naics_streamlit_page.py", title="NAICS Analysis", icon="📈")
st_network_nbs_page = st.Page("streamlit_pages/nbs_streamlit_page.py", title="Network NBS Milestones",
icon="☑️")
st_network_funding_page = st.Page("streamlit_pages/funding_streamlit_page.py",
title="Network Funding Milestones", icon="💵")
st_trainings_zero_attendees_page = st.Page("streamlit_pages/trainings_count_statistics_page.py",
title="Network Zero Attendees Trainings", icon="👟")
st_trainings_attendee_ranges = st.Page("streamlit_pages/training_attendee_ranges_page.py",
title="Network Training Attendee Ranges", icon="📊")
st_trainings_event_attendee_comparison = st.Page("streamlit_pages/trainings_event_attendee_comparison_page.py",
title="Network Training Event Attendee Comparison",
icon="📈")
st_trainings_primary_topics = st.Page("streamlit_pages/trainings_primary_topic_page.py",
title="Network Primary Training Topics",
icon="🔎")
st_trainings_attendee_counts = st.Page("streamlit_pages/trainings_attendees_count_page.py",
title="Network Training Attendee Counts",
icon="#️⃣")
st_trainings_event_counts = st.Page("streamlit_pages/trainings_event_count_page.py",
title="Network Training Event Counts",
icon='#️⃣')
st_center_milestones = st.Page("streamlit_pages/center_streamlit_page.py", title="Center Milestone Attribution", icon='🎈')
nav_dict = {
"Home": [st_home_page],
"Utility": [st_comparer_page],
"KPI Attribution": [st_network_nbs_page, st_network_funding_page, st_center_milestones],
"Trainings": [st_trainings_zero_attendees_page, st_trainings_attendee_ranges, st_trainings_event_attendee_comparison, st_trainings_primary_topics, st_trainings_attendee_counts, st_trainings_event_counts],
"Other Reports": [st_naics_page]
}
st_admin_page = st.Page("streamlit_pages/admin_streamlit_page.py", title="Admin Panel", icon='⚙️')
if user_roles and ADMIN_ROLE_NAME in user_roles:
nav_dict["Utility"].append(st_admin_page)
# Set up navigation
pg = st.navigation(
nav_dict
)
pg.run()
elif st.session_state.get('authentication_status') is False or st.session_state.get('authentication_status') is None:
left_col, center_col, right_col = st.columns([0.1, 0.8, 0.1])
banner_loc = "assets/pasbdc_logo.jpg"
try:
center_col.image(banner_loc)
except Exception as e:
logger.exception(f"Tried to load login page banner image {banner_loc} but failed, got {e}")
st.error(f"Home page banner failed to load. A detailed message has been added to the logs.")
st.warning('Enter a valid username and password')