Complete the persistence update

test_save_result will be failing till systems are completed.
This commit is contained in:
Guille Gutierrez 2023-05-23 14:36:48 -04:00
parent 5a7427e457
commit 9f00208cbb
5 changed files with 170 additions and 52 deletions

View File

@ -377,8 +377,8 @@ class Building(CityObject):
monthly_values = PeakLoads(self).heating_peak_loads_from_methodology monthly_values = PeakLoads(self).heating_peak_loads_from_methodology
if monthly_values is None: if monthly_values is None:
return None return None
results[cte.MONTH] = pd.DataFrame(monthly_values, columns=['heating peak loads']) results[cte.MONTH] = pd.DataFrame(monthly_values, columns=[cte.HEATING_PEAK_LOAD])
results[cte.YEAR] = pd.DataFrame([max(monthly_values)], columns=['heating peak loads']) results[cte.YEAR] = pd.DataFrame([max(monthly_values)], columns=[cte.HEATING_PEAK_LOAD])
return results return results
@property @property
@ -394,8 +394,8 @@ class Building(CityObject):
monthly_values = PeakLoads(self).cooling_peak_loads_from_methodology monthly_values = PeakLoads(self).cooling_peak_loads_from_methodology
if monthly_values is None: if monthly_values is None:
return None return None
results[cte.MONTH] = pd.DataFrame(monthly_values, columns=['cooling peak loads']) results[cte.MONTH] = pd.DataFrame(monthly_values, columns=[cte.COOLING_PEAK_LOAD])
results[cte.YEAR] = pd.DataFrame([max(monthly_values)], columns=['cooling peak loads']) results[cte.YEAR] = pd.DataFrame([max(monthly_values)], columns=[cte.COOLING_PEAK_LOAD])
return results return results
@property @property

View File

@ -82,7 +82,7 @@ class InselMonthlyEnergyBalance:
if levels_of_detail.usage < 1: if levels_of_detail.usage < 1:
raise Exception(f'Level of detail of usage = {levels_of_detail.usage}. Required minimum level 1') raise Exception(f'Level of detail of usage = {levels_of_detail.usage}. Required minimum level 1')
if levels_of_detail.weather is None: if levels_of_detail.weather is None:
raise Exception(f'Level of detail of usage not assigned') raise Exception(f'Level of detail of weather not assigned')
if levels_of_detail.weather < 1: if levels_of_detail.weather < 1:
raise Exception(f'Level of detail of weather = {levels_of_detail.weather}. Required minimum level 1') raise Exception(f'Level of detail of weather = {levels_of_detail.weather}. Required minimum level 1')
if levels_of_detail.surface_radiation is None: if levels_of_detail.surface_radiation is None:

View File

@ -201,7 +201,8 @@ MIN_FLOAT = float('-inf')
# Tools # Tools
SRA = 'sra' SRA = 'sra'
INSEL_MEB = 'insel meb' INSEL_MEB = 'insel meb'
PEAK_LOAD = 'peak load' COOLING_PEAK_LOAD = f'cooling peak load'
HEATING_PEAK_LOAD = f'heating peak load'
# Costs units # Costs units
CURRENCY_PER_SQM = 'currency/m2' CURRENCY_PER_SQM = 'currency/m2'

View File

@ -30,7 +30,7 @@ class DBControl:
def application_info(self, application_uuid) -> Application: def application_info(self, application_uuid) -> Application:
""" """
Retrieve the application info for the given uuid Retrieve the application info for the given uuid from the database
:param application_uuid: the uuid for the application :param application_uuid: the uuid for the application
:return: Application :return: Application
""" """
@ -38,7 +38,7 @@ class DBControl:
def user_info(self, name, password, application_id) -> User: def user_info(self, name, password, application_id) -> User:
""" """
Retrieve the user info for the given name and password and application_id Retrieve the user info for the given name and password and application_id from the database
:param name: the username :param name: the username
:param password: the user password :param password: the user password
:param application_id: the application id :param application_id: the application id
@ -48,7 +48,7 @@ class DBControl:
def user_login(self, name, password, application_uuid) -> User: def user_login(self, name, password, application_uuid) -> User:
""" """
Retrieve the user info Retrieve the user info from the database
:param name: the username :param name: the username
:param password: the user password :param password: the user password
:param application_uuid: the application uuid :param application_uuid: the application uuid
@ -58,7 +58,7 @@ class DBControl:
def cities_by_user_and_application(self, user_id, application_id) -> [City]: def cities_by_user_and_application(self, user_id, application_id) -> [City]:
""" """
Retrieve the cities belonging to the user and the application Retrieve the cities belonging to the user and the application from the database
:param user_id: User id :param user_id: User id
:param application_id: Application id :param application_id: Application id
:return: [City] :return: [City]
@ -67,7 +67,7 @@ class DBControl:
def building_info(self, name, city_id) -> CityObject: def building_info(self, name, city_id) -> CityObject:
""" """
Retrieve the building info Retrieve the building info from the database
:param name: Building name :param name: Building name
:param city_id: City ID :param city_id: City ID
:return: CityObject :return: CityObject
@ -76,7 +76,7 @@ class DBControl:
def results(self, user_id, application_id, cities, result_names=None) -> Dict: def results(self, user_id, application_id, cities, result_names=None) -> Dict:
""" """
Retrieve the simulation results for the given cities Retrieve the simulation results for the given cities from the database
:param user_id: the user id owning the results :param user_id: the user id owning the results
:param application_id: the application id owning the results :param application_id: the application id owning the results
:param cities: dictionary containing the city and building names for the results :param cities: dictionary containing the city and building names for the results
@ -109,18 +109,18 @@ class DBControl:
def persist_city(self, city: City, pickle_path, application_id: int, user_id: int): def persist_city(self, city: City, pickle_path, application_id: int, user_id: int):
""" """
Persist city into postgres database Creates a city into the database
:param city: City to be stored :param city: City to be stored
:param pickle_path: Path to save the pickle file :param pickle_path: Path to save the pickle file
:param application_id: Application id owning this city :param application_id: Application id owning this city
:param user_id: User who create the city :param user_id: User who create the city
return identity_id return identity_id
""" """
self._city_repository.insert(city, pickle_path, application_id, user_id) return self._city_repository.insert(city, pickle_path, application_id, user_id)
def update_city(self, city_id, city): def update_city(self, city_id, city):
""" """
Update an existing city in postgres database Update an existing city in the database
:param city_id: the id of the city to update :param city_id: the id of the city to update
:param city: the updated city object :param city: the updated city object
""" """
@ -128,7 +128,7 @@ class DBControl:
def persist_application(self, name: str, description: str, application_uuid: str): def persist_application(self, name: str, description: str, application_uuid: str):
""" """
Creates an application Creates information for an application in the database
:param name: name of application :param name: name of application
:param description: the description of the application :param description: the description of the application
:param application_uuid: the uuid of the application to be created :param application_uuid: the uuid of the application to be created
@ -137,40 +137,26 @@ class DBControl:
def update_application(self, name: str, description: str, application_uuid: str): def update_application(self, name: str, description: str, application_uuid: str):
""" """
Update an application Update the application information stored in the database
:param name: name of application :param name: name of application
:param description: the description of the application :param description: the description of the application
:param application_uuid: the uuid of the application to be created :param application_uuid: the uuid of the application to be created
""" """
return self._application.update(application_uuid, name, description) return self._application.update(application_uuid, name, description)
def delete_city(self, city_id):
"""
Deletes a single city from postgres
:param city_id: the id of the city to get
"""
self._city_repository.delete(city_id)
def delete_application(self, application_uuid):
"""
Deletes a single application from postgres
:param application_uuid: the id of the application to get
"""
self._application.delete(application_uuid)
def add_simulation_results(self, name, values, city_id=None, city_object_id=None): def add_simulation_results(self, name, values, city_id=None, city_object_id=None):
""" """
Add simulation results to the city or to the city_object Add simulation results to the city or to the city_object to the database
:param name: simulation and simulation engine name :param name: simulation and simulation engine name
:param values: simulation values in json format :param values: simulation values in json format
:param city_id: city id or None :param city_id: city id or None
:param city_object_id: city object id or None :param city_object_id: city object id or None
""" """
self._simulation_results.insert(name, values, city_id, city_object_id) return self._simulation_results.insert(name, values, city_id, city_object_id)
def create_user(self, name: str, application_id: int, password: str, role: UserRoles): def create_user(self, name: str, application_id: int, password: str, role: UserRoles):
""" """
Creates a new user Creates a new user in the database
:param name: the name of the user :param name: the name of the user
:param application_id: the application id of the user :param application_id: the application id of the user
:param password: the password of the user :param password: the password of the user
@ -180,7 +166,7 @@ class DBControl:
def update_user(self, user_id: int, name: str, password: str, role: UserRoles): def update_user(self, user_id: int, name: str, password: str, role: UserRoles):
""" """
Creates a new user Updates a user in the database
:param user_id: the id of the user :param user_id: the id of the user
:param name: the name of the user :param name: the name of the user
:param password: the password of the user :param password: the password of the user
@ -188,17 +174,41 @@ class DBControl:
""" """
return self._user.update(user_id, name, password, role) return self._user.update(user_id, name, password, role)
def delete_user(self, user_id):
"""
Retrieve a single user
:param user_id: the id of the user to delete
"""
return self._user.delete(user_id)
def get_by_name_and_application(self, name: str, application: int): def get_by_name_and_application(self, name: str, application: int):
""" """
Retrieve a single user Retrieve a single user from the database
:param name: username :param name: username
:param application: application accessing hub :param application: application accessing hub
""" """
return self._user.get_by_name_and_application(name, application) return self._user.get_by_name_and_application(name, application)
def delete_user(self, user_id):
"""
Delete a single user from the database
:param user_id: the id of the user to delete
"""
self._user.delete(user_id)
def delete_city(self, city_id):
"""
Deletes a single city from the database
:param city_id: the id of the city to get
"""
self._city_repository.delete(city_id)
def delete_results_by_name(self, name, city_id=None, city_object_id=None):
"""
Deletes city object simulation results from the database
:param name: simulation name
:param city_id: if given, delete delete the results for the city with id city_id
:param city_object_id: if given, delete delete the results for the city object with id city_object_id
"""
self._simulation_results.delete(name, city_id=city_id, city_object_id=city_object_id)
def delete_application(self, application_uuid):
"""
Deletes a single application from the database
:param application_uuid: the id of the application to get
"""
self._application.delete(application_uuid)

View File

@ -4,8 +4,11 @@ SPDX - License - Identifier: LGPL - 3.0 - or -later
Copyright © 2022 Concordia CERC group Copyright © 2022 Concordia CERC group
Project Coder Peter Yefi peteryefi@gmail.com Project Coder Peter Yefi peteryefi@gmail.com
""" """
import glob
import json
import logging import logging
import os import os
import subprocess
import unittest import unittest
from unittest import TestCase from unittest import TestCase
from pathlib import Path from pathlib import Path
@ -14,14 +17,20 @@ import sqlalchemy.exc
from hub.imports.geometry_factory import GeometryFactory from hub.imports.geometry_factory import GeometryFactory
from hub.imports.construction_factory import ConstructionFactory from hub.imports.construction_factory import ConstructionFactory
from hub.imports.usage_factory import UsageFactory from hub.imports.usage_factory import UsageFactory
from hub.imports.results_factory import ResultFactory
from hub.imports.weather_factory import WeatherFactory
from hub.exports.energy_building_exports_factory import EnergyBuildingsExportsFactory
from hub.exports.exports_factory import ExportsFactory
from hub.persistence.db_control import DBControl from hub.persistence.db_control import DBControl
from hub.persistence.repository import Repository from hub.persistence.repository import Repository
from sqlalchemy import create_engine from sqlalchemy import create_engine
from hub.persistence.models import City, Application, CityObject from hub.persistence.models import City, Application, CityObject, SimulationResults
from hub.persistence.models import User, UserRoles from hub.persistence.models import User, UserRoles
from hub.helpers.dictionaries import Dictionaries from hub.helpers.dictionaries import Dictionaries
from sqlalchemy.exc import ProgrammingError from sqlalchemy.exc import ProgrammingError
import uuid import uuid
import hub.helpers.constants as cte
import distutils.spawn
class Control: class Control:
@ -59,21 +68,34 @@ class Control:
User.__table__.create(bind=repository.engine, checkfirst=True) User.__table__.create(bind=repository.engine, checkfirst=True)
City.__table__.create(bind=repository.engine, checkfirst=True) City.__table__.create(bind=repository.engine, checkfirst=True)
CityObject.__table__.create(bind=repository.engine, checkfirst=True) CityObject.__table__.create(bind=repository.engine, checkfirst=True)
SimulationResults.__table__.create(bind=repository.engine, checkfirst=True)
city_file = "tests_data/FZK_Haus_LoD_2.gml" city_file = "tests_data/FZK_Haus_LoD_2.gml"
weather_file = 'CAN_PQ_Montreal.Intl.AP.716270_CWEC.epw'
output_path = Path('./tests_outputs/').resolve()
self._city = GeometryFactory('citygml', self._city = GeometryFactory('citygml',
city_file, city_file,
function_to_hub=Dictionaries().alkis_function_to_hub_function).city function_to_hub=Dictionaries().alkis_function_to_hub_function).city
ConstructionFactory('nrcan', self._city).enrich() ConstructionFactory('nrcan', self._city).enrich()
UsageFactory('nrcan', self._city).enrich() UsageFactory('nrcan', self._city).enrich()
WeatherFactory('epw', self._city, file_name='CAN_PQ_Montreal.Intl.AP.716270_CWEC.epw').enrich()
ExportsFactory('sra', self._city, output_path, weather_file=weather_file, weather_format='epw').export()
sra_file = str((output_path / f'{self._city.name}_sra.xml').resolve())
subprocess.run([self.sra, sra_file], stdout=subprocess.DEVNULL)
ResultFactory('sra', self._city, output_path).enrich()
EnergyBuildingsExportsFactory('insel_monthly_energy_balance', self._city, output_path).export()
_insel_files = glob.glob(f'{output_path}/*.insel')
for insel_file in _insel_files:
subprocess.run([self.insel, str(insel_file)], stdout=subprocess.DEVNULL)
ResultFactory('insel_monthly_energy_balance', self._city, output_path).enrich()
self._database = DBControl( self._database = DBControl(
db_name=repository.configuration.db_name, db_name=repository.configuration.db_name,
app_env='TEST', app_env='TEST',
dotenv_path=dotenv_path) dotenv_path=dotenv_path)
self._unique_id = str(uuid.uuid4()) self._application_uuid = str(uuid.uuid4())
self._application_id = self._database.persist_application("test", "test application", self.unique_id) self._application_id = self._database.persist_application("test", "test application", self.application_uuid)
self._user_id = self._database.create_user("Admin", self._application_id, "Admin@123", UserRoles.Admin) self._user_id = self._database.create_user("Admin", self._application_id, "Admin@123", UserRoles.Admin)
self._pickle_path = 'tests_data/pickle_path.bz2' self._pickle_path = 'tests_data/pickle_path.bz2'
@ -82,8 +104,8 @@ class Control:
return self._database return self._database
@property @property
def unique_id(self): def application_uuid(self):
return self._unique_id return self._application_uuid
@property @property
def application_id(self): def application_id(self):
@ -97,6 +119,18 @@ class Control:
def skip_test(self): def skip_test(self):
return self._skip_test return self._skip_test
@property
def insel(self):
return distutils.spawn.find_executable("insel")
@property
def sra(self):
return distutils.spawn.find_executable("sra")
@property
def skip_insel_test(self):
return self.insel is None
@property @property
def skip_reason(self): def skip_reason(self):
return self._skip_reason return self._skip_reason
@ -149,3 +183,76 @@ TestDBFactory
self.assertEqual(updated_city.name, control.city.name) self.assertEqual(updated_city.name, control.city.name)
break break
control.database.delete_city(city_id) control.database.delete_city(city_id)
@unittest.skipIf(control.skip_test, control.skip_reason)
@unittest.skipIf(control.skip_insel_test, 'insel is not installed')
def test_save_results(self):
city_id = control.database.persist_city(control.city,
control.pickle_path,
control.application_id,
control.user_id)
city_objects_id = []
for building in control.city.buildings:
_building = control.database.building_info(building.name, city_id)
if cte.MONTH not in building.cooling:
print(f'building {building.name} not calculated')
continue
monthly_cooling = building.cooling[cte.MONTH][cte.INSEL_MEB]
yearly_cooling = building.cooling[cte.YEAR][cte.INSEL_MEB]
monthly_heating = building.heating[cte.MONTH][cte.INSEL_MEB]
yearly_heating = building.heating[cte.YEAR][cte.INSEL_MEB]
monthly_cooling_peak_load = building.cooling_peak_load[cte.MONTH][cte.COOLING_PEAK_LOAD]
yearly_cooling_peak_load = building.cooling_peak_load[cte.YEAR][cte.COOLING_PEAK_LOAD]
monthly_heating_peak_load = building.heating_peak_load[cte.MONTH][cte.HEATING_PEAK_LOAD]
yearly_heating_peak_load = building.heating_peak_load[cte.YEAR][cte.HEATING_PEAK_LOAD]
monthly_lighting_electrical_demand = building.lighting_electrical_demand[cte.MONTH][cte.INSEL_MEB]
yearly_lighting_electrical_demand = building.lighting_electrical_demand[cte.YEAR][cte.INSEL_MEB]
monthly_appliances_electrical_demand = building.appliances_electrical_demand[cte.MONTH][cte.INSEL_MEB]
yearly_appliances_electrical_demand = building.appliances_electrical_demand[cte.YEAR][cte.INSEL_MEB]
monthly_domestic_hot_water_heat_demand = building.domestic_hot_water_heat_demand[cte.MONTH][cte.INSEL_MEB]
yearly_domestic_hot_water_heat_demand = building.domestic_hot_water_heat_demand[cte.YEAR][cte.INSEL_MEB]
monthly_heating_consumption = building.heating_consumption[cte.MONTH][cte.INSEL_MEB]
yearly_heating_consumption = building.heating_consumption[cte.YEAR][cte.INSEL_MEB]
monthly_cooling_consumption = building.cooling_consumption[cte.MONTH][cte.INSEL_MEB]
yearly_cooling_consumption = building.cooling_consumption[cte.YEAR][cte.INSEL_MEB]
monthly_domestic_hot_water_consumption = building.domestic_hot_water_consumption[cte.MONTH][cte.INSEL_MEB]
yearly_domestic_hot_water_consumption = building._domestic_hot_water_consumption[cte.YEAR][cte.INSEL_MEB]
db_building_id = _building.id
city_objects_id.append(db_building_id)
control.database.add_simulation_results(
cte.INSEL_MEB,
json.dumps({cte.INSEL_MEB: [
{"monthly_cooling": monthly_cooling.to_json()},
{"yearly_cooling": yearly_cooling.to_json()},
{"monthly_heating": monthly_heating.to_json()},
{"yearly_heating": yearly_heating.to_json()},
{"monthly_cooling_peak_load": monthly_cooling_peak_load.to_json()},
{"yearly_cooling_peak_load": yearly_cooling_peak_load.to_json()},
{"monthly_heating_peak_load": monthly_heating_peak_load.to_json()},
{"yearly_heating_peak_load": yearly_heating_peak_load.to_json()},
{"monthly_lighting_electrical_demand": monthly_lighting_electrical_demand.to_json()},
{"yearly_lighting_electrical_demand": yearly_lighting_electrical_demand.to_json()},
{"monthly_appliances_electrical_demand": monthly_appliances_electrical_demand.to_json()},
{"yearly_appliances_electrical_demand": yearly_appliances_electrical_demand.to_json()},
{"monthly_domestic_hot_water_heat_demand": monthly_domestic_hot_water_heat_demand.to_json()},
{"yearly_domestic_hot_water_heat_demand": yearly_domestic_hot_water_heat_demand.to_json()},
{"monthly_heating_consumption": monthly_heating_consumption.to_json()},
{"yearly_heating_consumption": yearly_heating_consumption.to_json()},
{"monthly_cooling_consumption": monthly_cooling_consumption.to_json()},
{"yearly_cooling_consumption": yearly_cooling_consumption.to_json()},
{"monthly_domestic_hot_water_consumption": monthly_domestic_hot_water_consumption.to_json()},
{"yearly_domestic_hot_water_consumption": yearly_domestic_hot_water_consumption.to_json()}
]}), city_object_id=db_building_id)
self.assertEqual(1, len(city_objects_id), 'wrong number of results')
self.assertIsNotNone(city_objects_id[0], 'city_object_id is None')
for _id in city_objects_id:
control.database.delete_results_by_name('insel meb', city_object_id=_id)
control.database.delete_city(city_id)
@classmethod
def tearDownClass(cls):
control.database.delete_application(control.application_uuid)
control.database.delete_user(control.user_id)