### ===================================================================================================================
### 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
### ===================================================================================================================