first commit
This commit is contained in:
166
streamlit_dashboard/main.py
Normal file
166
streamlit_dashboard/main.py
Normal file
@@ -0,0 +1,166 @@
|
||||
"""
|
||||
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')
|
||||
|
||||
|
||||
Reference in New Issue
Block a user