Compare commits

..

No commits in common. "main" and "v0.1.0.2" have entirely different histories.

22 changed files with 481 additions and 406 deletions

View File

@ -20,6 +20,11 @@ class Configuration:
""" """
def __init__(self, db_name: str, dotenv_path: str, app_env='TEST'): def __init__(self, db_name: str, dotenv_path: str, app_env='TEST'):
"""
:param db_name: database name
:param app_env: application environment, test or production
:param dotenv_path: the absolute path to dotenv file
"""
try: try:
# load environmental variables # load environmental variables
if not Path(dotenv_path).exists(): if not Path(dotenv_path).exists():

View File

@ -236,13 +236,6 @@ class DBControl:
def delete_application(self, application_uuid): def delete_application(self, application_uuid):
""" """
Deletes a single application from the database Deletes a single application from the database
:param application_uuid: the id of the application to delete :param application_uuid: the id of the application to get
""" """
self._application.delete(application_uuid) self._application.delete(application_uuid)
def get_city(self, city_id):
"""
Get a single city by id
:param city_id: the id of the city to get
"""
return self._city.get_by_id(city_id)

View File

@ -23,6 +23,14 @@ class DBSetup:
""" """
def __init__(self, db_name, app_env, dotenv_path, admin_password, application_uuid): def __init__(self, db_name, app_env, dotenv_path, admin_password, application_uuid):
"""
Creates database tables a default admin user and a default admin app with the given password and uuid
:param db_name: database name
:param app_env: application environment type [TEST|PROD]
:param dotenv_path: .env file path
:param admin_password: administrator password for the application uuid
:application_uuid: application uuid
"""
repository = Repository(db_name=db_name, app_env=app_env, dotenv_path=dotenv_path) repository = Repository(db_name=db_name, app_env=app_env, dotenv_path=dotenv_path)
# Create the tables using the models # Create the tables using the models

View File

@ -16,7 +16,7 @@ from cerc_persistence.configuration import Models
class Application(Models): class Application(Models):
""" """
Application(Models) class A model representation of an application
""" """
__tablename__ = 'application' __tablename__ = 'application'
id = Column(Integer, Sequence('application_id_seq'), primary_key=True) id = Column(Integer, Sequence('application_id_seq'), primary_key=True)

View File

@ -14,8 +14,7 @@ from cerc_persistence.configuration import Models
class City(Models): class City(Models):
""" """A model representation of a city
City(Models) class
""" """
__tablename__ = 'city' __tablename__ = 'city'
id = Column(Integer, Sequence('city_id_seq'), primary_key=True) id = Column(Integer, Sequence('city_id_seq'), primary_key=True)

View File

@ -17,7 +17,7 @@ from cerc_persistence.configuration import Models
class CityObject(Models): class CityObject(Models):
""" """
CityObject(Models) class A model representation of an application
""" """
__tablename__ = 'city_object' __tablename__ = 'city_object'
id = Column(Integer, Sequence('city_object_id_seq'), primary_key=True) id = Column(Integer, Sequence('city_object_id_seq'), primary_key=True)

View File

@ -15,7 +15,7 @@ from cerc_persistence.configuration import Models
class SimulationResults(Models): class SimulationResults(Models):
""" """
SimulationResults(Models) class A model representation of an application
""" """
__tablename__ = 'simulation_results' __tablename__ = 'simulation_results'
id = Column(Integer, Sequence('simulation_results_id_seq'), primary_key=True) id = Column(Integer, Sequence('simulation_results_id_seq'), primary_key=True)

View File

@ -24,7 +24,7 @@ class UserRoles(enum.Enum):
class User(Models): class User(Models):
""" """
User(Models) class A model representation of a city
""" """
__tablename__ = 'user' __tablename__ = 'user'
id = Column(Integer, Sequence('user_id_seq'), primary_key=True) id = Column(Integer, Sequence('user_id_seq'), primary_key=True)

View File

@ -18,7 +18,7 @@ from cerc_persistence.models import Application as Model
class Application(Repository): class Application(Repository):
""" """
Application(Repository) class Application repository
""" """
_instance = None _instance = None
@ -26,6 +26,9 @@ class Application(Repository):
super().__init__(db_name, dotenv_path, app_env) super().__init__(db_name, dotenv_path, app_env)
def __new__(cls, db_name, dotenv_path, app_env): def __new__(cls, db_name, dotenv_path, app_env):
"""
Implemented for a singleton pattern
"""
if cls._instance is None: if cls._instance is None:
cls._instance = super(Application, cls).__new__(cls) cls._instance = super(Application, cls).__new__(cls)
return cls._instance return cls._instance

View File

@ -20,7 +20,7 @@ from cerc_persistence.models import CityObject
class City(Repository): class City(Repository):
""" """
City(Repository) class City repository
""" """
_instance = None _instance = None
@ -28,6 +28,9 @@ class City(Repository):
super().__init__(db_name, dotenv_path, app_env) super().__init__(db_name, dotenv_path, app_env)
def __new__(cls, db_name, dotenv_path, app_env): def __new__(cls, db_name, dotenv_path, app_env):
"""
Implemented for a singleton pattern
"""
if cls._instance is None: if cls._instance is None:
cls._instance = super(City, cls).__new__(cls) cls._instance = super(City, cls).__new__(cls)
return cls._instance return cls._instance
@ -133,7 +136,3 @@ class City(Repository):
except SQLAlchemyError as err: except SQLAlchemyError as err:
logging.error('Error while fetching city by name %s', err) logging.error('Error while fetching city by name %s', err)
raise SQLAlchemyError from err raise SQLAlchemyError from err
def get_by_id(self, city_id) -> Model:
with Session(self.engine) as session:
return session.execute(select(Model).where(Model.id == city_id)).first()[0]

View File

@ -19,7 +19,7 @@ from cerc_persistence.repository import Repository
class CityObject(Repository): class CityObject(Repository):
""" """
CityObject(Repository) class City object repository
""" """
_instance = None _instance = None
@ -27,6 +27,9 @@ class CityObject(Repository):
super().__init__(db_name, dotenv_path, app_env) super().__init__(db_name, dotenv_path, app_env)
def __new__(cls, db_name, dotenv_path, app_env): def __new__(cls, db_name, dotenv_path, app_env):
"""
Implemented for a singleton pattern
"""
if cls._instance is None: if cls._instance is None:
cls._instance = super(CityObject, cls).__new__(cls) cls._instance = super(CityObject, cls).__new__(cls)
return cls._instance return cls._instance

View File

@ -20,7 +20,7 @@ from cerc_persistence.models import SimulationResults as Model
class SimulationResults(Repository): class SimulationResults(Repository):
""" """
SimulationResults(Repository) class Simulation results repository
""" """
_instance = None _instance = None
@ -28,6 +28,9 @@ class SimulationResults(Repository):
super().__init__(db_name, dotenv_path, app_env) super().__init__(db_name, dotenv_path, app_env)
def __new__(cls, db_name, dotenv_path, app_env): def __new__(cls, db_name, dotenv_path, app_env):
"""
Implemented for a singleton pattern
"""
if cls._instance is None: if cls._instance is None:
cls._instance = super(SimulationResults, cls).__new__(cls) cls._instance = super(SimulationResults, cls).__new__(cls)
return cls._instance return cls._instance

View File

@ -18,7 +18,7 @@ from cerc_persistence.models import User as Model, Application as ApplicationMod
class User(Repository): class User(Repository):
""" """
User(Repository) class User class
""" """
_instance = None _instance = None
@ -26,6 +26,9 @@ class User(Repository):
super().__init__(db_name, dotenv_path, app_env) super().__init__(db_name, dotenv_path, app_env)
def __new__(cls, db_name, dotenv_path, app_env): def __new__(cls, db_name, dotenv_path, app_env):
"""
Implemented for a singleton pattern
"""
if cls._instance is None: if cls._instance is None:
cls._instance = super(User, cls).__new__(cls) cls._instance = super(User, cls).__new__(cls)
return cls._instance return cls._instance

View File

@ -1,4 +1,4 @@
""" """
CERC Persistence version number CERC Persistence version number
""" """
__version__ = '1.0.0.2' __version__ = '0.1.0.2'

View File

@ -20,13 +20,8 @@ with open(version) as f:
setup( setup(
name='cerc-persistence', name='cerc-persistence',
version=main_ns['__version__'], version=main_ns['__version__'],
description="CERC Persistence consist of a set of classes to store and retrieve Cerc Hub cities and results", description="",
long_description="CERC Persistence consist of a set of classes to store and retrieve Cerc Hub cities and results.\n\n" long_description="",
"Developed at Concordia university in Canada as part of the research group from the Next Generation "
"Cities Institute, our aim among others is to provide a comprehensive set of tools to help "
"researchers and urban developers to make decisions to improve the livability and efficiency of our "
"cities, cerc persistence will store the simulation results and city information to make those "
"available for other researchers.",
classifiers=[ classifiers=[
"License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)", "License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)",
"Programming Language :: Python", "Programming Language :: Python",

302
tests/test_db_factory.py Normal file
View File

@ -0,0 +1,302 @@
"""
Test db factory
SPDX - License - Identifier: LGPL - 3.0 - or -later
Copyright © 2022 Concordia CERC group
Project Coder Peter Yefi peteryefi@gmail.com
"""
import distutils.spawn
import glob
import json
import logging
import os
import subprocess
import unittest
from pathlib import Path
from unittest import TestCase
import psycopg2
import sqlalchemy.exc
from sqlalchemy import create_engine
from sqlalchemy.exc import ProgrammingError
from sqlalchemy_utils import database_exists, create_database, drop_database
import hub.helpers.constants as cte
from hub.exports.energy_building_exports_factory import EnergyBuildingsExportsFactory
from hub.exports.exports_factory import ExportsFactory
from hub.helpers.data.montreal_function_to_hub_function import MontrealFunctionToHubFunction
from hub.imports.construction_factory import ConstructionFactory
from hub.imports.energy_systems_factory import EnergySystemsFactory
from hub.imports.geometry_factory import GeometryFactory
from hub.imports.results_factory import ResultFactory
from hub.imports.usage_factory import UsageFactory
from hub.imports.weather_factory import WeatherFactory
from cerc_persistence.db_control import DBControl
from cerc_persistence.models import City, Application, CityObject, SimulationResults
from cerc_persistence.models import User, UserRoles
from cerc_persistence.repository import Repository
class Control:
_skip_test = False
_skip_reason = 'PostgreSQL not properly installed in host machine'
def __init__(self):
"""
Test
setup
:return: None
"""
self._skip_test = False
# Create test database
dotenv_path = Path("{}/.local/etc/hub/.env".format(os.path.expanduser('~'))).resolve()
if not dotenv_path.exists():
self._skip_test = True
self._skip_reason = f'.env file missing at {dotenv_path}'
return
dotenv_path = str(dotenv_path)
repository = Repository(db_name='test_db', app_env='TEST', dotenv_path=dotenv_path)
engine = create_engine(repository.configuration.connection_string)
if database_exists(engine.url):
drop_database(engine.url)
create_database(engine.url)
Application.__table__.create(bind=engine, checkfirst=True)
User.__table__.create(bind=engine, checkfirst=True)
City.__table__.create(bind=engine, checkfirst=True)
CityObject.__table__.create(bind=engine, checkfirst=True)
SimulationResults.__table__.create(bind=engine, checkfirst=True)
city_file = Path('tests_data/test.geojson').resolve()
output_path = Path('tests_outputs/').resolve()
self._city = GeometryFactory('geojson',
city_file,
height_field='citygml_me',
year_of_construction_field='ANNEE_CONS',
aliases_field=['ID_UEV', 'CIVIQUE_DE', 'NOM_RUE'],
function_field='CODE_UTILI',
function_to_hub=MontrealFunctionToHubFunction().dictionary).city
ConstructionFactory('nrcan', self._city).enrich()
UsageFactory('nrcan', self._city).enrich()
WeatherFactory('epw', self._city).enrich()
ExportsFactory('sra', self._city, output_path).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()
for building in self._city.buildings:
building.energy_systems_archetype_name = 'system 1 gas pv'
EnergySystemsFactory('montreal_custom', self._city).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(
db_name=repository.configuration.db_name,
app_env='TEST',
dotenv_path=dotenv_path)
self._application_uuid = 'b9e0ce80-1218-410c-8a64-9d9b7026aad8'
self._application_id = 1
self._user_id = 1
try:
self._application_id = self._database.persist_application(
'test',
'test',
self.application_uuid
)
except sqlalchemy.exc.SQLAlchemyError:
self._application_id = self._database.application_info(self.application_uuid).id
try:
self._user_id = self._database.create_user('test', self._application_id, 'test', UserRoles.Admin)
except sqlalchemy.exc.SQLAlchemyError:
self._user_id = self._database.user_info(name='test', password='test', application_id=self._application_id).id
self._pickle_path = Path('tests_data/pickle_path.bz2').resolve()
@property
def database(self):
return self._database
@property
def application_uuid(self):
return self._application_uuid
@property
def application_id(self):
return self._application_id
@property
def user_id(self):
return self._user_id
@property
def skip_test(self):
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
def skip_reason(self):
return self._skip_reason
@property
def message(self):
return self._skip_reason
@property
def city(self):
return self._city
@property
def pickle_path(self):
return self._pickle_path
control = Control()
class TestDBFactory(TestCase):
"""
TestDBFactory
"""
@unittest.skipIf(control.skip_test, control.skip_reason)
def test_save_city(self):
control.city.name = "Montreal"
city_id = control.database.persist_city(
control.city,
control.pickle_path,
control.city.name,
control.application_id,
control.user_id)
control.database.delete_city(city_id)
@unittest.skipIf(control.skip_test, control.skip_reason)
def test_get_update_city(self):
city_id = control.database.persist_city(control.city,
control.pickle_path,
control.city.name,
control.application_id,
control.user_id)
control.city.name = "Ottawa"
control.database.update_city(city_id, control.city)
cities = control.database.cities_by_user_and_application(
control.user_id,
control.application_id)
for updated_city in cities:
if updated_city.id == city_id:
self.assertEqual(updated_city.name, control.city.name)
break
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,
'current status',
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_demand:
print(f'building {building.name} not calculated')
continue
monthly_cooling_peak_load = building.cooling_peak_load[cte.MONTH]
yearly_cooling_peak_load = building.cooling_peak_load[cte.YEAR]
monthly_heating_peak_load = building.heating_peak_load[cte.MONTH]
yearly_heating_peak_load = building.heating_peak_load[cte.YEAR]
monthly_lighting_peak_load = building.lighting_peak_load[cte.MONTH]
yearly_lighting_peak_load = building.lighting_peak_load[cte.YEAR]
monthly_appliances_peak_load = building.appliances_peak_load[cte.MONTH]
yearly_appliances_peak_load = building.appliances_peak_load[cte.YEAR]
monthly_cooling_demand = building.cooling_demand[cte.MONTH]
yearly_cooling_demand = building.cooling_demand[cte.YEAR]
monthly_heating_demand = building.heating_demand[cte.MONTH]
yearly_heating_demand = building.heating_demand[cte.YEAR]
monthly_lighting_electrical_demand = building.lighting_electrical_demand[cte.MONTH]
yearly_lighting_electrical_demand = building.lighting_electrical_demand[cte.YEAR]
monthly_appliances_electrical_demand = building.appliances_electrical_demand[cte.MONTH]
yearly_appliances_electrical_demand = building.appliances_electrical_demand[cte.YEAR]
monthly_domestic_hot_water_heat_demand = building.domestic_hot_water_heat_demand[cte.MONTH]
yearly_domestic_hot_water_heat_demand = building.domestic_hot_water_heat_demand[cte.YEAR]
monthly_heating_consumption = building.heating_consumption[cte.MONTH]
yearly_heating_consumption = building.heating_consumption[cte.YEAR]
monthly_cooling_consumption = building.cooling_consumption[cte.MONTH]
yearly_cooling_consumption = building.cooling_consumption[cte.YEAR]
monthly_domestic_hot_water_consumption = building.domestic_hot_water_consumption[cte.MONTH]
yearly_domestic_hot_water_consumption = building._domestic_hot_water_consumption[cte.YEAR]
monthly_distribution_systems_electrical_consumption = building.distribution_systems_electrical_consumption[
cte.MONTH]
yearly_distribution_systems_electrical_consumption = building.distribution_systems_electrical_consumption[
cte.YEAR]
monthly_on_site_electrical_production = [x * cte.WATTS_HOUR_TO_JULES
for x in building.onsite_electrical_production[cte.MONTH]]
yearly_on_site_electrical_production = [x * cte.WATTS_HOUR_TO_JULES
for x in building.onsite_electrical_production[cte.YEAR]]
results = json.dumps({cte.INSEL_MEB: [
{'monthly_cooling_peak_load': monthly_cooling_peak_load},
{'yearly_cooling_peak_load': yearly_cooling_peak_load},
{'monthly_heating_peak_load': monthly_heating_peak_load},
{'yearly_heating_peak_load': yearly_heating_peak_load},
{'monthly_lighting_peak_load': monthly_lighting_peak_load},
{'yearly_lighting_peak_load': yearly_lighting_peak_load},
{'monthly_appliances_peak_load': monthly_appliances_peak_load},
{'yearly_appliances_peak_load': yearly_appliances_peak_load},
{'monthly_cooling_demand': monthly_cooling_demand},
{'yearly_cooling_demand': yearly_cooling_demand},
{'monthly_heating_demand': monthly_heating_demand},
{'yearly_heating_demand': yearly_heating_demand},
{'monthly_lighting_electrical_demand': monthly_lighting_electrical_demand},
{'yearly_lighting_electrical_demand': yearly_lighting_electrical_demand},
{'monthly_appliances_electrical_demand': monthly_appliances_electrical_demand},
{'yearly_appliances_electrical_demand': yearly_appliances_electrical_demand},
{'monthly_domestic_hot_water_heat_demand': monthly_domestic_hot_water_heat_demand},
{'yearly_domestic_hot_water_heat_demand': yearly_domestic_hot_water_heat_demand},
{'monthly_heating_consumption': monthly_heating_consumption},
{'yearly_heating_consumption': yearly_heating_consumption},
{'monthly_cooling_consumption': monthly_cooling_consumption},
{'yearly_cooling_consumption': yearly_cooling_consumption},
{'monthly_domestic_hot_water_consumption': monthly_domestic_hot_water_consumption},
{'yearly_domestic_hot_water_consumption': yearly_domestic_hot_water_consumption},
{'monthly_distribution_systems_electrical_consumption': monthly_distribution_systems_electrical_consumption},
{'yearly_distribution_systems_electrical_consumption': yearly_distribution_systems_electrical_consumption},
{'monthly_on_site_electrical_production': monthly_on_site_electrical_production},
{'yearly_on_site_electrical_production': yearly_on_site_electrical_production}
]})
db_building_id = _building.id
city_objects_id.append(db_building_id)
control.database.add_simulation_results(
cte.INSEL_MEB,
results, city_object_id=db_building_id)
self.assertEqual(17, 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
@unittest.skipIf(control.skip_test, control.skip_reason)
def tearDownClass(cls):
control.database.delete_application(control.application_uuid)
control.database.delete_user(control.user_id)
os.unlink(control.pickle_path)
output_files = glob.glob('./tests_outputs/*')
for output_file in output_files:
os.unlink(output_file)

128
tests/test_db_retrieve.py Normal file
View File

@ -0,0 +1,128 @@
"""
Test db retrieve
SPDX - License - Identifier: LGPL - 3.0 - or -later
Copyright © 2022 Concordia CERC group
Project Coder Ruben Sanchez ruben.sanchez@mail.concordia.ca
"""
import datetime
import distutils.spawn
import logging
import os
import unittest
from pathlib import Path
from unittest import TestCase
import sqlalchemy.exc
from sqlalchemy import create_engine
from sqlalchemy.exc import ProgrammingError
from cerc_persistence.db_control import DBControl
from cerc_persistence.repository import Repository
class Control:
_skip_test = False
_skip_reason = 'PostgreSQL not properly installed in host machine'
def __init__(self):
"""
Test
setup
:return: None
"""
self._skip_test = False
# Create test database
dotenv_path = Path("{}/.local/etc/hub/.env".format(os.path.expanduser('~'))).resolve()
if not dotenv_path.exists():
self._skip_test = True
self._skip_reason = f'.env file missing at {dotenv_path}'
return
dotenv_path = str(dotenv_path)
repository = Repository(db_name='montreal_retrofit_test', app_env='TEST', dotenv_path=dotenv_path)
engine = create_engine(repository.configuration.connection_string)
try:
# delete test database if it exists
connection = engine.connect()
connection.close()
except ProgrammingError:
logging.info('Database does not exist. Nothing to delete')
except sqlalchemy.exc.OperationalError as operational_error:
self._skip_test = True
self._skip_reason = f'{operational_error}'
return
self._database = DBControl(
db_name=repository.configuration.db_name,
app_env='TEST',
dotenv_path=dotenv_path)
self._application_uuid = '60b7fc1b-f389-4254-9ffd-22a4cf32c7a3'
self._application_id = 1
self._user_id = 1
self._pickle_path = 'tests_data/pickle_path.bz2'
@property
def database(self):
return self._database
@property
def application_uuid(self):
return self._application_uuid
@property
def application_id(self):
return self._application_id
@property
def user_id(self):
return self._user_id
@property
def skip_test(self):
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
def skip_reason(self):
return self._skip_reason
@property
def message(self):
return self._skip_reason
@property
def pickle_path(self):
return self._pickle_path
control = Control()
class TestDBFactory(TestCase):
"""
TestDBFactory
"""
@unittest.skipIf(control.skip_test, control.skip_reason)
def test_retrieve_results(self):
datetime.datetime.now()
request_values = {
"scenarios": [
{"current status": ["01002777", "01002773", "01036804"]},
{"skin retrofit": ["01002777", "01002773", "01036804"]},
{"system retrofit and pv": ["01002777", "01002773", "01036804"]},
{"skin and system retrofit with pv": ["01002777", "01002773", "01036804"]}
]
}
results = control.database.results(control.user_id, control.application_id, request_values)
print(results)

View File

@ -1,171 +0,0 @@
"""
Test control
SPDX - License - Identifier: LGPL - 3.0 - or -later
Copyright © 2022 Concordia CERC group
Project Coder Guille Gutierrez <guillermo.gutierrezmorote@concordia.ca
"""
import distutils.spawn
import glob
import os
import subprocess
from pathlib import Path
import sqlalchemy
import sqlalchemy.exc
from hub.exports.energy_building_exports_factory import EnergyBuildingsExportsFactory
from hub.exports.exports_factory import ExportsFactory
from hub.helpers.data.montreal_function_to_hub_function import MontrealFunctionToHubFunction
from hub.imports.construction_factory import ConstructionFactory
from hub.imports.energy_systems_factory import EnergySystemsFactory
from hub.imports.geometry_factory import GeometryFactory
from hub.imports.results_factory import ResultFactory
from hub.imports.usage_factory import UsageFactory
from hub.imports.weather_factory import WeatherFactory
from sqlalchemy import create_engine
from sqlalchemy_utils import database_exists, create_database, drop_database
from cerc_persistence.db_control import DBControl
from cerc_persistence.models import City, Application, CityObject, SimulationResults, UserRoles
from cerc_persistence.models import User
from cerc_persistence.repository import Repository
class Control:
_skip_test = False
_skip_reason = 'PostgreSQL not properly installed in host machine'
def __init__(self):
"""
Test
setup
:return: None
"""
self._skip_test = False
# Create test database
dotenv_path = Path("{}/.local/etc/hub/.env".format(os.path.expanduser('~'))).resolve()
if not dotenv_path.exists():
self._skip_test = True
self._skip_reason = f'.env file missing at {dotenv_path}'
return
dotenv_path = str(dotenv_path)
repository = Repository(db_name='persistence_test_db', app_env='TEST', dotenv_path=dotenv_path)
engine = create_engine(repository.configuration.connection_string)
if database_exists(engine.url):
drop_database(engine.url)
create_database(engine.url)
Application.__table__.create(bind=engine, checkfirst=True)
User.__table__.create(bind=engine, checkfirst=True)
City.__table__.create(bind=engine, checkfirst=True)
CityObject.__table__.create(bind=engine, checkfirst=True)
SimulationResults.__table__.create(bind=engine, checkfirst=True)
self._database = DBControl(
db_name=repository.configuration.db_name,
app_env='TEST',
dotenv_path=dotenv_path)
example_path = (Path(__file__).parent / 'tests_data').resolve()
city_file = (example_path / 'test.geojson').resolve()
output_path = (Path(__file__).parent / 'tests_outputs').resolve()
self._application_uuid = '60b7fc1b-f389-4254-9ffd-22a4cf32c7a3'
self._application_id = 1
self._user_id = 1
self._pickle_path = 'tests_data/pickle_path.bz2'
self._city = GeometryFactory('geojson',
city_file,
height_field='citygml_me',
year_of_construction_field='ANNEE_CONS',
aliases_field=['ID_UEV', 'CIVIQUE_DE', 'NOM_RUE'],
function_field='CODE_UTILI',
function_to_hub=MontrealFunctionToHubFunction().dictionary).city
ConstructionFactory('nrcan', self._city).enrich()
UsageFactory('nrcan', self._city).enrich()
WeatherFactory('epw', self._city).enrich()
ExportsFactory('sra', self._city, output_path).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()
for building in self._city.buildings:
building.energy_systems_archetype_name = 'system 1 gas pv'
EnergySystemsFactory('montreal_custom', self._city).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(
db_name=repository.configuration.db_name,
app_env='TEST',
dotenv_path=dotenv_path)
self._application_uuid = 'b9e0ce80-1218-410c-8a64-9d9b7026aad8'
self._application_id = 1
self._user_id = 1
try:
self._application_id = self._database.persist_application(
'test',
'test',
self.application_uuid
)
except sqlalchemy.exc.SQLAlchemyError:
self._application_id = self._database.application_info(self.application_uuid).id
try:
self._user_id = self._database.create_user('test', self._application_id, 'test', UserRoles.Admin)
except sqlalchemy.exc.SQLAlchemyError:
self._user_id = self._database.user_info(name='test', password='test', application_id=self._application_id).id
self._pickle_path = (example_path / 'pickle_path.bz2').resolve()
self._city
@property
def database(self):
return self._database
@property
def application_uuid(self):
return self._application_uuid
@property
def application_id(self):
return self._application_id
@property
def user_id(self):
return self._user_id
@property
def skip_test(self):
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
def skip_reason(self):
return self._skip_reason
@property
def message(self):
return self._skip_reason
@property
def pickle_path(self):
return self._pickle_path
@property
def city(self):
return self._city

View File

@ -1,159 +0,0 @@
"""
Test db factory
SPDX - License - Identifier: LGPL - 3.0 - or -later
Copyright © 2022 Concordia CERC group
Project Coder Peter Yefi peteryefi@gmail.com
"""
import glob
import os
import unittest
from unittest import TestCase
import hub.helpers.constants as cte
from unittests.control import Control
control = Control()
class TestDBFactory(TestCase):
"""
TestDBFactory
"""
@unittest.skipIf(control.skip_test, control.skip_reason)
def test_save_city(self):
control.city.name = "Montreal"
city_id = control.database.persist_city(
control.city,
control.pickle_path,
control.city.name,
control.application_id,
control.user_id)
control.database.delete_city(city_id)
@unittest.skipIf(control.skip_test, control.skip_reason)
def test_get_update_city(self):
city_id = control.database.persist_city(control.city,
control.pickle_path,
control.city.name,
control.application_id,
control.user_id)
control.city.name = "Ottawa"
control.database.update_city(city_id, control.city)
cities = control.database.cities_by_user_and_application(
control.user_id,
control.application_id)
for updated_city in cities:
if updated_city.id == city_id:
self.assertEqual(updated_city.name, control.city.name)
break
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,
'current status',
control.application_id,
control.user_id)
city_objects_id = []
request_values = {
'scenarios': [{'current status': ['1']}]
}
for building in control.city.buildings:
_building = control.database.building_info(building.name, city_id)
if cte.MONTH not in building.cooling_demand:
print(f'building {building.name} not calculated')
continue
monthly_cooling_peak_load = building.cooling_peak_load[cte.MONTH]
yearly_cooling_peak_load = building.cooling_peak_load[cte.YEAR]
monthly_heating_peak_load = building.heating_peak_load[cte.MONTH]
yearly_heating_peak_load = building.heating_peak_load[cte.YEAR]
monthly_lighting_peak_load = building.lighting_peak_load[cte.MONTH]
yearly_lighting_peak_load = building.lighting_peak_load[cte.YEAR]
monthly_appliances_peak_load = building.appliances_peak_load[cte.MONTH]
yearly_appliances_peak_load = building.appliances_peak_load[cte.YEAR]
monthly_cooling_demand = building.cooling_demand[cte.MONTH]
yearly_cooling_demand = building.cooling_demand[cte.YEAR]
monthly_heating_demand = building.heating_demand[cte.MONTH]
yearly_heating_demand = building.heating_demand[cte.YEAR]
monthly_lighting_electrical_demand = building.lighting_electrical_demand[cte.MONTH]
yearly_lighting_electrical_demand = building.lighting_electrical_demand[cte.YEAR]
monthly_appliances_electrical_demand = building.appliances_electrical_demand[cte.MONTH]
yearly_appliances_electrical_demand = building.appliances_electrical_demand[cte.YEAR]
monthly_domestic_hot_water_heat_demand = building.domestic_hot_water_heat_demand[cte.MONTH]
yearly_domestic_hot_water_heat_demand = building.domestic_hot_water_heat_demand[cte.YEAR]
monthly_heating_consumption = building.heating_consumption[cte.MONTH]
yearly_heating_consumption = building.heating_consumption[cte.YEAR]
monthly_cooling_consumption = building.cooling_consumption[cte.MONTH]
yearly_cooling_consumption = building.cooling_consumption[cte.YEAR]
monthly_domestic_hot_water_consumption = building.domestic_hot_water_consumption[cte.MONTH]
yearly_domestic_hot_water_consumption = building._domestic_hot_water_consumption[cte.YEAR]
monthly_distribution_systems_electrical_consumption = building.distribution_systems_electrical_consumption[
cte.MONTH]
yearly_distribution_systems_electrical_consumption = building.distribution_systems_electrical_consumption[
cte.YEAR]
monthly_on_site_electrical_production = [x * cte.WATTS_HOUR_TO_JULES
for x in building.onsite_electrical_production[cte.MONTH]]
yearly_on_site_electrical_production = [x * cte.WATTS_HOUR_TO_JULES
for x in building.onsite_electrical_production[cte.YEAR]]
results = {cte.INSEL_MEB: {
'monthly_cooling_peak_load': monthly_cooling_peak_load,
'yearly_cooling_peak_load': yearly_cooling_peak_load,
'monthly_heating_peak_load': monthly_heating_peak_load,
'yearly_heating_peak_load': yearly_heating_peak_load,
'monthly_lighting_peak_load': monthly_lighting_peak_load,
'yearly_lighting_peak_load': yearly_lighting_peak_load,
'monthly_appliances_peak_load': monthly_appliances_peak_load,
'yearly_appliances_peak_load': yearly_appliances_peak_load,
'monthly_cooling_demand': monthly_cooling_demand,
'yearly_cooling_demand': yearly_cooling_demand,
'monthly_heating_demand': monthly_heating_demand,
'yearly_heating_demand': yearly_heating_demand,
'monthly_lighting_electrical_demand': monthly_lighting_electrical_demand,
'yearly_lighting_electrical_demand': yearly_lighting_electrical_demand,
'monthly_appliances_electrical_demand': monthly_appliances_electrical_demand,
'yearly_appliances_electrical_demand': yearly_appliances_electrical_demand,
'monthly_domestic_hot_water_heat_demand': monthly_domestic_hot_water_heat_demand,
'yearly_domestic_hot_water_heat_demand': yearly_domestic_hot_water_heat_demand,
'monthly_heating_consumption': monthly_heating_consumption,
'yearly_heating_consumption': yearly_heating_consumption,
'monthly_cooling_consumption': monthly_cooling_consumption,
'yearly_cooling_consumption': yearly_cooling_consumption,
'monthly_domestic_hot_water_consumption': monthly_domestic_hot_water_consumption,
'yearly_domestic_hot_water_consumption': yearly_domestic_hot_water_consumption,
'monthly_distribution_systems_electrical_consumption': monthly_distribution_systems_electrical_consumption,
'yearly_distribution_systems_electrical_consumption': yearly_distribution_systems_electrical_consumption,
'monthly_on_site_electrical_production': monthly_on_site_electrical_production,
'yearly_on_site_electrical_production': yearly_on_site_electrical_production
}}
db_building_id = _building.id
city_objects_id.append(db_building_id)
control.database.add_simulation_results(
cte.INSEL_MEB,
results, city_object_id=db_building_id)
self.assertEqual(17, len(city_objects_id), 'wrong number of results')
self.assertIsNotNone(city_objects_id[0], 'city_object_id is None')
results = control.database.results(control.user_id, control.application_id, request_values)
self.assertEqual(
28,
len(results['current status'][0]['insel meb'].keys()),
'wrong number of results after retrieve'
)
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
@unittest.skipIf(control.skip_test, control.skip_reason)
def tearDownClass(cls):
control.database.delete_application(control.application_uuid)
control.database.delete_user(control.user_id)
os.unlink(control.pickle_path)
output_files = glob.glob('./tests_outputs/*')
for output_file in output_files:
os.unlink(output_file)

View File

@ -1,36 +0,0 @@
"""
Test db retrieve
SPDX - License - Identifier: LGPL - 3.0 - or -later
Copyright © 2022 Concordia CERC group
Project Coder Ruben Sanchez ruben.sanchez@mail.concordia.ca
"""
import datetime
import unittest
from unittest import TestCase
from unittests.control import Control
control = Control()
class TestDBFactory(TestCase):
"""
TestDBFactory
"""
@unittest.skipIf(control.skip_test, control.skip_reason)
def test_retrieve_results(self):
datetime.datetime.now()
request_values = {
"scenarios": [
{"current status": ["01002777", "01002773", "01036804"]},
{"skin retrofit": ["01002777", "01002773", "01036804"]},
{"system retrofit and pv": ["01002777", "01002773", "01036804"]},
{"skin and system retrofit with pv": ["01002777", "01002773", "01036804"]}
]
}
results = control.database.results(control.user_id, control.application_id, request_values)
scenarios = ['current status', 'skin retrofit', 'system retrofit and pv', 'skin and system retrofit with pv']
for key, value in results.items():
self.assertTrue(key in scenarios, 'Wrong key value')
self.assertEqual(len(value), 0, 'wrong number of results')