Merge branch 'hp_results' into 'master'

Hp results

See merge request Guille/hub!61
This commit is contained in:
Guillermo Gutierrez Morote 2023-03-06 14:13:12 +00:00
commit 47db683eea
14 changed files with 8985 additions and 49 deletions

View File

@ -29,6 +29,7 @@ from hub.helpers.geometry_helper import GeometryHelper
from hub.helpers.location import Location from hub.helpers.location import Location
from hub.city_model_structure.energy_system import EnergySystem from hub.city_model_structure.energy_system import EnergySystem
from hub.city_model_structure.lca_material import LcaMaterial from hub.city_model_structure.lca_material import LcaMaterial
import pandas as pd
class City: class City:
@ -447,8 +448,31 @@ class City:
def merge(self, city) -> City: def merge(self, city) -> City:
_merge_city = self.copy _merge_city = self.copy
selected_city_object = None
building = None
# set initial minimum radiation to a higher number
min_radiation = 1999999
for city_object in city.city_objects: for city_object in city.city_objects:
_merge_city.add_city_object(city_object) if city_object.type == 'building':
building = city_object
building_radiation = 0
for surface in city_object.surfaces:
radiation = surface.global_irradiance
if 'year' not in radiation and 'month' not in radiation:
continue
elif "year" in radiation:
building_radiation += radiation["year"].iloc[0]
elif "month" in radiation and "year" not in radiation:
surface.global_irradiance["year"] = pd.DataFrame({radiation["month"].sum()})
building_radiation += radiation["year"].iloc[0]
if building_radiation < min_radiation:
min_radiation = building_radiation
selected_city_object = city_object
# merge the city object with the minimum radiation
if selected_city_object is not None:
_merge_city.add_city_object(selected_city_object)
else:
_merge_city.add_city_object(building)
return _merge_city return _merge_city
@property @property

View File

@ -8,6 +8,7 @@ from hub.persistence import City
from hub.persistence import Application from hub.persistence import Application
from hub.persistence import User from hub.persistence import User
from hub.persistence import CityObject from hub.persistence import CityObject
from hub.persistence import SimulationResults
class DBFactory: class DBFactory:
@ -20,6 +21,7 @@ class DBFactory:
self._application = Application(db_name=db_name, app_env=app_env, dotenv_path=dotenv_path) self._application = Application(db_name=db_name, app_env=app_env, dotenv_path=dotenv_path)
self._user = User(db_name=db_name, app_env=app_env, dotenv_path=dotenv_path) self._user = User(db_name=db_name, app_env=app_env, dotenv_path=dotenv_path)
self._city_object = CityObject(db_name=db_name, app_env=app_env, dotenv_path=dotenv_path) self._city_object = CityObject(db_name=db_name, app_env=app_env, dotenv_path=dotenv_path)
self._simulation_results = SimulationResults(db_name=db_name, dotenv_path=dotenv_path, app_env=app_env)
def get_city(self, city_id): def get_city(self, city_id):
""" """
@ -42,6 +44,34 @@ class DBFactory:
""" """
return self._city.get_by_user(user_id) return self._city.get_by_user(user_id)
def get_simulation_results_by_id(self, sim_id):
"""
Retrieve a single simulation results
:param sim_id: the id of the simulation results to get
"""
return self._simulation_results.get_simulation_results_by_id(sim_id)
def get_simulation_results_by_name(self, name):
"""
Retrieve a single simulation results
:param name: the name of the simulation results to get
"""
return self._simulation_results.get_simulation_results_by_name(name)
def get_simulation_results_by_city_id(self, city_id):
"""
Retrieve a single simulation results
:param city_id: the city id of the simulation results to get
"""
return self._simulation_results.get_simulation_results_by_city_id(city_id)
def get_simulation_results_by_city_object_id(self, city_object_id):
"""
Retrieve a single simulation results
:param city_object_id: the city object id of the simulation results to get
"""
return self._simulation_results.get_simulation_results_by_city_object_id(city_object_id)
def application_info(self, application_uuid): def application_info(self, application_uuid):
return self._application.get_by_uuid(application_uuid) return self._application.get_by_uuid(application_uuid)

View File

@ -6,6 +6,7 @@ Project CoderPeter Yefi peteryefi@gmail.com
""" """
from hub.persistence import User from hub.persistence import User
class UserFactory: class UserFactory:
""" """
UserFactory class UserFactory class
@ -14,17 +15,19 @@ class UserFactory:
def __init__(self, db_name, app_env, dotenv_path): def __init__(self, db_name, app_env, dotenv_path):
self._user_repo = User(db_name=db_name, app_env=app_env, dotenv_path=dotenv_path) self._user_repo = User(db_name=db_name, app_env=app_env, dotenv_path=dotenv_path)
def login_user(self, email: str, password: str): def login_user(self, name: str, password: str, application_id: int):
""" """
Retrieve a single city from postgres Retrieve a single city from postgres
:param email: the email of the user :param name: the email of the user
:param password: the password of the user :param password: the password of the user
:param application_id: the id of the application accessing hub
""" """
return self._user_repo.get_user_by_email_and_password(email, password) return self._user_repo.get_by_name_application_and_password(name, password, application_id)
def get_user_by_email(self, email): def get_by_name_and_application(self, name: str, application: int):
""" """
Retrieve a single user Retrieve a single user
:param email: the email of the user to get :param name: user name
:param application: application accessing hub
""" """
return self._user_repo.get_by_email(email) return self._user_repo.get_by_name_and_application(name, application)

View File

@ -6,6 +6,7 @@ log_dir = (Path(__file__).parent.parent / 'logs').resolve()
log_file = (log_dir / 'hub.log').resolve() log_file = (log_dir / 'hub.log').resolve()
try: try:
if not os.path.isfile(log_file): if not os.path.isfile(log_file):
if not os.path.exists:
os.mkdir(log_dir) os.mkdir(log_dir)
with open(log_file, 'x'): with open(log_file, 'x'):
pass pass

View File

@ -7,6 +7,7 @@ Project CoderPeter Yefi peteryefi@gmail.com
from hub.city_model_structure.city import City from hub.city_model_structure.city import City
from hub.persistence import City as CityRepository from hub.persistence import City as CityRepository
from hub.persistence import SimulationResults from hub.persistence import SimulationResults
from hub.persistence import Application
class DBFactory: class DBFactory:
@ -17,6 +18,7 @@ class DBFactory:
def __init__(self, db_name, dotenv_path, app_env): def __init__(self, db_name, dotenv_path, app_env):
self._city_repository = CityRepository(db_name=db_name, dotenv_path=dotenv_path, app_env=app_env) self._city_repository = CityRepository(db_name=db_name, dotenv_path=dotenv_path, app_env=app_env)
self._simulation_results = SimulationResults(db_name=db_name, dotenv_path=dotenv_path, app_env=app_env) self._simulation_results = SimulationResults(db_name=db_name, dotenv_path=dotenv_path, app_env=app_env)
self._application = Application(db_name=db_name, dotenv_path=dotenv_path, app_env=app_env)
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):
""" """
@ -36,6 +38,24 @@ class DBFactory:
""" """
return self._city_repository.update(city_id, city) return self._city_repository.update(city_id, city)
def persist_application(self, name: str, description: str, application_uuid: str):
"""
Creates an application
:param name: name of application
:param description: the description of the application
:param application_uuid: the uuid of the application to be created
"""
return self._application.insert(name, description, application_uuid)
def update_application(self, name: str, description: str, application_uuid: str):
"""
Update an application
:param name: name of application
:param description: the description of the application
:param application_uuid: the uuid of the application to be created
"""
return self._application.update(application_uuid, name, description)
def delete_city(self, city_id): def delete_city(self, city_id):
""" """
Deletes a single city from postgres Deletes a single city from postgres
@ -43,6 +63,13 @@ class DBFactory:
""" """
self._city_repository.delete(city_id) 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

View File

@ -4,6 +4,7 @@ SPDX - License - Identifier: LGPL - 3.0 - or -later
Copyright © 2022 Concordia CERC group Copyright © 2022 Concordia CERC group
Project CoderPeter Yefi peteryefi@gmail.com Project CoderPeter Yefi peteryefi@gmail.com
""" """
from hub.persistence import User from hub.persistence import User
from hub.persistence import UserRoles from hub.persistence import UserRoles
@ -16,17 +17,17 @@ class UserFactory:
def __init__(self, db_name, app_env, dotenv_path): def __init__(self, db_name, app_env, dotenv_path):
self._user_repo = User(db_name=db_name, app_env=app_env, dotenv_path=dotenv_path) self._user_repo = User(db_name=db_name, app_env=app_env, dotenv_path=dotenv_path)
def create_user(self, name: str, email: str, 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
:param name: the name of the user :param name: the name of the user
:param email: the email 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
:param role: the role of the user :param role: the role of the user
""" """
return self._user_repo.insert(name, email, password, role) return self._user_repo.insert(name, password, role, application_id)
def update_user(self, user_id: int, name: str, email: str, password: str, role: UserRoles): def update_user(self, user_id: int, name: str, password: str, role: UserRoles):
""" """
Creates a new user Creates a new user
:param user_id: the id of the user :param user_id: the id of the user
@ -35,11 +36,11 @@ class UserFactory:
:param password: the password of the user :param password: the password of the user
:param role: the role of the user :param role: the role of the user
""" """
return self._user_repo.update(user_id, name, email, password, role) return self._user_repo.update(user_id, name, password, role)
def delete_user(self, user_id): def delete_user(self, user_id):
""" """
Retrieve a single user Retrieve a single user
:param user_id: the id of the user to delete :param user_id: the id of the user to delete
""" """
return self._user_repo.delete_user(user_id) return self._user_repo.delete(user_id)

View File

@ -12,6 +12,7 @@ from sqlalchemy import DateTime
from sqlalchemy.dialects.postgresql import JSONB from sqlalchemy.dialects.postgresql import JSONB
from hub.persistence.configuration import Models from hub.persistence.configuration import Models
class SimulationResults(Models): class SimulationResults(Models):
""" """
A model representation of an application A model representation of an application

View File

@ -42,6 +42,7 @@ class Application(Repository):
if application is None: if application is None:
try: try:
application = Model(name=name, description=description, application_uuid=application_uuid) application = Model(name=name, description=description, application_uuid=application_uuid)
self.session.add(application) self.session.add(application)
self.session.commit() self.session.commit()
return application return application

View File

@ -6,7 +6,6 @@ Project Coder Peter Yefi peteryefi@gmail.com
""" """
import datetime import datetime
import pickle
from typing import Union, Dict from typing import Union, Dict
from sqlalchemy import select from sqlalchemy import select
@ -104,6 +103,7 @@ class City(Repository):
:return: a city :return: a city
""" """
try: try:
self.session.query(CityObject).filter(CityObject.city_id == city_id).delete()
self.session.query(Model).filter(Model.id == city_id).delete() self.session.query(Model).filter(Model.id == city_id).delete()
self.session.commit() self.session.commit()
except SQLAlchemyError as err: except SQLAlchemyError as err:

View File

@ -125,6 +125,50 @@ class SimulationResults(Repository):
except SQLAlchemyError as err: except SQLAlchemyError as err:
logger.error(f'Error while fetching city by city_id: {err}') logger.error(f'Error while fetching city by city_id: {err}')
def get_simulation_results_by_id(self, sim_id) -> [Model]:
"""
Fetch simulation results by id
:param sim_id: a simulation result identifier
:return: [Model] with the provided sim_id
"""
try:
return self.session.execute(select(Model).where(Model.id == sim_id)).first()
except SQLAlchemyError as err:
logger.error(f'Error while fetching simulation results by sim_id: {err}')
def get_simulation_results_by_name(self, name) -> [Model]:
"""
Fetch simulation results by name
:param name: the name of the simulation results
:return: [Model] with the provided name
"""
try:
return self.session.execute(select(Model).where(Model.name == name))
except SQLAlchemyError as err:
logger.error(f'Error while fetching simulation results by name: {err}')
def get_simulation_results_by_city_id(self, city_id) -> [Model]:
"""
Fetch simulation results by name
:param city_id: the id of the city
:return: [Model] with the provided city id
"""
try:
return self.session.execute(select(Model).where(Model.city_id == city_id))
except SQLAlchemyError as err:
logger.error(f'Error while fetching simulation results by name: {err}')
def get_simulation_results_by_city_object_id(self, city_object_id) -> [Model]:
"""
Fetch simulation results by name
:param city_object_id: the id of the city object
:return: [Model] with the provided city object id
"""
try:
return self.session.execute(select(Model).where(Model.city_object_id == city_object_id))
except SQLAlchemyError as err:
logger.error(f'Error while fetching simulation results by name: {err}')
def get_city_object(self, city_object_id) -> [CityObject]: def get_city_object(self, city_object_id) -> [CityObject]:
""" """
Fetch a city object based city id Fetch a city object based city id

View File

@ -6,8 +6,12 @@ Project Coder Guille Gutierrez Guillermo.GutierrezMorote@concordia.ca
""" """
from pathlib import Path from pathlib import Path
from unittest import TestCase from unittest import TestCase
from hub.imports.geometry_factory import GeometryFactory from hub.imports.geometry_factory import GeometryFactory
import platform
from hub.exports.exports_factory import ExportsFactory
import subprocess
from subprocess import SubprocessError, TimeoutExpired, CalledProcessError
from hub.imports.results_factory import ResultFactory
class TestCityMerge(TestCase): class TestCityMerge(TestCase):
@ -20,6 +24,16 @@ class TestCityMerge(TestCase):
:return: None :return: None
""" """
self._example_path = (Path(__file__).parent / 'tests_data').resolve() self._example_path = (Path(__file__).parent / 'tests_data').resolve()
self._output_path = (Path(__file__).parent / 'tests_outputs').resolve()
self._weather_file = (self._example_path / 'CAN_PQ_Montreal.Intl.AP.716270_CWEC.epw').resolve()
self._climate_file = (self._example_path / 'New_York.cli').resolve()
self._executable = None
if platform.system() == 'Linux':
self._executable = 'citysim_sra'
if platform.system() == 'Darwin':
self._executable = 'citysim_sra'
elif platform.system() == 'Windows':
self._executable = 'shortwave_integer'
def _get_citygml(self, file): def _get_citygml(self, file):
file_path = (self._example_path / file).resolve() file_path = (self._example_path / file).resolve()
@ -34,3 +48,23 @@ class TestCityMerge(TestCase):
self.assertEqual(len(city_1.city_objects), 1, 'Wrong amount of city_objects found in city_1') self.assertEqual(len(city_1.city_objects), 1, 'Wrong amount of city_objects found in city_1')
self.assertEqual(len(city_2.city_objects), 1, 'Wrong amount of city_objects found in city_2') self.assertEqual(len(city_2.city_objects), 1, 'Wrong amount of city_objects found in city_2')
self.assertEqual(len(city.city_objects), 2, 'Wrong amount of city_objects found in city') self.assertEqual(len(city.city_objects), 2, 'Wrong amount of city_objects found in city')
def test_merge_with_radiation(self):
city_one = self._get_citygml('one_building_in_kelowna.gml')
city_two = self._get_citygml('pluto_building.gml')
city_two.name = 'New_York'
city_two.climate_file = self._climate_file
try:
ExportsFactory(export_type='sra', city=city_two, path=self._output_path, weather_file=self._weather_file,
target_buildings=city_two.buildings, weather_format='epw').export()
subprocess.run([self._executable, f'{str(self._output_path)}/{city_two.name}_sra.xml'], stdout=subprocess.DEVNULL)
except (SubprocessError, TimeoutExpired, CalledProcessError) as error:
raise Exception(error)
ResultFactory('sra', city_two, self._output_path).enrich()
merged_city = city_one.merge(city_two)
self.assertEqual(len(merged_city.buildings), 2)
self.assertEqual(merged_city.buildings[1].surfaces[0].global_irradiance['year'].iloc[0], 254.3453196347032)
self.assertEqual(merged_city.buildings[0].surfaces[0].global_irradiance, {})
self.assertEqual(merged_city.buildings[0].surfaces[2].global_irradiance, {})
self.assertEqual(city_one.buildings[0].surfaces[0].global_irradiance, merged_city.buildings[0].surfaces[0].global_irradiance)
self.assertEqual(city_two.buildings[0].surfaces[0].global_irradiance, merged_city.buildings[1].surfaces[0].global_irradiance)

View File

@ -9,12 +9,12 @@ from hub.imports.geometry_factory import GeometryFactory
from hub.imports.db_factory import DBFactory from hub.imports.db_factory import DBFactory
from hub.imports.user_factory import UserFactory from hub.imports.user_factory import UserFactory
from hub.exports.db_factory import DBFactory as ExportDBFactory from hub.exports.db_factory import DBFactory as ExportDBFactory
from hub.persistence.base_repo import BaseRepo from hub.persistence.repository import Repository
from sqlalchemy import create_engine from sqlalchemy import create_engine
from hub.persistence.models import City from hub.persistence.models import City, Application, CityObject
from hub.persistence.models import User, UserRoles from hub.persistence.models import User, UserRoles
from pickle import loads
from sqlalchemy.exc import ProgrammingError from sqlalchemy.exc import ProgrammingError
import uuid
class TestDBFactory(TestCase): class TestDBFactory(TestCase):
@ -29,8 +29,8 @@ class TestDBFactory(TestCase):
:return: None :return: None
""" """
# Create test database # Create test database
repo = BaseRepo(db_name='test_db', app_env='TEST', dotenv_path='/usr/local/etc/hub/.env') repo = Repository(db_name='test_db', app_env='TEST', dotenv_path='/usr/local/etc/hub/.env')
eng = create_engine(f'postgresql://{repo.config.get_db_user()}@/{repo.config.get_db_user()}') eng = create_engine(f'postgresql://{repo.configuration.get_db_user()}@/{repo.configuration.get_db_user()}')
try: try:
# delete test database if it exists # delete test database if it exists
@ -45,56 +45,58 @@ class TestDBFactory(TestCase):
cnn.execute('commit') cnn.execute('commit')
cnn.execute("CREATE DATABASE test_db") cnn.execute("CREATE DATABASE test_db")
cnn.close() cnn.close()
Application.__table__.create(bind=repo.engine, checkfirst=True)
User.__table__.create(bind=repo.engine, checkfirst=True) User.__table__.create(bind=repo.engine, checkfirst=True)
City.__table__.create(bind=repo.engine, checkfirst=True) City.__table__.create(bind=repo.engine, checkfirst=True)
CityObject.__table__.create(bind=repo.engine, checkfirst=True)
city_file = "tests_data/C40_Final.gml" city_file = "tests_data/C40_Final.gml"
cls.city = GeometryFactory('citygml', city_file).city cls.city = GeometryFactory('citygml', city_file).city
cls._db_factory = DBFactory(db_name='test_db', app_env='TEST', dotenv_path='../.env') cls._db_factory = DBFactory(db_name='test_db', app_env='TEST', dotenv_path='../.env')
cls._export_db_factory = ExportDBFactory(db_name='test_db', app_env='TEST', dotenv_path='../.env') cls._export_db_factory = ExportDBFactory(db_name='test_db', app_env='TEST', dotenv_path='../.env')
user_factory = UserFactory(db_name='test_db', app_env='TEST', dotenv_path='../.env') user_factory = UserFactory(db_name='test_db', app_env='TEST', dotenv_path='../.env')
cls._user = user_factory.create_user("Admin", "admin@hub.com", "Admin@123", UserRoles.Admin) cls.unique_id = str(uuid.uuid4())
cls.application = cls._db_factory.persist_application("test", "test application", cls.unique_id)
cls._user = user_factory.create_user("Admin", cls.application.id, "Admin@123", UserRoles.Admin)
cls.pickle_path = 'tests_data/pickle_path.bz2'
def test_save_application(self):
self.assertEqual(self.application.name, "test")
self.assertEqual(self.application.description, "test application")
self.assertEqual(str(self.application.application_uuid), self.unique_id)
def test_save_city(self): def test_save_city(self):
saved_city = self._db_factory.persist_city(self._user.id, self.city) self.city.name = "Montréal"
saved_city = self._db_factory.persist_city(self.city, self.pickle_path, self.application.id, self._user.id)
self.assertEqual(saved_city.name, 'Montréal') self.assertEqual(saved_city.name, 'Montréal')
pickled_city = loads(saved_city.city) self.assertEqual(saved_city.pickle_path, self.pickle_path)
self.assertEqual(len(pickled_city.buildings), 10) self.assertEqual(saved_city.level_of_detail, self.city.level_of_detail.geometry)
self.assertEqual(pickled_city.buildings[0].floor_area, 1990.9913970530033)
self._db_factory.delete_city(saved_city.id) self._db_factory.delete_city(saved_city.id)
def test_save_same_city_with_same_hub_version(self):
first_city = self._db_factory.persist_city(self._user.id, self.city)
second_city = self._db_factory.persist_city(self._user.id, self.city)
self.assertEqual(second_city['message'], f'Same version of {self.city.name} exist')
self.assertEqual(first_city.name, 'Montréal')
self.assertEqual(first_city.country_code, 'ca')
self._db_factory.delete_city(first_city.id)
def test_get_city_by_name(self): def test_get_city_by_name(self):
city = self._db_factory.persist_city(self._user.id, self.city) city = self._db_factory.persist_city(self.city, self.pickle_path, self.application.id, self._user.id)
retrieved_city = self._export_db_factory.get_city_by_name(city.name) retrieved_city = self._export_db_factory.get_city_by_name(city.name)
self.assertEqual(retrieved_city[0].lower_corner[0], 610610.7547462888) self.assertEqual(retrieved_city[0].application_id, 1)
self._db_factory.delete_city(city.id)
def test_get_city_by_user(self):
city = self._db_factory.persist_city(self._user.id, self.city)
retrieved_city = self._export_db_factory.get_city_by_user(self._user.id)
self.assertEqual(retrieved_city[0].user_id, self._user.id) self.assertEqual(retrieved_city[0].user_id, self._user.id)
self._db_factory.delete_city(city.id) self._db_factory.delete_city(city.id)
def test_get_city_by_user(self):
city = self._db_factory.persist_city(self.city, self.pickle_path, self.application.id, self._user.id)
retrieved_city = self._export_db_factory.get_city_by_user(self._user.id)
self.assertEqual(retrieved_city[0].pickle_path, self.pickle_path)
self._db_factory.delete_city(city.id)
def test_get_city_by_id(self): def test_get_city_by_id(self):
city = self._db_factory.persist_city(self._user.id, self.city) city = self._db_factory.persist_city(self.city, self.pickle_path, self.application.id, self._user.id)
retrieved_city = self._export_db_factory.get_city(city.id) retrieved_city = self._export_db_factory.get_city(city.id)
self.assertEqual(retrieved_city.upper_corner[0], 610818.6731258357) self.assertEqual(retrieved_city.level_of_detail, self.city.level_of_detail.geometry)
self._db_factory.delete_city(city.id) self._db_factory.delete_city(city.id)
def test_get_update_city(self): def test_get_update_city(self):
city = self._db_factory.persist_city(self._user.id, self.city) city = self._db_factory.persist_city(self.city, self.pickle_path, self.application.id, self._user.id)
self.city.longitude = 1.43589 self.city.name = "Ottawa"
self.city.latitude = -9.38928339
self._db_factory.update_city(city.id, self.city) self._db_factory.update_city(city.id, self.city)
updated_city = self._export_db_factory.get_city(city.id) updated_city = self._export_db_factory.get_city(city.id)
self.assertEqual(updated_city.longitude, 1.43589) self.assertEqual(updated_city.name, self.city.name)
self.assertEqual(updated_city.latitude, -9.38928339)
self._db_factory.delete_city(city.id) self._db_factory.delete_city(city.id)

File diff suppressed because it is too large Load Diff

Binary file not shown.