From 672b9874f2449e59c1740abbff1afb05506e81fc Mon Sep 17 00:00:00 2001 From: guille Date: Wed, 1 Feb 2023 06:05:12 -0500 Subject: [PATCH] Persistence refactory --- hub/persistence/__init__.py | 7 +- .../{db_config.py => configuration.py} | 6 +- hub/persistence/db_setup.py | 37 +++--- hub/persistence/models/__init__.py | 9 +- hub/persistence/models/application.py | 6 +- hub/persistence/models/city.py | 6 +- hub/persistence/models/city_object.py | 8 +- hub/persistence/models/simulation_results.py | 9 +- hub/persistence/models/user.py | 16 ++- hub/persistence/repositories/__init__.py | 4 +- .../{application_repo.py => application.py} | 25 ++-- .../repositories/{city_repo.py => city.py} | 78 ++++++------- hub/persistence/repositories/city_object.py | 0 .../repositories/heat_pump_simulation_repo.py | 108 ------------------ .../repositories/{user_repo.py => user.py} | 58 +++++----- .../{base_repo.py => repository.py} | 8 +- setup.py | 5 +- 17 files changed, 143 insertions(+), 247 deletions(-) rename hub/persistence/{db_config.py => configuration.py} (92%) rename hub/persistence/repositories/{application_repo.py => application.py} (74%) rename hub/persistence/repositories/{city_repo.py => city.py} (61%) create mode 100644 hub/persistence/repositories/city_object.py delete mode 100644 hub/persistence/repositories/heat_pump_simulation_repo.py rename hub/persistence/repositories/{user_repo.py => user.py} (57%) rename hub/persistence/{base_repo.py => repository.py} (68%) diff --git a/hub/persistence/__init__.py b/hub/persistence/__init__.py index caebc883..2a7eff84 100644 --- a/hub/persistence/__init__.py +++ b/hub/persistence/__init__.py @@ -1,6 +1,5 @@ -from .base_repo import BaseRepo -from .repositories.city_repo import CityRepo -from .repositories.heat_pump_simulation_repo import HeatPumpSimulationRepo +from .repository import Repository +from .repositories.city import City from .db_setup import DBSetup -from .repositories.user_repo import UserRepo +from .repositories.user import User from .models.user import UserRoles diff --git a/hub/persistence/db_config.py b/hub/persistence/configuration.py similarity index 92% rename from hub/persistence/db_config.py rename to hub/persistence/configuration.py index 4ef2dc14..9c01ebe1 100644 --- a/hub/persistence/db_config.py +++ b/hub/persistence/configuration.py @@ -13,10 +13,10 @@ from hub.hub_logger import logger Base = declarative_base() -class BaseConfiguration(object): +class Configuration: + """ + Configuration class to hold common persistence configuration """ - Base configuration class to hold common persistence configuration - """ def __init__(self, db_name: str, dotenv_path: str, app_env='TEST'): """ diff --git a/hub/persistence/db_setup.py b/hub/persistence/db_setup.py index e261f033..e0acfd29 100644 --- a/hub/persistence/db_setup.py +++ b/hub/persistence/db_setup.py @@ -1,13 +1,19 @@ +""" +Database setup +SPDX - License - Identifier: LGPL - 3.0 - or -later +Copyright © 2022 Concordia CERC group +Project Coder Peter Yefi peteryefi@gmail.com +""" -from hub.persistence import BaseRepo +from hub.persistence import Repository +from hub.persistence.models import Application from hub.persistence.models import City -from hub.persistence.models import HeatPumpSimulation +from hub.persistence.models import CityObject from hub.persistence.models import User from hub.persistence.models import UserRoles -from hub.persistence.models import Application -from hub.persistence.models import UserApplications -from hub.persistence.repositories import UserRepo -from hub.persistence.repositories import ApplicationRepo +from hub.persistence.models import SimulationResults +from hub.persistence.repositories import User as UserRepository +from hub.persistence.repositories import Application as ApplicationRepository from hub.hub_logger import logger @@ -20,14 +26,17 @@ class DBSetup: :param app_env: :param dotenv_path: """ - repo = BaseRepo(db_name=db_name, app_env=app_env, dotenv_path=dotenv_path) - User.__table__.create(bind=repo.engine, checkfirst=True) - City.__table__.create(bind=repo.engine, checkfirst=True) - Application.__table__.create(bind=repo.engine, checkfirst=True) - UserApplications.__table__.create(bind=repo.engine, checkfirst=True) - HeatPumpSimulation.__table__.create(bind=repo.engine, checkfirst=True) - self._user_repo = UserRepo(db_name=db_name, app_env=app_env, dotenv_path=dotenv_path) - self._application_repo = ApplicationRepo(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 + Application.__table__.create(bind=repository.engine, checkfirst=True) + City.__table__.create(bind=repository.engine, checkfirst=True) + CityObject.__table__.create(bind=repository.engine, checkfirst=True) + SimulationResults.__table__.create(bind=repository.engine, checkfirst=True) + User.__table__.create(bind=repository.engine, checkfirst=True) + + self._user_repo = UserRepository(db_name=db_name, app_env=app_env, dotenv_path=dotenv_path) + self._application_repo = ApplicationRepository(db_name=db_name, app_env=app_env, dotenv_path=dotenv_path) self._create_admin_user(self._user_repo, admin_password) self._create_admin_app(self._application_repo, application_uuid) diff --git a/hub/persistence/models/__init__.py b/hub/persistence/models/__init__.py index 0779c3fe..77f5c105 100644 --- a/hub/persistence/models/__init__.py +++ b/hub/persistence/models/__init__.py @@ -1,8 +1,5 @@ -from .city import City -from .heat_pump_simulation import HeatPumpSimulation -from .heat_pump_simulation import SimulationTypes -from .heat_pump_simulation import HeatPumpTypes -from .user import User, UserRoles -from .user_applications import UserApplications from .application import Application +from .city import City from .city_object import CityObject +from .simulation_results import SimulationResults +from .user import User, UserRoles diff --git a/hub/persistence/models/application.py b/hub/persistence/models/application.py index d6bdd6be..37f4d76d 100644 --- a/hub/persistence/models/application.py +++ b/hub/persistence/models/application.py @@ -11,14 +11,12 @@ from sqlalchemy.dialects.postgresql import UUID from sqlalchemy import Column, Integer, String, Sequence from sqlalchemy import DateTime -from hub.persistence.db_config import Base - -class Application(Base): +class Application: """ A model representation of an application """ - __tablename__ = "application" + __table__ = "application" id = Column(Integer, Sequence('application_id_seq'), primary_key=True) name = Column(String, nullable=False) description = Column(String, nullable=False) diff --git a/hub/persistence/models/city.py b/hub/persistence/models/city.py index a7ff3007..45dfeaab 100644 --- a/hub/persistence/models/city.py +++ b/hub/persistence/models/city.py @@ -10,13 +10,11 @@ import datetime from sqlalchemy import Column, Integer, String, Sequence, ForeignKey from sqlalchemy import DateTime, PickleType -from hub.persistence.db_config import Base - -class City(Base): +class City: """A model representation of a city """ - __tablename__ = "city" + __table__ = "city" id = Column(Integer, Sequence('city_id_seq'), primary_key=True) city = Column(PickleType, nullable=False) name = Column(String, nullable=False) diff --git a/hub/persistence/models/city_object.py b/hub/persistence/models/city_object.py index d97391b7..86ee798c 100644 --- a/hub/persistence/models/city_object.py +++ b/hub/persistence/models/city_object.py @@ -10,14 +10,11 @@ import datetime from sqlalchemy import Column, Integer, String, Sequence, ForeignKey, Float from sqlalchemy import DateTime -from hub.persistence.db_config import Base - - -class CityObject(Base): +class CityObject: """ A model representation of an application """ - __tablename__ = "city_object" + __table__ = "city_object" id = Column(Integer, Sequence('application_id_seq'), primary_key=True) city_id = Column(Integer, ForeignKey('city.id'), nullable=False) name = Column(String, nullable=False) @@ -41,4 +38,3 @@ class CityObject(Base): self.usage = usage self.volume = volume self.area = area - diff --git a/hub/persistence/models/simulation_results.py b/hub/persistence/models/simulation_results.py index 54519e54..a985148c 100644 --- a/hub/persistence/models/simulation_results.py +++ b/hub/persistence/models/simulation_results.py @@ -7,18 +7,15 @@ Project Coder Guille Gutierrez Guillermo.GutierrezMorote@concordia.ca import datetime -from sqlalchemy import Column, Integer, String, Sequence, ForeignKey, Float +from sqlalchemy import Column, Integer, String, Sequence, ForeignKey from sqlalchemy import DateTime from sqlalchemy.dialects.postgresql import JSONB -from hub.persistence.db_config import Base - - -class CityObject(Base): +class SimulationResults: """ A model representation of an application """ - __tablename__ = "simulation_results" + __table__ = "simulation_results" id = Column(Integer, Sequence('application_id_seq'), primary_key=True) city_id = Column(Integer, ForeignKey('city.id'), nullable=True) city_object_id = Column(Integer, ForeignKey('city_object.id'), nullable=True) diff --git a/hub/persistence/models/user.py b/hub/persistence/models/user.py index ce780a2f..b53132ae 100644 --- a/hub/persistence/models/user.py +++ b/hub/persistence/models/user.py @@ -5,14 +5,11 @@ Copyright © 2022 Concordia CERC group Project Coder Peter Yefi peteryefi@gmail.com """ +import datetime +import enum + from sqlalchemy import Column, Integer, String, Sequence from sqlalchemy import DateTime, Enum -from hub.persistence.db_config import Base -import datetime -from sqlalchemy.orm import validates -import re -import enum -from sqlalchemy.orm import relationship class UserRoles(enum.Enum): @@ -20,10 +17,11 @@ class UserRoles(enum.Enum): Hub_Reader = 'Hub_Reader' -class User(Base): - """A model representation of a city +class User: """ - __tablename__ = "user" + A model representation of a city + """ + __table__ = "user" id = Column(Integer, Sequence('user_id_seq'), primary_key=True) name = Column(String, nullable=False) password = Column(String, nullable=False) diff --git a/hub/persistence/repositories/__init__.py b/hub/persistence/repositories/__init__.py index c693ccad..158b8448 100644 --- a/hub/persistence/repositories/__init__.py +++ b/hub/persistence/repositories/__init__.py @@ -1,2 +1,2 @@ -from .user_repo import UserRepo -from .application_repo import ApplicationRepo +from .user import User +from .application import Application diff --git a/hub/persistence/repositories/application_repo.py b/hub/persistence/repositories/application.py similarity index 74% rename from hub/persistence/repositories/application_repo.py rename to hub/persistence/repositories/application.py index ea1dca35..8ce303ed 100644 --- a/hub/persistence/repositories/application_repo.py +++ b/hub/persistence/repositories/application.py @@ -12,11 +12,11 @@ from sqlalchemy import select from sqlalchemy.exc import SQLAlchemyError from hub.hub_logger import logger -from hub.persistence import BaseRepo -from hub.persistence.models import Application +from hub.persistence import Repository +from hub.persistence.models import Application as Model -class ApplicationRepo(BaseRepo): +class Application(Repository): _instance = None def __init__(self, db_name: str, dotenv_path: str, app_env: str): @@ -27,14 +27,14 @@ class ApplicationRepo(BaseRepo): Implemented for a singleton pattern """ if cls._instance is None: - cls._instance = super(ApplicationRepo, cls).__new__(cls) + cls._instance = super(Application, cls).__new__(cls) return cls._instance - def insert(self, name: str, description: str, application_uuid: str) -> Union[Application, Dict]: + def insert(self, name: str, description: str, application_uuid: str) -> Union[Model, Dict]: application = self.get_by_uuid(application_uuid) if application is None: try: - application = Application(name=name, description=description, application_uuid=application_uuid) + application = Model(name=name, description=description, application_uuid=application_uuid) self.session.add(application) self.session.flush() self.session.commit() @@ -53,21 +53,24 @@ class ApplicationRepo(BaseRepo): :return: """ try: - self.session.query(Application).filter(Application.application_uuid == application_uuid) \ - .update({'name': name, 'description': description, 'updated': datetime.datetime.utcnow()}) + self.session.query(Model).filter( + Model.application_uuid == application_uuid + ).update({'name': name, 'description': description, 'updated': datetime.datetime.utcnow()}) self.session.commit() except SQLAlchemyError as err: logger.error(f'Error while updating application: {err}') return {'err_msg': 'Error occurred while updating application'} - def get_by_uuid(self, application_uuid: str) -> [Application]: + def get_by_uuid(self, application_uuid: str) -> [Model]: """ Fetch Application based on the application uuid :param application_uuid: the application uuid :return: [Application] with the provided application_uuid """ try: - return self.session.execute(select(Application).where(Application.application_uuid == application_uuid)).first() + return self.session.execute(select(Model).where( + Model.application_uuid == application_uuid) + ).first() except SQLAlchemyError as err: logger.error(f'Error while fetching application by application_uuid: {err}') @@ -78,7 +81,7 @@ class ApplicationRepo(BaseRepo): :return: None """ try: - self.session.query(Application).filter(Application.application_uuid == application_uuid).delete() + self.session.query(Model).filter(Model.application_uuid == application_uuid).delete() self.session.commit() except SQLAlchemyError as err: logger.error(f'Error while deleting application: {err}') diff --git a/hub/persistence/repositories/city_repo.py b/hub/persistence/repositories/city.py similarity index 61% rename from hub/persistence/repositories/city_repo.py rename to hub/persistence/repositories/city.py index 985ab26b..08ded818 100644 --- a/hub/persistence/repositories/city_repo.py +++ b/hub/persistence/repositories/city.py @@ -12,14 +12,14 @@ from typing import Union, Dict from sqlalchemy import select from sqlalchemy.exc import SQLAlchemyError -from hub.city_model_structure.city import City +from hub.city_model_structure.city import City as CityHub from hub.hub_logger import logger -from hub.persistence import BaseRepo -from hub.persistence.models import City as DBCity +from hub.persistence import Repository +from hub.persistence.models import City as Model from hub.version import __version__ -class CityRepo(BaseRepo): +class City(Repository): _instance = None def __init__(self, db_name: str, dotenv_path: str, app_env: str): @@ -30,10 +30,10 @@ class CityRepo(BaseRepo): Implemented for a singleton pattern """ if cls._instance is None: - cls._instance = super(CityRepo, cls).__new__(cls) + cls._instance = super(City, cls).__new__(cls) return cls._instance - def insert(self, city: City, application_id, user_id: int) -> Union[City, Dict]: + def insert(self, city: CityHub, application_id, user_id: int) -> Union[Model, Dict]: """ Insert a city :param city: The complete city instance @@ -42,9 +42,14 @@ class CityRepo(BaseRepo): :return: City and Dictionary """ try: - release = __version__ - db_city = DBCity(pickle.dumps(city), city.name, city.level_of_detail, city.climate_file, application_id, user_id, - release) + db_city = Model( + pickle.dumps(city), + city.name, + city.level_of_detail, + city.climate_file, + application_id, + user_id, + __version__) self.session.add(db_city) self.session.flush() @@ -53,18 +58,18 @@ class CityRepo(BaseRepo): except SQLAlchemyError as err: logger.error(f'An error occurred while creating city: {err}') - def get_by_id(self, city_id: int) -> DBCity: + def get_by_id(self, city_id: int) -> Model: """ Fetch a City based on the id :param city_id: the city id :return: a city """ try: - return self.session.execute(select(DBCity).where(DBCity.id == city_id)).first()[0] + return self.session.execute(select(Model).where(Model.id == city_id)).first()[0] except SQLAlchemyError as err: logger.error(f'Error while fetching city: {err}') - def _get_by_hub_version(self, hub_release: str, city_name: str) -> City: + def _get_by_hub_version_and_name(self, hub_release: str, city_name: str) -> Model: """ Fetch a City based on the name and hub project :param hub_release: the hub release @@ -72,55 +77,50 @@ class CityRepo(BaseRepo): :return: a city """ try: - return self.session.execute(select(DBCity) - .where(DBCity.hub_release == hub_release, DBCity.name == city_name)).first() + return self.session.execute(select(Model) + .where(Model.hub_release == hub_release, Model.name == city_name) + ).first() except SQLAlchemyError as err: logger.error(f'Error while fetching city: {err}') - def update(self, city_id: int, city: City): - """ - Updates a city - :param city_id: the id of the city to be updated - :param city: the city object - :return: - """ - try: - self.session.query(DBCity).filter(DBCity.id == city_id) \ - .update({ - 'name': city.name, 'srs_name': city.srs_name, 'country_code': city.country_code, 'longitude': city.longitude, - 'latitude': city.latitude, 'time_zone': city.time_zone, 'lower_corner': city.lower_corner.tolist(), - 'upper_corner': city.upper_corner.tolist(), 'climate_reference_city': city.climate_reference_city, - 'updated': datetime.datetime.utcnow() - }) - - self.session.commit() - except SQLAlchemyError as err: - logger.error(f'Error while updating city: {err}') - - def get_by_name(self, city_name: str) -> [DBCity]: + def get_by_name(self, city_name: str) -> [Model]: """ Fetch city based on the name :param city_name: the name of the building :return: [ModelCity] with the provided name """ try: - result_set = self.session.execute(select(DBCity).where(DBCity.name == city_name)) + result_set = self.session.execute(select(Model).where(Model.name == city_name)) return [building[0] for building in result_set] except SQLAlchemyError as err: logger.error(f'Error while fetching city by name: {err}') - def get_by_user(self, user_id: int) -> [DBCity]: + def get_by_user(self, user_id: int) -> [Model]: """ Fetch city based on the user who created it :param user_id: the id of the user :return: [ModelCity] with the provided name """ try: - result_set = self.session.execute(select(DBCity).where(DBCity.user_id == user_id)) + result_set = self.session.execute(select(Model).where(Model.user_id == user_id)) return [building[0] for building in result_set] except SQLAlchemyError as err: logger.error(f'Error while fetching city by name: {err}') + def update(self, city_id: int, city: CityHub): + """ + Updates a city name (other updates makes no sense) + :param city_id: the id of the city to be updated + :param city: the city object + :return: + """ + try: + now = datetime.datetime.utcnow() + self.session.query(Model).filter(Model.id == city_id).update({'name': city.name,'updated': now}) + self.session.commit() + except SQLAlchemyError as err: + logger.error(f'Error while updating city: {err}') + def delete_city(self, city_id: int): """ Deletes a City with the id @@ -128,7 +128,7 @@ class CityRepo(BaseRepo): :return: a city """ try: - self.session.query(DBCity).filter(DBCity.id == city_id).delete() + self.session.query(Model).filter(Model.id == city_id).delete() self.session.commit() except SQLAlchemyError as err: logger.error(f'Error while fetching city: {err}') diff --git a/hub/persistence/repositories/city_object.py b/hub/persistence/repositories/city_object.py new file mode 100644 index 00000000..e69de29b diff --git a/hub/persistence/repositories/heat_pump_simulation_repo.py b/hub/persistence/repositories/heat_pump_simulation_repo.py deleted file mode 100644 index 4f0ef60e..00000000 --- a/hub/persistence/repositories/heat_pump_simulation_repo.py +++ /dev/null @@ -1,108 +0,0 @@ -""" -Heat pump simulation repository with database CRUD operations -SPDX - License - Identifier: LGPL - 3.0 - or -later -Copyright © 2022 Concordia CERC group -Project Coder Peter Yefi peteryefi@gmail.com -""" - -from hub.persistence import BaseRepo, CityRepo -from sqlalchemy.exc import SQLAlchemyError -from sqlalchemy import select -from hub.persistence.models import HeatPumpSimulation -from typing import Union, Dict -from hub.hub_logger import logger - - -class HeatPumpSimulationRepo(BaseRepo): - _instance = None - - def __init__(self, db_name, dotenv_path, app_env): - super().__init__(db_name, dotenv_path, app_env) - self._city_repo = CityRepo(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: - cls._instance = super(HeatPumpSimulationRepo, cls).__new__(cls) - return cls._instance - - def insert(self, hp_sim_data: Dict, city_id: int) -> Union[HeatPumpSimulation, Dict]: - """ - Inserts the results of heat pump simulation - :param hp_sim_data: dictionary with heatpump the simulation inputs and output - :param city_id: the city that was used in running the simulation - :return: HeatPumpSimulation - """ - - city = self._city_repo.get_by_id(city_id) - if city is None: - return {'message': 'city not found in database'} - - try: - hp_simulation = HeatPumpSimulation(city_id, hp_sim_data["HourlyElectricityDemand"], - hp_sim_data["DailyElectricityDemand"], hp_sim_data["MonthlyElectricityDemand"], - hp_sim_data["DailyFossilFuelConsumption"], - hp_sim_data["MonthlyFossilFuelConsumption"]) - hp_simulation.city_id = city_id - hp_simulation.end_year = hp_sim_data["EndYear"] - hp_simulation.start_year = hp_sim_data["StartYear"] - hp_simulation.max_demand_storage_hour = hp_sim_data["HoursOfStorageAtMaxDemand"] - hp_simulation.max_hp_energy_input = hp_sim_data["MaximumHPEnergyInput"] - hp_simulation.building_supply_temp = hp_sim_data["BuildingSuppTemp"] - hp_simulation.temp_difference = hp_sim_data["TemperatureDifference"] - hp_simulation.fuel_lhv = hp_sim_data["FuelLHV"] - hp_simulation.fuel_price = hp_sim_data["FuelPrice"] - hp_simulation.fuel_efficiency = hp_sim_data["FuelEF"] - hp_simulation.fuel_density = hp_sim_data["FuelDensity"] - hp_simulation.hp_supply_temp = hp_sim_data["HPSupTemp"] - hp_simulation.simulation_type = hp_sim_data["SimulationType"] - hp_simulation.heat_pump_model = hp_sim_data["HeatPumpModel"] - hp_simulation.heat_pump_type = hp_sim_data["HeatPumpType"] - - # Persist heat pump simulation data - self.session.add(hp_simulation) - self.session.flush() - self.session.commit() - return hp_simulation - except SQLAlchemyError as err: - logger.error(f'Error while saving heat pump simulation data: {err}') - except KeyError as err: - logger.error(f'A required field is missing in your heat pump simulation dictionary: {err}') - - def get_by_id(self, hp_simulation_id: int) -> HeatPumpSimulation: - """ - Fetches heat pump simulation data - :param hp_simulation_id: the city id - :return: a HeatPumpSimulation - """ - try: - return self.session.execute(select(HeatPumpSimulation).where(HeatPumpSimulation.id == hp_simulation_id)).first()[ - 0] - except SQLAlchemyError as err: - logger.error(f'Error while fetching city: {err}') - - def get_by_city(self, city_id: int) -> [HeatPumpSimulation]: - """ - Fetch heat pump simulation results by city - :param city_id: the name of the building - :return: [HeatPumpSimulation] with the provided name - """ - try: - result_set = self.session.execute(select(HeatPumpSimulation).where(HeatPumpSimulation.city_id == city_id)) - return [sim_data[0] for sim_data in result_set] - except SQLAlchemyError as err: - logger.error(f'Error while fetching city by name: {err}') - - def delete_hp_simulation(self, hp_simulation_id: int): - """ - Deletes a heat pump simulation results - :param hp_simulation_id: the heat pump simulation results id - :return: - """ - try: - self.session.query(HeatPumpSimulation).filter(HeatPumpSimulation.id == hp_simulation_id).delete() - self.session.commit() - except SQLAlchemyError as err: - logger.error(f'Error while fetching city: {err}') diff --git a/hub/persistence/repositories/user_repo.py b/hub/persistence/repositories/user.py similarity index 57% rename from hub/persistence/repositories/user_repo.py rename to hub/persistence/repositories/user.py index 2ff5a839..1809874e 100644 --- a/hub/persistence/repositories/user_repo.py +++ b/hub/persistence/repositories/user.py @@ -5,10 +5,10 @@ Copyright © 2022 Concordia CERC group Project Coder Peter Yefi peteryefi@gmail.com """ -from hub.persistence import BaseRepo +from hub.persistence import Repository from sqlalchemy.exc import SQLAlchemyError from sqlalchemy import select -from hub.persistence.models import User +from hub.persistence.models import User as Model from hub.persistence.models import UserRoles from hub.helpers.auth import Auth from typing import Union, Dict @@ -16,7 +16,7 @@ from hub.hub_logger import logger import datetime -class UserRepo(BaseRepo): +class User(Repository): _instance = None def __init__(self, db_name: str, dotenv_path: str, app_env: str): @@ -27,14 +27,14 @@ class UserRepo(BaseRepo): Implemented for a singleton pattern """ if cls._instance is None: - cls._instance = super(UserRepo, cls).__new__(cls) + cls._instance = super(User, cls).__new__(cls) return cls._instance - def insert(self, name: str, password: str, role: UserRoles, application_id: int) -> Union[User, Dict]: + def insert(self, name: str, password: str, role: UserRoles, application_id: int) -> Union[Model, Dict]: user = self.get_by_name_and_application(name, application_id) if user is None: try: - user = User(name=name, password=Auth.hash_password(password), role=role, application_id=application_id) + user = Model(name=name, password=Auth.hash_password(password), role=role, application_id=application_id) self.session.add(user) self.session.flush() self.session.commit() @@ -42,38 +42,42 @@ class UserRepo(BaseRepo): except SQLAlchemyError as err: logger.error(f'An error occurred while creating user: {err}') else: - return {'message': f'user with {email} email already exists'} + return {'message': f'user {name} already exists for that application'} - def update(self, user_id: int, name: str, email: str, password: str, role: UserRoles) -> Union[Dict, None]: + def update(self, user_id: int, name: str, password: str, role: UserRoles) -> Union[Dict, None]: """ Updates a user :param user_id: the id of the user to be updated :param name: the name of the user - :param email: the email of the user :param password: the password of the user :param role: the role of the user :return: """ try: - if Auth.validate_password(password): - self.session.query(User).filter(User.id == user_id) \ - .update({'name': name, 'email': email, 'password': Auth.hash_password(password), 'role': role, - 'updated': datetime.datetime.utcnow()}) + self.session.query(Model).filter(Model.id == user_id).update({ + 'name': name, + 'password': Auth.hash_password(password), + 'role': role, + 'updated': datetime.datetime.utcnow() + }) self.session.commit() except SQLAlchemyError as err: logger.error(f'Error while updating user: {err}') return {'err_msg': 'Error occurred while updated user'} - def get_by_email(self, email: str) -> [User]: + def get_by_name_and_application(self, name: str, application_id: int) -> [Model]: """ Fetch user based on the email address - :param email: the email of the user - :return: [User] with the provided email + :param name: User name + :param application_id: User application name + :return: [User] matching the search criteria """ try: - return self.session.execute(select(User).where(User.email == email)).first() + return self.session.execute( + select(Model).where(Model.name == name and Model.application_id == application_id) + ).first() except SQLAlchemyError as err: - logger.error(f'Error while fetching user by email: {err}') + logger.error(f'Error while fetching user by name and application: {err}') def delete_user(self, user_id: int): """ @@ -82,25 +86,27 @@ class UserRepo(BaseRepo): :return: None """ try: - self.session.query(User).filter(User.id == user_id).delete() + self.session.query(Model).filter(Model.id == user_id).delete() self.session.commit() except SQLAlchemyError as err: logger.error(f'Error while fetching user: {err}') - def get_user_by_email_and_password(self, email: str, password: str) -> [User]: + def get_user_by_name_application_and_password(self, name: str, password: str, application_id: int) -> [Model]: """ Fetch user based on the email and password - :param email: the email of the user - :param password: the password of the user + :param name: User name + :param password: User password + :param application_id: User password + :return: [User] with the provided email and password """ try: - user = self.session.execute(select(User).where(User.email == email)).first() + user = self.session.execute( + select(Model).where(Model.name == name and Model.application_id == application_id) + ).first() if user: if Auth.check_password(password, user[0].password): return user - else: - return {'message': 'Wrong email/password combination'} - return {'message': 'user not found'} + return {'message': 'invalid login information'} except SQLAlchemyError as err: logger.error(f'Error while fetching user by email: {err}') diff --git a/hub/persistence/base_repo.py b/hub/persistence/repository.py similarity index 68% rename from hub/persistence/base_repo.py rename to hub/persistence/repository.py index 97d5ac1e..d9cea244 100644 --- a/hub/persistence/base_repo.py +++ b/hub/persistence/repository.py @@ -5,17 +5,17 @@ Copyright © 2022 Concordia CERC group Project Coder Peter Yefi peteryefi@gmail.com """ -from hub.persistence.db_config import BaseConfiguration +from hub.persistence.configuration import Configuration from sqlalchemy import create_engine from sqlalchemy.orm import Session -class BaseRepo: +class Repository: def __init__(self, db_name, dotenv_path: str, app_env='TEST'): try: - self.config = BaseConfiguration(db_name, dotenv_path, app_env) - self.engine = create_engine(self.config.conn_string()) + self.configuration = Configuration(db_name, dotenv_path, app_env) + self.engine = create_engine(self.configuration.conn_string()) self.session = Session(self.engine) except ValueError as err: print(f'Missing value for credentials: {err}') diff --git a/setup.py b/setup.py index 44e91fa7..ed618602 100644 --- a/setup.py +++ b/setup.py @@ -13,7 +13,10 @@ setup( description="CERC Hub consist in a set of classes (Central data model), importers and exporters to help researchers " "to create better and sustainable cities", long_description="CERC Hub consist in a set of classes (Central data model), importers and exporters to help " - "researchers to create better and sustainable cities", + "researchers to create better and sustainable cities.\n\nDevelop at Concordia university in canada " + "as part of the research group from the next generation cities institute our aim among others it's " + "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", classifiers=[ "License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)", "Programming Language :: Python",