167 lines
7.4 KiB
Python
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')
|
|
|
|
|