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