first commit
This commit is contained in:
256
streamlit_dashboard/page_classes/nbs_milestones_page_class.py
Normal file
256
streamlit_dashboard/page_classes/nbs_milestones_page_class.py
Normal file
@@ -0,0 +1,256 @@
|
||||
from typing import Dict, Any, List
|
||||
import logging
|
||||
|
||||
import pandas as pd
|
||||
from streamlit.delta_generator import DeltaGenerator
|
||||
from fiscalyear import *
|
||||
from plotly.graph_objects import Figure
|
||||
import streamlit as st
|
||||
|
||||
from constants_module import OUT_COLUMNS, NEOSERRA_COLUMNS, TRAINING_COUNT_COLUMNS
|
||||
from streamlit_constants import DASHBOARD_CONFIG_OBJECT_KEY
|
||||
from utility_classes.base_report_page import BaseReportPage
|
||||
from utility_classes.figure_with_max_y import FigureWithMaxY
|
||||
from cached_function_wrappers.shared import get_df_centers
|
||||
from cached_function_wrappers.nbs_cached_functions import cached_get_nbs_data
|
||||
from section_1_graph_library_module import ( # pyright:ignore
|
||||
make_nbs_attribution_network_wide,
|
||||
make_attribution_rate_chart,
|
||||
make_theoretical_attribution_rate_chart,
|
||||
)
|
||||
from utility_classes.dashboard_config_parser import DashboardConfig, ExportModulePair
|
||||
|
||||
|
||||
class NetworkNbsMilestonesReportPage(BaseReportPage):
|
||||
"""
|
||||
Concrete implementation of a report page analyzing New Business Start (NBS) milestones.
|
||||
|
||||
This class manages the data retrieval and rendering lifecycle for NBS metrics. It tracks
|
||||
attribution sources and documentation compliance rates across centers, utilizing temporal
|
||||
state (fiscal years) to route requests to the correct data endpoints.
|
||||
|
||||
:param kwargs: Arbitrary keyword arguments
|
||||
"""
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
"""
|
||||
Initializes the temporal boundaries and configuration state for the NBS report.
|
||||
|
||||
Captures the current and previous fiscal years to set up report filtering logic and
|
||||
extracts the global dashboard configuration from the session state to resolve data export URLs.
|
||||
|
||||
:param kwargs: Arbitrary keyword arguments.
|
||||
"""
|
||||
|
||||
super().__init__("Network New Business Start Milestones Report")
|
||||
|
||||
self.fiscal_year = FiscalYear.current()
|
||||
self.prev_fiscal_year = self.fiscal_year.prev_fiscal_year
|
||||
|
||||
self.fiscal_year_text = f'FY{str(self.fiscal_year.fiscal_year)[2:]}'
|
||||
self.prev_fiscal_year_text = f'FY{str(self.prev_fiscal_year.fiscal_year)[2:]}'
|
||||
|
||||
def get_fiscal_year_export_url(self, selected_fiscal_year) -> str:
|
||||
"""
|
||||
Resolves the external dataset endpoint based on the selected temporal state.
|
||||
|
||||
Maps the user's fiscal year selection to the appropriate configured export URL,
|
||||
ensuring downstream data fetches hit the correct historical or current dataset.
|
||||
|
||||
:param selected_fiscal_year: The string representation of the chosen fiscal year.
|
||||
:type selected_fiscal_year: Any
|
||||
:return: The URL for the corresponding dataset export.
|
||||
:rtype: str
|
||||
"""
|
||||
export_urls:ExportModulePair = self.app_config.get_nbs_milestones_urls()
|
||||
if selected_fiscal_year == self.fiscal_year_text:
|
||||
return export_urls.current_fy
|
||||
else:
|
||||
return export_urls.prev_fy
|
||||
|
||||
@staticmethod
|
||||
def get_page_name():
|
||||
"""
|
||||
Provides the static display identifier for the report.
|
||||
|
||||
This value is consumed by dashboard orchestrators to populate navigation menus
|
||||
and UI selectors.
|
||||
|
||||
:return: The human-readable name of the report.
|
||||
:rtype: str
|
||||
"""
|
||||
return "NBS Funding Milestones"
|
||||
|
||||
def render_controls(self, container: DeltaGenerator) -> Dict[str, Any]:
|
||||
"""
|
||||
Defines the input UI and captures the parameter state required for report generation.
|
||||
|
||||
Renders selection widgets for fiscal year and target centers. Enforces a strict halt
|
||||
on Streamlit execution if the base dataset fails to load, preventing cascading errors
|
||||
in downstream processing steps.
|
||||
|
||||
:param container: The layout element to attach the input widgets to.
|
||||
:type container: DeltaGenerator
|
||||
:return: A dictionary containing the user-selected fiscal year and centers.
|
||||
:rtype: Dict[str, Any]
|
||||
"""
|
||||
report_settings_expander = container.expander(
|
||||
label="Report Options",
|
||||
expanded=True,
|
||||
key=self.get_widget_key("report_settings_expander")
|
||||
)
|
||||
|
||||
report_settings_expander.markdown("## Dataset Options")
|
||||
report_settings_expander.markdown("These settings will modify the input dataset used to generate the graphs.")
|
||||
selected_fiscal_year = report_settings_expander.selectbox(
|
||||
label="Fiscal Year",
|
||||
options=[self.prev_fiscal_year_text, self.fiscal_year_text],
|
||||
index=1,
|
||||
key=self.get_widget_key("selected_fiscal_year_selectbox")
|
||||
)
|
||||
|
||||
reportable_only = report_settings_expander.checkbox(label="Reportable only?", value=True, key=self.get_widget_key("reportable_only_checkbox"))
|
||||
|
||||
export_url:str = self.get_fiscal_year_export_url(selected_fiscal_year)
|
||||
|
||||
try:
|
||||
all_centers = get_df_centers(export_url)
|
||||
except Exception as e:
|
||||
self.logger.exception(f"Failed to fetch the dataset for this page: {e}")
|
||||
container.error(
|
||||
f"Failed to get the list of all centers for the dataset for this page. A detailed error message has been added to the logs. {self.app_config.get_errors_contact_string()}")
|
||||
st.stop()
|
||||
|
||||
selected_centers = report_settings_expander.multiselect(label="Centers", options=all_centers,
|
||||
default=all_centers,
|
||||
key=self.get_widget_key("selected_centers_multiselect"))
|
||||
return {
|
||||
"selected_fiscal_year":selected_fiscal_year,
|
||||
"selected_centers":selected_centers,
|
||||
"reportable_only":reportable_only
|
||||
}
|
||||
|
||||
def generate_figures(self, parameters: Dict[str, Any]):
|
||||
"""
|
||||
Executes the analytical data pipeline and constructs visualization objects.
|
||||
|
||||
Fetches the NBS dataset, applies structural transformations, and generates Plotly charts
|
||||
for attribution and documentation rates. Computes the maximum Y-axis value for quantity-based
|
||||
charts to enable cross-report axis synchronization.
|
||||
|
||||
:param parameters: The parameter dictionary captured from the render_controls phase.
|
||||
:type parameters: Dict[str, Any]
|
||||
:return: A dictionary mapping identifiers to FigureWithMaxY objects and the raw dataframe.
|
||||
:rtype: Dict[str, Any]
|
||||
"""
|
||||
selected_fiscal_year = parameters['selected_fiscal_year']
|
||||
selected_centers = parameters['selected_centers']
|
||||
reportable_only = parameters['reportable_only']
|
||||
|
||||
export_url = self.get_fiscal_year_export_url(selected_fiscal_year)
|
||||
|
||||
nbs_df = cached_get_nbs_data(export_url, reportable_only=reportable_only, allowed_centers=selected_centers)
|
||||
|
||||
try:
|
||||
network_wide_funding_fig = make_nbs_attribution_network_wide(
|
||||
nbs_df,
|
||||
# Zero out the graph note, we'll render it manually later
|
||||
graph_note="",
|
||||
title=f"New Business Start Attributions Per Center {selected_fiscal_year}",
|
||||
network_label="PASBDC*",
|
||||
col_neo_center=NEOSERRA_COLUMNS.center,
|
||||
col_documentation_level=OUT_COLUMNS.milestone_documentation_level
|
||||
)
|
||||
except Exception as e:
|
||||
self.logger.exception(f"Failed to generate the network wide funding stacked bars chart: {e}")
|
||||
st.error(f"Failed to generate the network wide stacked funding bar chart for this page. A detailed error has been placed in the logs. {self.app_config.get_errors_contact_string()}")
|
||||
st.stop()
|
||||
|
||||
try:
|
||||
documented_only_fig = make_attribution_rate_chart(
|
||||
nbs_df,
|
||||
fiscalyear=selected_fiscal_year,
|
||||
documented_tag=OUT_COLUMNS.val_documented,
|
||||
col_neo_center=NEOSERRA_COLUMNS.center,
|
||||
col_documentation_level=OUT_COLUMNS.milestone_documentation_level
|
||||
)
|
||||
except Exception as e:
|
||||
self.logger.exception(f"Failed to generate the network wide documented only bar chart: {e}")
|
||||
st.error(
|
||||
f"Failed to generate the network wide documented only bar chart for this page. A detailed error has been placed in the logs. {self.app_config.get_errors_contact_string()}")
|
||||
st.stop()
|
||||
try:
|
||||
theoretical_fig = make_theoretical_attribution_rate_chart(
|
||||
nbs_df,
|
||||
title=f"Documented Percentage if All Funding Milestones With an Attribution Source had an Affirmation {selected_fiscal_year}",
|
||||
documented_tag=OUT_COLUMNS.val_documented,
|
||||
affirmation_missing_tag=OUT_COLUMNS.val_affirmation_missing,
|
||||
col_neo_center=NEOSERRA_COLUMNS.center,
|
||||
col_documentation_level=OUT_COLUMNS.milestone_documentation_level
|
||||
)
|
||||
|
||||
# Obtain the max y value for the chart we care about synchronizing the axis with if this report is in
|
||||
# a page comparer
|
||||
max_bar_y = nbs_df[NEOSERRA_COLUMNS.center].value_counts().max()
|
||||
except Exception as e:
|
||||
self.logger.exception(f"Failed to generate the network wide theoretical documentation bar chart: {e}")
|
||||
st.error(
|
||||
f"Failed to generate the network wide theoretical documentation bar chart for this page. A detailed error has been placed in the logs. {self.app_config.get_errors_contact_string()}")
|
||||
st.stop()
|
||||
|
||||
return {
|
||||
"network_wide_nbs_fig":FigureWithMaxY(figure=network_wide_funding_fig, max_y=max_bar_y),
|
||||
"documented_only_fig":FigureWithMaxY(figure=documented_only_fig, max_y=0.0),
|
||||
"theoretical_fig":FigureWithMaxY(figure=theoretical_fig, max_y=0.0),
|
||||
"nbs_df":nbs_df
|
||||
}
|
||||
|
||||
def render_figures(self, container: DeltaGenerator, output_data: Dict[str, Any]):
|
||||
"""
|
||||
Binds the computed visualization artifacts and raw data to the Streamlit UI.
|
||||
|
||||
Draws the Plotly figures to the screen and injects static HTML definitions for
|
||||
documentation compliance levels to ensure users can accurately interpret the charts.
|
||||
Exposes the raw dataframe via an expander for data auditing.
|
||||
|
||||
:param container: The layout element to draw the report visuals onto.
|
||||
:type container: DeltaGenerator
|
||||
:param output_data: The computed figures and dataframes from generate_figures.
|
||||
:type output_data: Dict[str, Any]
|
||||
"""
|
||||
network_wide_nbs_fig:Figure= output_data['network_wide_nbs_fig']['figure']
|
||||
documented_only_fig:Figure = output_data['documented_only_fig']['figure']
|
||||
theoretical_fig:Figure = output_data['theoretical_fig']['figure']
|
||||
nbs_df:pd.DataFrame = output_data['nbs_df']
|
||||
|
||||
container.plotly_chart(network_wide_nbs_fig, key=self.get_widget_key("network_wide_funding_fig"))
|
||||
container.markdown(
|
||||
"<b>NOTE:</b>Documentation levels were determined as follows.<br><br>"
|
||||
"<b>Documented: Will be submitted to Nexus as long as 'Director Verified' is checked</b></br> There is a non-blank, non-'Requested on eCenter' attribution source AND Affirmation Statement was non-blank<br>\tNOTE: If the attribution source is eCenter, no affirmation is required.<br>"
|
||||
"<b>Affirmation Statement Missing: Will NOT be submitted to Nexus</b></br> Attribution source is non-blank, non-'Requested on eCenter' BUT affirmation statement was blank.</br>"
|
||||
"<b>Not Documented:Will NOT be submitted to Nexus</b></br> The attribution source is blank or 'Requested on eCenter'",
|
||||
unsafe_allow_html=True
|
||||
)
|
||||
container.plotly_chart(documented_only_fig, key=self.get_widget_key("documented_only_fig"))
|
||||
container.plotly_chart(theoretical_fig, key=self.get_widget_key("theoretical_fig"))
|
||||
|
||||
dataset_expander = container.expander(
|
||||
label="Source Datasets",
|
||||
expanded=True,
|
||||
key=self.get_widget_key("dataset_expander")
|
||||
)
|
||||
dataset_expander.markdown("## Source Data")
|
||||
dataset_expander.markdown("### Neoserra Trainings Dataset")
|
||||
dataset_expander.write(nbs_df)
|
||||
|
||||
def get_syncable_figure_keys(self) -> List[str]:
|
||||
"""
|
||||
Declares the specific figures that permit dynamic external Y-axis scaling.
|
||||
|
||||
Restricts synchronization to the network-wide funding figure, as scaling axes on
|
||||
the percentage-based attribution charts would distort the visual representation.
|
||||
|
||||
:return: A list of dictionary keys corresponding to sync-compatible figures.
|
||||
:rtype: List[str]
|
||||
"""
|
||||
return ["network_wide_nbs_fig"]
|
||||
Reference in New Issue
Block a user