Partial completion of new persistence.

Results are still missing and need to be added to the final commit. including the db table creation that seems to be missing
This commit is contained in:
Guille Gutierrez 2023-05-16 18:06:22 -04:00
parent 48dddae525
commit a33bf0b366
15 changed files with 125 additions and 109 deletions

View File

@ -246,7 +246,9 @@ class Polygon:
polygon = shapley_polygon(coordinates) polygon = shapley_polygon(coordinates)
try: try:
vertices_2d, faces = trimesh.creation.triangulate_polygon(polygon, engine='triangle') vertices_2d, faces = trimesh.creation.triangulate_polygon(polygon, engine='triangle')
mesh = Trimesh(vertices=vertices, faces=faces) mesh = Trimesh(vertices=vertices, faces=faces)
# check orientation # check orientation

View File

@ -92,7 +92,6 @@ class Polyhedron:
points = polygon.coordinates points = polygon.coordinates
if len(points) != 3: if len(points) != 3:
sub_polygons = polygon.triangles sub_polygons = polygon.triangles
# todo: I modified this! To be checked @Guille
if len(sub_polygons) >= 1: if len(sub_polygons) >= 1:
for sub_polygon in sub_polygons: for sub_polygon in sub_polygons:
face = [] face = []

View File

@ -494,6 +494,8 @@ class Building(CityObject):
""" """
_usage = '' _usage = ''
for internal_zone in self.internal_zones: for internal_zone in self.internal_zones:
if internal_zone.usages is None:
continue
for usage in internal_zone.usages: for usage in internal_zone.usages:
_usage = f'{_usage}{usage.name}_{usage.percentage} ' _usage = f'{_usage}{usage.name}_{usage.percentage} '
return _usage.rstrip() return _usage.rstrip()

View File

@ -77,7 +77,7 @@ class InselMonthlyEnergyBalance(Insel):
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:
raise Exception(f'Level of detail of usage not assigned') raise Exception(f'Level of detail of surface radiation not assigned')
if levels_of_detail.surface_radiation < 1: if levels_of_detail.surface_radiation < 1:
raise Exception(f'Level of detail of surface radiation = {levels_of_detail.surface_radiation}. ' raise Exception(f'Level of detail of surface radiation = {levels_of_detail.surface_radiation}. '
f'Required minimum level 1') f'Required minimum level 1')

View File

@ -37,7 +37,7 @@ class DBFactory:
def user_info(self, name, password, application_id): def user_info(self, name, password, application_id):
""" """
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
:param name: the user name :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
:return: User or None :return: User or None
@ -47,7 +47,7 @@ class DBFactory:
def user_login(self, name, password, application_uuid): def user_login(self, name, password, application_uuid):
""" """
Retrieve the user info Retrieve the user info
:param name: the user name :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
:return: User or None :return: User or None

View File

@ -4,10 +4,11 @@ import os
import sys import sys
def get_logger(file_logger=False): def get_logger(file_logger=False, debug_level=logger.ERROR):
""" """
Returns a logging object Returns a logging object
:param file_logger: a boolean to indicate the kind of logging :param file_logger: a boolean to indicate the kind of logging
:param debug_level: the value for the logger level (default error)
object to return, true (default) means a file logger is required object to return, true (default) means a file logger is required
:return: :return:
""" """
@ -21,11 +22,11 @@ def get_logger(file_logger=False):
os.mkdir(log_dir) os.mkdir(log_dir)
with open(log_file, 'x'): with open(log_file, 'x'):
pass pass
logger.basicConfig(filename=log_file, format=log_format, level=logger.DEBUG) logger.basicConfig(filename=log_file, format=log_format, level=debug_level)
return logger return logger
except IOError as err: except IOError as err:
print(f'I/O exception: {err}') print(f'I/O exception: {err}')
else: else:
logger.getLogger().addHandler(logger.StreamHandler(stream=sys.stdout)) logger.getLogger().addHandler(logger.StreamHandler(stream=sys.stdout))
logger.getLogger().setLevel(logger.DEBUG) logger.getLogger().setLevel(debug_level)
return logger.getLogger() return logger.getLogger()

View File

@ -35,6 +35,10 @@ class NrcanPhysicsParameters:
city = self._city city = self._city
nrcan_catalog = ConstructionCatalogFactory('nrcan').catalog nrcan_catalog = ConstructionCatalogFactory('nrcan').catalog
for building in city.buildings: for building in city.buildings:
if building.function not in Dictionaries().hub_function_to_nrcan_construction_function.keys():
logger.error(f'Building {building.name} has an unknown building function {building.function}\n')
sys.stderr.write(f'Building {building.name} has an unknown building function {building.function}\n')
continue
function = Dictionaries().hub_function_to_nrcan_construction_function[building.function] function = Dictionaries().hub_function_to_nrcan_construction_function[building.function]
try: try:
archetype = self._search_archetype(nrcan_catalog, function, building.year_of_construction, self._climate_zone) archetype = self._search_archetype(nrcan_catalog, function, building.year_of_construction, self._climate_zone)

View File

@ -35,21 +35,27 @@ class NrelPhysicsParameters:
city = self._city city = self._city
nrel_catalog = ConstructionCatalogFactory('nrel').catalog nrel_catalog = ConstructionCatalogFactory('nrel').catalog
for building in city.buildings: for building in city.buildings:
if building.function not in Dictionaries().hub_function_to_nrel_construction_function.keys():
logger.error(f'Building {building.name} has unknown function [{building.function}]')
sys.stderr.write(f'Building {building.name} has unknown function [{building.function}]\n')
continue
if building.function not in Dictionaries().hub_function_to_nrel_construction_function.keys():
logger.error(f'Building {building.name} has unknown function {building.function}\n')
sys.stderr.write(f'Building {building.name} has unknown function {building.function}\n')
continue
function = Dictionaries().hub_function_to_nrel_construction_function[building.function] function = Dictionaries().hub_function_to_nrel_construction_function[building.function]
try: try:
archetype = self._search_archetype(nrel_catalog, function, building.year_of_construction, archetype = self._search_archetype(nrel_catalog, function, building.year_of_construction, self._climate_zone)
self._climate_zone)
except KeyError: except KeyError:
logger.error(f'Building {building.name} has unknown construction archetype for building function: ' logger.error(f'Building {building.name} has unknown construction archetype for building function: '
f'{function} [{building.function}], building year of construction: {building.year_of_construction} ' f'{function} [{building.function}], building year of construction: {building.year_of_construction}'
f'and climate zone {self._climate_zone}\n') f' and climate zone {self._climate_zone}\n')
sys.stderr.write(f'Building {building.name} has unknown construction archetype for building function: ' sys.stderr.write(f'Building {building.name} has unknown construction archetype for building function: '
f'{function} [{building.function}], ' f'{function} [{building.function}], '
f'building year of construction: {building.year_of_construction} ' f'building year of construction: {building.year_of_construction} '
f'and climate zone {self._climate_zone}\n') f'and climate zone {self._climate_zone}\n')
continue continue
# if building has no thermal zones defined from geometry, and the building will be divided in storeys, # if building has no thermal zones defined from geometry, and the building will be divided in storeys,
# one thermal zone per storey is assigned # one thermal zone per storey is assigned
if len(building.internal_zones) == 1: if len(building.internal_zones) == 1:

View File

@ -13,6 +13,7 @@ from sqlalchemy import DateTime
from hub.city_model_structure.building import Building from hub.city_model_structure.building import Building
from hub.persistence.configuration import Models from hub.persistence.configuration import Models
class CityObject(Models): class CityObject(Models):
""" """
A model representation of an application A model representation of an application
@ -46,3 +47,23 @@ class CityObject(Models):
self.usage = building.usages_percentage self.usage = building.usages_percentage
self.volume = building.volume self.volume = building.volume
self.area = building.floor_area self.area = building.floor_area
storeys = building.storeys_above_ground
if storeys is None:
print(building.average_storey_height)
storeys = building.max_height / building.average_storey_height
self.total_heating_area = building.floor_area * storeys
wall_area = 0
for wall in building.walls:
wall_area += wall.solid_polygon.area
self.wall_area = wall_area
window_ratio = 0
for internal_zone in building.internal_zones:
for thermal_zone in internal_zone.thermal_zones:
for thermal_boundary in thermal_zone.thermal_boundaries:
window_ratio = thermal_boundary.window_ratio
break
self.windows_area = wall_area * window_ratio
system_name = building.energy_systems_archetype_name
if system_name is None:
system_name = ''
self.system_name = system_name

View File

@ -76,7 +76,8 @@ class City(Repository):
""" """
try: try:
now = datetime.datetime.utcnow() now = datetime.datetime.utcnow()
self.session.query(Model).filter(Model.id == city_id).update({'name': city.name,'updated': now}) print(f'{now}')
self.session.query(Model).filter(Model.id == city_id).update({'name': city.name, 'updated': now})
self.session.commit() self.session.commit()
except SQLAlchemyError as err: except SQLAlchemyError as err:
logger.error(f'Error while updating city: {err}') logger.error(f'Error while updating city: {err}')

View File

@ -41,20 +41,8 @@ class CityObject(Repository):
city_object = self.get_by_name_and_city(building.name, city_id) city_object = self.get_by_name_and_city(building.name, city_id)
if city_object is None: if city_object is None:
try: try:
object_usage = ''
for internal_zone in building.internal_zones:
for usage in internal_zone.usages:
object_usage = f'{object_usage}{usage.name}_{usage.percentage} '
object_usage = object_usage.rstrip()
city_object = Model(city_id=city_id, city_object = Model(city_id=city_id,
name=building.name, building=building)
alias=building.alias,
object_type=building.type,
year_of_construction=building.year_of_construction,
function=building.function,
usage=object_usage,
volume=building.volume,
area=building.floor_area)
self.session.add(city_object) self.session.add(city_object)
self.session.flush() self.session.flush()
self.session.commit() self.session.commit()

View File

@ -284,12 +284,3 @@ class TestConstructionFactory(TestCase):
self.assertIsNotNone(thermal_boundary.layers, 'layers is none') self.assertIsNotNone(thermal_boundary.layers, 'layers is none')
self._check_thermal_openings(thermal_boundary) self._check_thermal_openings(thermal_boundary)
self._check_surfaces(thermal_boundary) self._check_surfaces(thermal_boundary)
def test_archetype_not_found(self):
file = 'pluto_building.gml'
city = self._get_citygml(file)
for building in city.buildings:
building.year_of_construction = 1990
building.function = 'office'
ConstructionFactory('nrel', city).enrich()

View File

@ -11,6 +11,8 @@ from pathlib import Path
import sqlalchemy.exc 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.usage_factory import UsageFactory
from hub.imports.db_factory import DBFactory as ImportDBFactory from hub.imports.db_factory import DBFactory as ImportDBFactory
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
@ -18,29 +20,34 @@ 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
from hub.persistence.models import User, UserRoles from hub.persistence.models import User, UserRoles
from hub.helpers.dictionaries import Dictionaries
from sqlalchemy.exc import ProgrammingError from sqlalchemy.exc import ProgrammingError
import uuid import uuid
class Configure:
class Configure:
_skip_test = False _skip_test = False
_skip_reason = 'PostgreSQL not properly installed in host machine' _skip_reason = 'PostgreSQL not properly installed in host machine'
def __init__(self): def __init__(self):
""" """
Test Test
setup setup
:return: None :return: None
""" """
self._skip_test = False self._skip_test = False
# Create test database # Create test database
dotenv_path = str(Path("{}/.local/etc/hub/.env".format(os.path.expanduser('~'))).resolve()) dotenv_path = Path("{}/.local/etc/hub/.env".format(os.path.expanduser('~'))).resolve()
repository = Repository(db_name='hub_unittest', app_env='TEST', dotenv_path=dotenv_path) 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='hub_unittests', app_env='TEST', dotenv_path=dotenv_path)
engine = create_engine(repository.configuration.connection_string) engine = create_engine(repository.configuration.connection_string)
try: try:
# delete test database if it exists # delete test database if it exists
connection = engine.connect() connection = engine.connect()
connection.execute('commit')
connection.close() connection.close()
except ProgrammingError: except ProgrammingError:
print(f'Database does not exist. Nothing to delete') print(f'Database does not exist. Nothing to delete')
@ -55,7 +62,12 @@ class Configure:
CityObject.__table__.create(bind=repository.engine, checkfirst=True) CityObject.__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"
self._city = GeometryFactory('citygml', city_file).city self._city = GeometryFactory('citygml',
city_file,
function_to_hub=Dictionaries().alkis_function_to_hub_function).city
ConstructionFactory('nrcan', self._city).enrich()
UsageFactory('nrcan', self._city).enrich()
self._import_db_factory = ImportDBFactory( self._import_db_factory = ImportDBFactory(
db_name=repository.configuration.db_name, db_name=repository.configuration.db_name,
app_env='TEST', app_env='TEST',
@ -103,7 +115,7 @@ class Configure:
@property @property
def message(self): def message(self):
return self._skip_message return self._skip_reason
@property @property
def city(self): def city(self):
@ -113,12 +125,14 @@ class Configure:
def pickle_path(self): def pickle_path(self):
return self._pickle_path return self._pickle_path
configure = Configure() configure = Configure()
class TestDBFactory(TestCase): class TestDBFactory(TestCase):
""" """
TestDBFactory TestDBFactory
""" """
@unittest.skipIf(configure.skip_test, configure.skip_reason) @unittest.skipIf(configure.skip_test, configure.skip_reason)
def test_save_application(self): def test_save_application(self):
@ -129,43 +143,29 @@ class TestDBFactory(TestCase):
@unittest.skipIf(configure.skip_test, configure.skip_reason) @unittest.skipIf(configure.skip_test, configure.skip_reason)
def test_save_city(self): def test_save_city(self):
configure.city.name = "Montreal" configure.city.name = "Montreal"
saved_city = configure.import_db_factory.persist_city( city = configure.import_db_factory.persist_city(
configure.city, configure.city,
configure.pickle_path, configure.pickle_path,
configure.application.id, configure.application.id,
configure.user.id) configure.user.id)
self.assertEqual(saved_city.name, 'Montreal') self.assertEqual(city.name, 'Montreal')
self.assertEqual(saved_city.pickle_path, self.pickle_path) self.assertEqual(city.pickle_path, configure.pickle_path)
self.assertEqual(saved_city.level_of_detail, self.city.level_of_detail.geometry) self.assertEqual(city.level_of_detail, configure.city.level_of_detail.geometry)
self._db_factory.delete_city(saved_city.id) configure.import_db_factory.delete_city(city.id)
@unittest.skipIf(configure.skip_test, configure.skip_reason)
def test_get_city_by_name(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_name(city.name)
self.assertEqual(retrieved_city[0].application_id, 1)
self.assertEqual(retrieved_city[0].user_id, self._user.id)
self._db_factory.delete_city(city.id)
@unittest.skipIf(configure.skip_test, configure.skip_reason)
def test_get_city_by_user(self):
city = self._import_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)
@unittest.skipIf(configure.skip_test, configure.skip_reason)
def test_get_city_by_id(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(city.id)
self.assertEqual(retrieved_city.level_of_detail, self.city.level_of_detail.geometry)
self._db_factory.delete_city(city.id)
@unittest.skipIf(configure.skip_test, configure.skip_reason) @unittest.skipIf(configure.skip_test, configure.skip_reason)
def test_get_update_city(self): def test_get_update_city(self):
city = self._db_factory.persist_city(self.city, self.pickle_path, self.application.id, self._user.id) city = configure.import_db_factory.persist_city(configure.city,
self.city.name = "Ottawa" configure.pickle_path,
self._db_factory.update_city(city.id, self.city) configure.application.id,
updated_city = self._export_db_factory.get_city(city.id) configure._user.id)
self.assertEqual(updated_city.name, self.city.name) city.name = "Ottawa"
self._db_factory.delete_city(city.id) configure.import_db_factory.update_city(city.id, configure.city)
cities = configure.export_db_factory.cities_by_user_and_application(
configure.user.id,
configure.application.id)
for updated_city in cities:
if updated_city.id == city.id:
self.assertEqual(updated_city.name, city.name)
break
configure.import_db_factory.delete_city(city.id)

View File

@ -63,6 +63,7 @@ class TestExports(TestCase):
:parameter city: city :parameter city: city
:return: none :return: none
""" """
city.level_of_detail.surface_radiation = 2
for radiation in self._read_sra_file: for radiation in self._read_sra_file:
city_object_name = radiation.columns.values.tolist()[1].split(':')[1] city_object_name = radiation.columns.values.tolist()[1].split(':')[1]
building = city.city_object(city_object_name) building = city.city_object(city_object_name)