Source code for viiapackage.general.viia_cost_key_figures

### ===================================================================================================================
###   MYVIIA handling cost key figures
### ===================================================================================================================
# Copyright ©VIIA 2024

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

# General imports
from __future__ import annotations
import os
from pathlib import Path
from datetime import date, datetime
from openpyxl import load_workbook
from typing import TYPE_CHECKING, List, Union, Dict

# References for functions and classes in the rhdhv_fem package
from rhdhv_fem.fem_tools import fem_find_object
from rhdhv_fem.shapes import Wall, Roof, Fstrip
from rhdhv_fem.fem_math import fem_greater

# References for functions and classes in the viiaPackage
if TYPE_CHECKING:
    from viiapackage.viiaStatus import ViiaProject
from viiapackage.database import myviia_get_cost, myviia_post_cost


### ===================================================================================================================
###   2. Compute the cost key figures from the model
### ===================================================================================================================

[docs]def viia_compute_cost_key_figures( project: ViiaProject, facade_list: List[Union[str, Wall]] = None) -> Dict[str, Union[float, int]]: """ This function computes the cost key figures required by the cost engineer (at request of the client). Input: - project (obj): VIIA project object containing collections of fem objects and project variables. - facade_list (list of obj): List with names or object references of the wall objects that should be considered facades of the building structure. Default value is None, the area of the facade and the opening area of the facade are not computed. Output: - Returns a dictionary containing the following information: - BVO: Area of all the modelled floors (no fstrip and roofs), excluding the openings > 5m2, in [m2]. - BBO: Area of all the modelled ground floors, in [m2]. - BGO: Area of the facade including the openings, in [m2]. - OGO: Area of the openings in the facade, in [m2]. - BBWO: Area of the inner walls including the openings, in [m2]. - BDO: Area of all the modelled roofs including the openings, in [m2]. - ODO: Area of all the openings in the modelled roofs, in [m2]. """ key_figures = {} # Calculate BVO area = 0 for floor in [floor for floor in project.collections.floors if not isinstance(floor, Roof) and not isinstance(floor, Fstrip)]: area += floor.contour.get_area() if floor.openings: for opening in floor.openings: if fem_greater(opening.get_area(), 5): area -= opening.get_area() key_figures['BVO'] = area # Calculate OBG area = 0 for floor in [floor for floor in project.collections.floors if 'N0' in floor.name]: area += floor.contour.get_area() key_figures['BBO'] = area # Calculate BGO & OGO area = 0 area_openings = 0 if facade_list: for i, wall in enumerate(facade_list): if type(wall) == str: wall = fem_find_object(wall, project.collections.walls) if wall: area += wall.contour.get_area() if wall.openings: for opening in wall.openings: area_openings += opening.get_area() else: project.write_log( f"WARNING: Wall in facadelist '{facade_list[i]}' of viia_create_cost_key_figures is not " f"recognised. Please check input.") key_figures['BGO'] = area key_figures['OGO'] = area_openings # Calculate BDO & ODO area = 0 area_openings = 0 if facade_list: for roof in project.collections.roofs: area += roof.contour.get_area() if roof.openings: for opening in roof.openings: area_openings += opening.get_area() key_figures['BDO'] = area key_figures['ODO'] = area_openings return key_figures
### =================================================================================================================== ### 3. Send cost key figures to MYVIIA ### ===================================================================================================================
[docs]def viia_send_cost_key_figures_to_myviia( project: ViiaProject, data: Dict[str, Union[float, int]]) -> Dict[str, Union[int, float, str]]: """ Function to send cost key figures to MYVIIA. It checks if there is already an existing record and if that record needs to be updated. In that case a new record is added. Input: - project (obj): VIIA project object containing collections of fem objects and project variables. - data (dict): Dictionary with the cost key figures, the keys 'BVO', 'BBO', 'BGO', 'OGO', 'BDO' and 'ODO' should be present in this dictionary. The values should be floats, in [m2]. Output: - Compares and posts new values to MYVIIA. - Notifications of the process are provided. - Returns the response of the post in case the values on MYVIIA have been updated. If not, None is returned. """ # Check if data is complete if not {'BVO', 'BBO', 'BGO', 'OGO', 'BDO', 'ODO'}.issubset(data): raise ValueError(f"ERROR: Data for excel cost key figures is incomplete: {', '.join(data.keys())}.") # Convert data for MYVIIA (lowercase keys) data = {k.lower(): v for k, v in data.items()} # Get the current information from MYVIIA myviia_data = myviia_get_cost(object_deel_id=project.get_myviia_object_deel_id(), token=project.token) # Get the date of today in isoformat current_date = f"{datetime.now().isoformat()}Z" # Check the status on MYVIIA post_response = None if not myviia_data: # No previous data on MYVIIA, create new record post_response = myviia_post_cost( data={'object_deel_id': project.get_myviia_object_deel_id(), 'date': current_date, **data}, token=project.token).json() project.write_log( f"Cost key-figures sent to MYVIIA: {', '.join([f'{k} = {round(v, 2)} m2' for k, v in data.items()])}.") # Data already present on MYVIIA else: # Get the latest record and replace None items by 0.0 myviia_data = sorted(myviia_data, key=lambda d: (d['date'], d['id']))[-1] for k, v in myviia_data.items(): if k.upper() in data and v is None: myviia_data[k] = 0.0 # Check if any value needs to be updates updating = {k: [v, myviia_data[k.lower()]] for k, v in data.items() if myviia_data[k.lower()] != v} if updating.keys(): # Update the record on MYVIIA post_response = myviia_post_cost( data={'object_deel_id': project.get_myviia_object_deel_id(), 'date': current_date, **data}, token=project.token).json() updates = [f'{k} updated from {round(v[1], 2)} m2 to {round(v[0], 2)} m2' for k, v in updating.items()] project.write_log( f"Cost key-figures updated on MYVIIA: {', '.join(updates)}.") else: project.write_log("Cost key-figures are already up-to-date on MYVIIA.") # Inform user if the response was not correct if post_response and 'resource_id' not in post_response: project.write_log( "WARNING: Some issue occurred while sending to MYVIIA. Please check on MYVIIA, " "and contact VIIA-automating team if issue persists.") # Return the response of MYVIIA if new item is posted return post_response
### =================================================================================================================== ### 4. Function to create excel file with cost key figures ### ===================================================================================================================
[docs]def viia_cost_key_figures_to_excel(project: 'ViiaProject', data: Dict[str, Union[float, int]]) -> Path: """ This function fills the Excel template with the provided data for the cost key figures. Input: - project (obj): VIIA project object containing collections of fem objects and project variables. - data (dict): Dictionary with the cost key figures, the keys 'BVO', 'BBO', 'BGO', 'OGO', 'BDO' and 'ODO' should be present in this dictionary. The values should be floats, in [m2]. Output: - Creates an Excel sheet with the values filled. The location of the file is returned as path. """ # Check if data is complete if not {'BVO', 'BBO', 'BGO', 'OGO', 'BDO', 'ODO'}.issubset(data): raise ValueError(f"ERROR: Data for excel cost key figures is incomplete: {', '.join(data.keys())}.") # Load the template for the output file in Excel cost_workbook = load_workbook( project.viia_settings.project_specific_package_location / 'general' / 'template_cost_key_figures.xlsx') cost_sheet = cost_workbook['Sheet1'] # Fill the data in the Excel, hard coded cost_sheet['C9'] = data['BVO'] cost_sheet['C11'] = data['BBO'] cost_sheet['C13'] = data['BGO'] cost_sheet['C14'] = data['OGO'] cost_sheet['C15'] = data['BDO'] cost_sheet['C16'] = data['ODO'] # Information on current run cost_sheet['C6'] = datetime.now().strftime("%d-%m-%Y | %H:%M:%S") cost_sheet['C7'] = os.getlogin() # Save the updated excel in the working directory # The default name of the Excel document is set document = f"{project.name}-v{str(project.version).zfill(3)}-COST-{date.today().strftime('%Y%m%d')}.xlsx" output_file = project.workfolder_location / document cost_workbook.save(output_file) # Notification project.write_log( f"Key-figures from fem-model are exported to excel for cost engineer VIIA.\n" f" File: {output_file.as_posix()}") return output_file
### =================================================================================================================== ### 5. End of script ### ===================================================================================================================