Source code for viiapackage.tools.myviia_dashboard.helper_functions

### ===================================================================================================================
###   MYVIIA dashboard helper functions
### ===================================================================================================================
# Copyright ©VIIA 2025

### ===================================================================================================================
###   1. Import modules
### ===================================================================================================================

# General imports
from __future__ import annotations
import streamlit as st
from typing import TYPE_CHECKING, List

# References for functions and classes in the haskoning_structural package
from haskoning_structural.project_information import fem_get_building_volume

# References for functions and classes in the viiaPackage
if TYPE_CHECKING:
    from viiapackage.viiaStatus import ViiaProject
from viiapackage import viia_create_project
from viiapackage.project_information.viia_get_total_load_bearing_wall_length import \
    viia_get_total_load_bearing_wall_length
from viiapackage.tools.myviia_dashboard.MVDashboardConfig import MYVIIADashboardConfig


### ===================================================================================================================
###   2. Helper functions for the MYVIIA dashboard
### ===================================================================================================================

[docs]def initialise(obj_nr: str, obj_part: str): """ Function initialise the session by adding info to the session state of streamlit for later use. Input: - obj_nr (str): Name of the object. - obj_part (str): In case of multiple parts (separate TVA reports will be delivered), select the correct part for this analysis. On MYVIIA you can see which object parts are available. If the object is not split in separate parts, this input is not required. Output: - The streamlit session state is initialised. """ st.session_state['project'] = viia_create_project(project_name=obj_nr, object_part=obj_part) st.session_state['plots'] = {} st.session_state['saved_data'] = {} st.session_state['updated_data'] = {}
[docs]def create_overview(project: ViiaProject, container): """ Function to create an overview about the project in the sidebar. Input: - project (obj): VIIA project object containing collections of fem objects and project variables. - container (obj): Streamlit sidebar container. Output: - Overview is created in the sidebar. """ container.info( f""" # Overview\n {project.name} - {project.project_information['objectdeel']}\n {project.project_information['object_adressen'][0]['straatnaam']} {project.project_information['object_adressen'][0]['huisnummer']}\n {project.project_information['object_adressen'][0]['postcode']}, {project.project_information['object_adressen'][0]['plaatsnaam']}\n {project.project_information['npr_versie']}\n {project.analysis_type}\n Cluster: {project.project_information['cluster_nen']}\n Consequence class: {project.project_information['consequence_class']}\n Reference period: {project.project_information['reference_period']}\n PGA: {project.project_information['pga']}g\n [Box folder]({project.project_information['boxmap']})""")
[docs]def create_3d_plotly_fig( project: ViiaProject, obj_part: str, load_bearing: bool = False, calculate_volume: bool = False, floors: bool = False, roofs: bool = False, opacity: float = 0.3): """ Creates a 3D Plotly figure for the specified model visualization. Input: project (ViiaProject): VIIA project object containing collections of FEM objects and project variables. obj_part (str): Name of the object-part in MYVIIA. load_bearing (bool, optional): If True, creates a visualization for load-bearing walls. Defaults to False. calculate_volume (bool, optional): If True, creates a visualization for the building volume. Defaults to False. floors (bool, optional): If True, creates a visualization for floors. Defaults to False. roofs (bool, optional): If True, creates a visualization for roofs. Defaults to False. opacity (float, optional): Opacity level for the figure. Defaults to 1.0. Output: object: A Plotly Figure object representing the requested visualization. Raises: ValueError: If no valid condition is met to create a 3D Plotly figure. """ if load_bearing: result = viia_get_total_load_bearing_wall_length(project=project, create_plot_objects=True, add_non_load_bearing_walls=True) total_length, lines, fig = result[:3] total_non_bearing_length = result[3] if len(result) > 3 else None fig.update_layout(title=f"{project.name} {obj_part} " f"(Total Load Bearing Length: {total_length} m, " f"Total Non-Load Bearing Length: {total_non_bearing_length or 0} m)") elif calculate_volume: volume, fig = fem_get_building_volume(project=project, create_plot_object=True) fig.update_layout(title=f"{project.name} {obj_part} (Volume: {volume} m³)") elif floors or roofs: pic, fig = project.viia_create_plots( show=False, use_plotly=True, return_figure=True, save_plot=False, alpha=1.0) colours = {item.name: 'red' for item in (project.collections.floors if floors else project.collections.roofs)} project.update_plot_colour(figure=fig, colours=colours) fig.update_layout(title=f"{project.name} - {obj_part} ({'Floors' if floors else 'Roofs'} Visualization)") else: pic, fig = project.viia_create_plots( show=False, use_plotly=True, return_figure=True, save_plot=False, alpha=1.0) fig.update_layout(title=f"{project.name} - {obj_part}") if not calculate_volume or load_bearing: fig.update_traces(opacity=opacity) fig.update_layout(height=MYVIIADashboardConfig.PIC_HEIGHT) return fig
[docs]def first_non_zero_from_bottom_dict(data: list, keys: List[str]) -> dict: """ Function to retrieve the first non-zero value from the bottom of a list of dictionaries for specified keys. Input: - data (list): The list of dictionaries containing various attributes. - keys (list): The list of keys to check for non-zero values. Output: - Returns s dictionary with the first non-zero values for each specified key. """ results = {key: None for key in keys} for entry in reversed(data): for key in keys: value = entry.get(key) if value and value != 0 and results[key] is None: results[key] = value return results
[docs]def round_values(df): """ Function to round numerical values in the DataFrame to the first decimal place and remove any trailing zeros. Input: - df (pd.DataFrame): The pandas DataFrame with numerical values to be rounded. Output: - Returns the pandas DataFrame with rounded numerical values without trailing zeros. """ for col in df.columns: if col not in ['name', 'date']: df[col] = df[col].apply( lambda x: '{:.1f}'.format(x).rstrip('0').rstrip('.') if isinstance(x, (int, float)) else x) return df
[docs]def get_max_from_specific_columns(df, columns: List[str]) -> dict: """ Function to get the maximum value from specific columns in the DataFrame. Input: - df (pd.DataFrame): The DataFrame from which to get the maximum values. - columns (list of str): A list of column names to get the maximum values from. Output: - Returns dictionary with the maximum values for each specified column. """ max_values = df[columns].max() return max_values.to_dict()
[docs]def determine_folder_name(signal_name: str) -> str: """ Function determines the folder name based on the name of the signal. Input: - signal_name (str): The name of the signal to evaluate. Output: - Returns the corresponding folder name. Returns 'A12' if the signal name ends with 'v001', 'A15' if it ends with 'vXXX' with XXX>1, and 'Unusual folder name' otherwise. """ if signal_name.endswith('v001'): return 'A12' elif 'v' in signal_name: version_number = signal_name.split('v')[-1] if version_number.isdigit() and int(version_number) > 1: return 'A15' return 'Unusual folder name'
[docs]def highlight_max(s) -> List[str]: """ Highlight the maximum value in a Pandas Series. PInput: - s (pd.Series): The Pandas Series to evaluate. Output: - Returns a list of strings representing the background color for each element in the Series. The maximum value (excluding the first element) will have a background color of yellow. """ is_max = s == s[1:].max() return ['background-color: yellow' if v else '' for v in is_max]
### =================================================================================================================== ### 3. End of script ### ===================================================================================================================