Full implementation of basic persistence tasks for the one_shot admin tool.

This commit is contained in:
Guille Gutierrez 2023-02-02 13:00:58 -05:00
parent a34206e6ff
commit 16adfaa1a8
10 changed files with 102 additions and 23 deletions

View File

@ -6,6 +6,8 @@ Project CoderPeter Yefi peteryefi@gmail.com
""" """
from hub.persistence import City from hub.persistence import City
from hub.persistence import Application from hub.persistence import Application
from hub.persistence import User
from hub.persistence import CityObject
class DBFactory: class DBFactory:
@ -16,6 +18,8 @@ class DBFactory:
def __init__(self, db_name, app_env, dotenv_path): def __init__(self, db_name, app_env, dotenv_path):
self._city = City(db_name=db_name, app_env=app_env, dotenv_path=dotenv_path) self._city = City(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._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._city_object = CityObject(db_name=db_name, app_env=app_env, dotenv_path=dotenv_path)
def get_city(self, city_id): def get_city(self, city_id):
""" """
@ -41,3 +45,9 @@ class DBFactory:
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)
def user_info(self, name, password, application_id):
return self._user.get_by_name_application_and_password(name, password, application_id)
def building_info(self, name, city_id):
return self._city_object.get_by_name_and_city(name, city_id)

View File

@ -6,6 +6,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
class DBFactory: class DBFactory:
@ -15,10 +16,14 @@ 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)
def persist_city(self, application_id: int, user_id: int, city: City): def persist_city(self, city: City, application_id: int, user_id: int):
""" """
Persist city into postgres database Persist city into postgres database
:param city: City to be stored
:param application_id: Application id owning this city
:param user_id: User who create the city
""" """
return self._city_repository.insert(city, application_id, user_id) return self._city_repository.insert(city, application_id, user_id)
@ -36,3 +41,13 @@ class DBFactory:
:param city_id: the id of the city to get :param city_id: the id of the city to get
""" """
self._city_repository.delete(city_id) self._city_repository.delete(city_id)
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
:param name: simulation and simulation engine name
:param values: simulation values in json format
:param city_id: city id or None
:param city_object_id: city object id or None
"""
self._simulation_results.insert(name, values,city_id, city_object_id)

View File

@ -19,12 +19,18 @@ class CityGml:
""" """
CityGml class CityGml class
""" """
def __init__(self, path, extrusion_height_field=None, year_of_construction_field=None, function_field=None): def __init__(self,
path,
extrusion_height_field=None,
year_of_construction_field='yearOfConstruction',
function_field='function',
function_to_hub=None):
self._city = None self._city = None
self._lod = None self._lod = None
self._lod1_tags = ['lod1Solid', 'lod1MultiSurface'] self._lod1_tags = ['lod1Solid', 'lod1MultiSurface']
self._lod2_tags = ['lod2Solid', 'lod2MultiSurface', 'lod2MultiCurve'] self._lod2_tags = ['lod2Solid', 'lod2MultiSurface', 'lod2MultiCurve']
self._extrusion_height_field = extrusion_height_field self._extrusion_height_field = extrusion_height_field
self._function_to_hub = function_to_hub
self._year_of_construction_field = year_of_construction_field self._year_of_construction_field = year_of_construction_field
if function_field is None: if function_field is None:
function_field = 'function' function_field = 'function'
@ -110,12 +116,15 @@ class CityGml:
name = city_object['@id'] name = city_object['@id']
function = None function = None
year_of_construction = None year_of_construction = None
if 'yearOfConstruction' in city_object: if self._year_of_construction_field in city_object:
year_of_construction = city_object['yearOfConstruction'] year_of_construction = city_object[self._year_of_construction_field]
if 'function' in city_object: if self._function_field in city_object:
function = city_object[self._function_field] function = city_object[self._function_field]
if type(function) != str: if type(function) != str:
function = function['#text'] function = function['#text']
if self._function_to_hub is not None:
# use the transformation dictionary to retrieve the proper function
function = self._function_to_hub[function]
if any(key in city_object for key in self._lod1_tags): if any(key in city_object for key in self._lod1_tags):
if self._lod is None or self._lod > 1: if self._lod is None or self._lod > 1:

View File

@ -26,7 +26,12 @@ class Geojson:
X = 0 X = 0
Y = 1 Y = 1
def __init__(self, path, extrusion_height_field=None, year_of_construction_field=None, function_field=None): def __init__(self,
path,
extrusion_height_field=None,
year_of_construction_field=None,
function_field=None,
function_to_hub=None):
# todo: destination epsg should change according actual the location # todo: destination epsg should change according actual the location
self._transformer = Transformer.from_crs('epsg:4326', 'epsg:26911') self._transformer = Transformer.from_crs('epsg:4326', 'epsg:26911')
self._min_x = cte.MAX_FLOAT self._min_x = cte.MAX_FLOAT
@ -38,6 +43,7 @@ class Geojson:
self._extrusion_height_field = extrusion_height_field self._extrusion_height_field = extrusion_height_field
self._year_of_construction_field = year_of_construction_field self._year_of_construction_field = year_of_construction_field
self._function_field = function_field self._function_field = function_field
self._function_to_hub = function_to_hub
with open(path) as json_file: with open(path) as json_file:
self._geojson = json.loads(json_file.read()) self._geojson = json.loads(json_file.read())
@ -118,6 +124,9 @@ class Geojson:
function = None function = None
if self._function_field is not None: if self._function_field is not None:
function = feature['properties'][self._function_field] function = feature['properties'][self._function_field]
if self._function_to_hub is not None:
# use the transformation dictionary to retrieve the proper function
function = self._function_to_hub[function]
geometry = feature['geometry'] geometry = feature['geometry']
if 'id' in feature: if 'id' in feature:
building_name = feature['id'] building_name = feature['id']

View File

@ -26,7 +26,8 @@ class GeometryFactory:
data_frame=None, data_frame=None,
height_field=None, height_field=None,
year_of_construction_field=None, year_of_construction_field=None,
function_field=None): function_field=None,
function_to_hub=None):
self._file_type = '_' + file_type.lower() self._file_type = '_' + file_type.lower()
class_funcs = validate_import_export_type(GeometryFactory) class_funcs = validate_import_export_type(GeometryFactory)
if self._file_type not in class_funcs: if self._file_type not in class_funcs:
@ -38,6 +39,7 @@ class GeometryFactory:
self._height_field = height_field self._height_field = height_field
self._year_of_construction_field = year_of_construction_field self._year_of_construction_field = year_of_construction_field
self._function_field = function_field self._function_field = function_field
self._function_to_hub = function_to_hub
@property @property
def _citygml(self) -> City: def _citygml(self) -> City:
@ -45,7 +47,11 @@ class GeometryFactory:
Enrich the city by using CityGML information as data source Enrich the city by using CityGML information as data source
:return: City :return: City
""" """
return CityGml(self._path, self._height_field, self._year_of_construction_field, self._function_field).city return CityGml(self._path,
self._height_field,
self._year_of_construction_field,
self._function_field,
self._function_to_hub).city
@property @property
def _obj(self) -> City: def _obj(self) -> City:
@ -71,7 +77,11 @@ class GeometryFactory:
Enrich the city by using Geojson information as data source Enrich the city by using Geojson information as data source
:return: City :return: City
""" """
return Geojson(self._path, self._height_field, self._year_of_construction_field, self._function_field).city return Geojson(self._path,
self._height_field,
self._year_of_construction_field,
self._function_field,
self._function_to_hub).city
@property @property
def _osm_subway(self) -> City: def _osm_subway(self) -> City:

View File

@ -1,6 +1,8 @@
from .repository import Repository from .repository import Repository
from .repositories.city import City from .repositories.city import City
from .repositories.application import Application from .repositories.application import Application
from .repositories.simulation_results import SimulationResults
from .repositories.city_object import CityObject
from .db_setup import DBSetup from .db_setup import DBSetup
from .repositories.user import User from .repositories.user import User
from .models.user import UserRoles from .models.user import UserRoles

View File

@ -16,6 +16,7 @@ from hub.city_model_structure.city import City as CityHub
from hub.hub_logger import logger from hub.hub_logger import logger
from hub.persistence import Repository from hub.persistence import Repository
from hub.persistence.models import City as Model from hub.persistence.models import City as Model
from hub.persistence.models import CityObject
from hub.version import __version__ from hub.version import __version__
@ -45,17 +46,35 @@ class City(Repository):
db_city = Model( db_city = Model(
pickle.dumps(city), pickle.dumps(city),
city.name, city.name,
city.level_of_detail, city.level_of_detail.geometry,
city.climate_file, 'None' if city.climate_file is None else city.climate_file,
application_id, application_id,
user_id, user_id,
__version__) __version__)
self.session.add(db_city) self.session.add(db_city)
self.session.flush() self.session.flush()
for building in city.buildings:
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()
db_city_object = CityObject(db_city.id,
building.name,
building.alias,
building.type,
building.year_of_construction,
building.function,
object_usage,
building.volume,
building.floor_area)
self.session.add(db_city_object)
self.session.flush()
self.session.commit() self.session.commit()
return db_city return db_city
except SQLAlchemyError as err: except SQLAlchemyError as err:
print(f'An error occurred while creating city: {err}')
logger.error(f'An error occurred while creating city: {err}') logger.error(f'An error occurred while creating city: {err}')
def update(self, city_id: int, city: CityHub): def update(self, city_id: int, city: CityHub):

View File

@ -77,7 +77,7 @@ class CityObject(Repository):
for usage in internal_zone.usages: for usage in internal_zone.usages:
object_usage = f'{object_usage}{usage.name}_{usage.percentage} ' object_usage = f'{object_usage}{usage.name}_{usage.percentage} '
object_usage = object_usage.rstrip() object_usage = object_usage.rstrip()
self.session.query(Model).filter(Model.name == building.name and Model.city_id == city_id).update( self.session.query(Model).filter(Model.name == building.name, Model.city_id == city_id).update(
{'name': building.name, {'name': building.name,
'alias': building.alias, 'alias': building.alias,
'object_type': building.type, 'object_type': building.type,
@ -100,7 +100,7 @@ class CityObject(Repository):
:return: None :return: None
""" """
try: try:
self.session.query(Model).filter(Model.city_id == city_id and Model.name == name).delete() self.session.query(Model).filter(Model.city_id == city_id, Model.name == name).delete()
self.session.commit() self.session.commit()
except SQLAlchemyError as err: except SQLAlchemyError as err:
logger.error(f'Error while deleting application: {err}') logger.error(f'Error while deleting application: {err}')
@ -113,8 +113,13 @@ class CityObject(Repository):
:return: [CityObject] with the provided name belonging to the city with id city_id :return: [CityObject] with the provided name belonging to the city with id city_id
""" """
try: try:
return self.session.execute(select(Model).where( _city_object = self.session.execute(select(Model).where(
Model.name == name and Model.city_id == city_id Model.name == name, Model.city_id == city_id
)).first() )).first()
print(city_id, name, _city_object)
if _city_object is None:
return None
return _city_object[0]
except SQLAlchemyError as err: except SQLAlchemyError as err:
print(err)
logger.error(f'Error while fetching application by application_uuid: {err}') logger.error(f'Error while fetching application by application_uuid: {err}')

View File

@ -74,14 +74,14 @@ class SimulationResults(Repository):
""" """
try: try:
if city_id is not None: if city_id is not None:
self.session.query(Model).filter(Model.name == name and Model.city_id == city_id).update( self.session.query(Model).filter(Model.name == name, Model.city_id == city_id).update(
{ {
'values': values, 'values': values,
'updated': datetime.datetime.utcnow() 'updated': datetime.datetime.utcnow()
}) })
self.session.commit() self.session.commit()
elif city_object_id is not None: elif city_object_id is not None:
self.session.query(Model).filter(Model.name == name and Model.city_object_id == city_object_id).update( self.session.query(Model).filter(Model.name == name, Model.city_object_id == city_object_id).update(
{ {
'values': values, 'values': values,
'updated': datetime.datetime.utcnow() 'updated': datetime.datetime.utcnow()
@ -104,10 +104,10 @@ class SimulationResults(Repository):
""" """
try: try:
if city_id is not None: if city_id is not None:
self.session.query(Model).filter(Model.name == name and Model.city_id == city_id).delete() self.session.query(Model).filter(Model.name == name, Model.city_id == city_id).delete()
self.session.commit() self.session.commit()
elif city_object_id is not None: elif city_object_id is not None:
self.session.query(Model).filter(Model.name == name and Model.city_object_id == city_object_id).delete() self.session.query(Model).filter(Model.name == name, Model.city_object_id == city_object_id).delete()
self.session.commit() self.session.commit()
else: else:
return {'message': 'Missing either city_id or city_object_id'} return {'message': 'Missing either city_id or city_object_id'}

View File

@ -94,12 +94,12 @@ class User(Repository):
""" """
try: try:
return self.session.execute( return self.session.execute(
select(Model).where(Model.name == name and Model.application_id == application_id) select(Model).where(Model.name == name, Model.application_id == application_id)
).first() ).first()
except SQLAlchemyError as err: except SQLAlchemyError as err:
logger.error(f'Error while fetching user by name and application: {err}') logger.error(f'Error while fetching user by name and application: {err}')
def get_user_by_name_application_and_password(self, name: str, password: str, application_id: int) -> [Model]: def get_by_name_application_and_password(self, name: str, password: str, application_id: int) -> [Model]:
""" """
Fetch user based on the email and password Fetch user based on the email and password
:param name: User name :param name: User name
@ -110,11 +110,11 @@ class User(Repository):
""" """
try: try:
user = self.session.execute( user = self.session.execute(
select(Model).where(Model.name == name and Model.application_id == application_id) select(Model).where(Model.name == name, Model.application_id == application_id)
).first() ).first()
if user: if user:
if Auth.check_password(password, user[0].password): if Auth.check_password(password, user[0].password):
return user return user[0]
return {'message': 'invalid login information'} return {'message': 'invalid login information'}
except SQLAlchemyError as err: except SQLAlchemyError as err:
logger.error(f'Error while fetching user by email: {err}') logger.error(f'Error while fetching user by email: {err}')