Source code for viiapackage.reference_approach.viia_convert_data_eng_database

### ===================================================================================================================
###   Helper class to collect data from MYVIIA for the reference object
### ===================================================================================================================
# Copyright ©VIIA 2024

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

# General imports
from typing import List, Optional, Union
from datetime import datetime, date
from dataclasses import dataclass

# References for functions and classes in the rhdhv_fem package
from rhdhv_fem.fem_math import fem_compare_values, fem_smaller, fem_greater

# References for functions and classes in the viiaPackage
from viiapackage.viiaSettings import ViiaSettings


### ===================================================================================================================
###   2. CLASS MYVIIAEngDatabase
### ===================================================================================================================

[docs]@dataclass class MYVIIAEngDatabase: """ Helper class for storing the data from MYVIIA.""" id: int cluster: str = False cluster_main: str = False construction_year: int = False material_load_walls: str = False most_floor_height: float = False most_thickness_load_walls: float = False nr_levels: int = False objectnumber_viia: str = False objectpart: str = False objectpart_id: int = False other_floors_material: str = False pga: float = False rekenmethodiek: str = False status: str = False tva_sent: date = False current_object = None consequence_class: str = False bg_floor_material: list = False roof_material: list = False building_height: float = False foundation_type: str = False measures: list = None total_cost: Union[str, float] = None box_link: str = None dat_file_link: str = None
[docs] @staticmethod def from_myviia(dictionary): """ Method of 'MYVIIAEngDatabase' to convert data from the endpoint to the flat object description required for the reference approach and scoring.""" check_keys = [ 'id', 'material_load_walls', 'most_floor_height', 'most_thickness_load_walls', 'nr_levels', 'other_floors_material', 'pga', 'tva_sent', 'building_height', 'roof_material', 'bg_floor_material'] inputs = {key: dictionary[key] for key in check_keys if key in dictionary} if dictionary['object_deel']['rekenmethodiek_id'] is None: inputs['rekenmethodiek'] = 'Unknown' else: inputs['rekenmethodiek'] = dictionary['object_deel']['rekenmethodiek_id']['subtype'] inputs['objectnumber_viia'] = dictionary['object_deel']['viia_object_id']['objectnummer_viia'] inputs['cluster_main'] = dictionary['object_deel']['viia_object_id']['object_cluster_id']['hoofdgroep'] inputs['cluster'] = dictionary['object_deel']['viia_object_id']['object_cluster_id']['cluster'] inputs['status'] = dictionary['object_deel']['viia_object_id']['object_status_id']['status'] inputs['construction_year'] = dictionary['object_deel']['viia_object_id']['oorspronkelijk_bouwjaar'] inputs['consequence_class'] = dictionary['object_deel']['gevolgklasse'] inputs['box_link'] = dictionary['object_deel']['viia_object_id']['boxmap'] inputs['dat_file_link'] = dictionary['object_deel']['link_rekenmodel'] if inputs['construction_year']: inputs['construction_year'] = int( inputs['construction_year'].lower().replace('ca', '').replace('.', '').replace(' ', ''). replace('<', '').replace('>', '')) inputs['objectpart'] = dictionary['object_deel']['naam'] inputs['objectpart_id'] = dictionary['object_deel_id'] return MYVIIAEngDatabase(**inputs)
@property def importance_factor(self) -> Optional[float]: if self.consequence_class: return ViiaSettings.IMPORTANCE_FACTORS[self.consequence_class] return None @property def design_pga(self) -> Optional[float]: if self.pga and self.importance_factor: return self.importance_factor * self.pga return None @property def is_recent(self) -> bool: """ Method of 'MYVIIAEngDatabase' to check if the object is finished after 1st January 2019.""" if self.tva_sent: if self.tva_sent >= date(2019, 1, 1): return True return False @property def score_cluster(self) -> int: """ Method of 'MYVIIAEngDatabase' to calculate score for the aspect of cluster.""" if self.cluster_main and self.cluster_main == self.current_object.cluster_main: return 100 return 0 @property def score_construction_year(self) -> float: """ Method of 'MYVIIAEngDatabase' to calculate score for the aspect of construction year.""" if self.construction_year and self.construction_year <= self.current_object.construction_year: return 100 return 0 @property def score_most_floor_height(self) -> float: """ Method of 'MYVIIAEngDatabase' to calculate score for the aspect of most common floor height which implies the most common wall height.""" if self.most_floor_height: if fem_smaller(abs(self.most_floor_height - self.current_object.most_floor_height), 0.5): return 100 elif fem_smaller(abs(self.most_floor_height - self.current_object.most_floor_height), 1): return 70 return 0 def get_material_score(self, material: str): mat_rank = [ 'concrete', 'clay masonry', 'calcium silicate > 1985', 'calcium silicate > 1960', 'aerated concrete', 'timber', 'modification wall'] ref_mat = material if 'clay masonry' in material: ref_mat = 'clay masonry' elif 'HSB' in material: ref_mat = 'timber' if ref_mat == self.current_object.material_load_walls: return 100 if mat_rank.index(ref_mat) - mat_rank.index(self.current_object.material_load_walls) == 1: return 70 elif mat_rank.index(ref_mat) - mat_rank.index(self.current_object.material_load_walls) == 2: return 40 return 0 @property def score_material_load_walls(self) -> float: """ Method of 'MYVIIAEngDatabase' to calculate score for the aspect of most common load bearing walls. """ if self.material_load_walls: if isinstance(self.material_load_walls, str): return self.get_material_score(material=self.material_load_walls) elif isinstance(self.material_load_walls, list): scores = [] for material in self.material_load_walls: scores.append(self.get_material_score(material=material)) return max(scores) return 0 @property def score_most_thickness_load_walls(self) -> float: """ Method of 'MYVIIAEngDatabase' to calculate score for the aspect of the thickness of load bearing walls.""" if self.most_thickness_load_walls: if fem_compare_values( value1=self.current_object.most_thickness_load_walls, value2=self.most_thickness_load_walls, precision=3): return 100 if self.current_object.most_thickness_load_walls - 20 <= self.most_thickness_load_walls <= \ self.current_object.most_thickness_load_walls + 20: return 90 return 0 @property def score_nr_levels(self): """ Method of 'MYVIIAEngDatabase' to calculate score for the aspect of number of story levels.""" if self.nr_levels and self.nr_levels == self.current_object.nr_levels: return 100 return 0 @property def score_other_floors_material(self) -> float: """ Method of 'MYVIIAEngDatabase' to calculate score for the aspect of floor material.""" if self.other_floors_material and self.other_floors_material == self.current_object.other_floors_material: return 100 return 0 @property def score_pga(self) -> float: """ Method of 'MYVIIAEngDatabase' to calculate score for the aspect of the pga of the address.""" if self.design_pga and self.importance_factor: if fem_smaller(self.design_pga, 0.9 * self.current_object.design_pga): return 0 if fem_smaller(0.9 * self.current_object.design_pga, self.design_pga) and fem_smaller(self.design_pga, self.current_object.design_pga): return 1000*(self.design_pga - 0.9 * self.current_object.design_pga)/self.current_object.design_pga if fem_smaller(self.current_object.design_pga, self.design_pga) and fem_smaller(self.design_pga, 1.5 * self.current_object.design_pga): return 100 if fem_smaller(1.5 * self.current_object.design_pga, self.design_pga) and fem_smaller(self.design_pga, 2 * self.current_object.design_pga): return 200*(2 * self.current_object.design_pga - self.design_pga)/self.current_object.design_pga if fem_smaller(2 * self.current_object.design_pga, self.design_pga): return 0 return 0 @property def score(self) -> float: """ Method of 'MYVIIAEngDatabase' to score if the object can be used as reference.""" return ( 0.222 * self.score_cluster + 0.194 * self.score_nr_levels + 0.167 * self.score_pga + 0.139 * self.score_material_load_walls + 0.111 * self.score_other_floors_material + 0.083 * self.score_most_thickness_load_walls + 0.056 * self.score_construction_year + 0.028 * self.score_most_floor_height)
### =================================================================================================================== ### 3. Helper functions for the conversions ### =================================================================================================================== def _convert_date(str_date: str) -> Optional[date]: """ Convert date from string (2021-11-16) to datetime object.""" if str_date and str_date != '0000-00-00': return datetime.strptime(str_date, '%Y-%m-%d').date() return None ### =================================================================================================================== ### 4. Function to convert the data from MYVIIA for MYVIIAEngDatabase instances ### ===================================================================================================================
[docs]def viia_convert_data_eng_database(data: List[dict]) -> List[MYVIIAEngDatabase]: """ Function to convert the raw data from MYVIIA to instances of the MYVIIAEngDatabase helper-class which is used for the comparison of the reference object. The list of records is filtered for 'NLTH' analyses finished after 1st January 2019. Input: - data (list of dict): Raw input from the MYVIIA endpoint for the engineering database. Format of the dictionary is set in MYVIIA. Output: - Returns list of instances of MYVIIAEngDatabase with references valid to be used in the reference approach. """ # Update the input for the date and tva finished date for item in data: item['date'] = _convert_date(item['date']) item['tva_sent'] = None for deliverable in item['object_deel']['deliverables']: if deliverable['deliverable_id']['deliverable'] == 'TVA': item['tva_sent'] = _convert_date(deliverable['datum_verstuurd']) break # Filter for multiple entries of the same objectpart by latest date myviia_data = [] for object_id in set([item['object_deel_id'] for item in data]): items = [item for item in data if item['object_deel_id'] == object_id] myviia_data.append(sorted(items, key=lambda d: d['date'])[-1]) # Create the instances myviia_data = [MYVIIAEngDatabase.from_myviia(record) for record in myviia_data] # Only use NLTH objects in the pre-selection that are finished after 1st January 2019 return [ database_object for database_object in myviia_data if database_object.rekenmethodiek == 'NLTH' and database_object.is_recent]
### =================================================================================================================== ### 5. End of script ### ===================================================================================================================