From 59787573485c1a391c5d6a2b0f137044900010da Mon Sep 17 00:00:00 2001 From: guille Date: Tue, 31 Jan 2023 13:11:39 -0500 Subject: [PATCH] Partial correction of persistence --- hub/DEPLOYMENT.md | 2 +- hub/city_model_structure/building.py | 16 +-- hub/helpers/auth.py | 14 --- hub/persistence/db_setup.py | 34 ++++-- hub/persistence/models/__init__.py | 1 + hub/persistence/models/application.py | 5 +- hub/persistence/models/city.py | 40 +++---- hub/persistence/models/city_object.py | 44 ++++++++ .../models/heat_pump_simulation.py | 83 -------------- hub/persistence/models/simulation_results.py | 34 ++++++ hub/persistence/models/user.py | 14 +-- hub/persistence/models/user_applications.py | 30 ----- hub/persistence/repositories/__init__.py | 1 + .../repositories/application_repo.py | 84 ++++++++++++++ hub/persistence/repositories/city_repo.py | 80 +++++-------- hub/persistence/repositories/user_repo.py | 15 ++- hub/version.py | 1 + pyproject.toml | 48 +------- setup.py | 106 ++++++++++++++---- 19 files changed, 342 insertions(+), 310 deletions(-) create mode 100644 hub/persistence/models/city_object.py delete mode 100644 hub/persistence/models/heat_pump_simulation.py create mode 100644 hub/persistence/models/simulation_results.py delete mode 100644 hub/persistence/models/user_applications.py create mode 100644 hub/persistence/repositories/application_repo.py create mode 100644 hub/version.py diff --git a/hub/DEPLOYMENT.md b/hub/DEPLOYMENT.md index 05299696..422b1aa7 100644 --- a/hub/DEPLOYMENT.md +++ b/hub/DEPLOYMENT.md @@ -74,7 +74,7 @@ from hub.persistence import DBSetup from pathlib import Path dotenv_path = (Path(__file__).parent / '.env').resolve() -DBSetup(db_name='hub_db', app_env='PROD', dotenv_path=dotenv_path) +DBSetup(db_name='hub_db', app_env='PROD', dotenv_path=dotenv_path, admin_password="your password here", application_uuid="your admin application uuid") ``` The *DBSetUp* class also creates a default admin user with default credentials that can be changed. with the import UserFactory class. The admin user (name, email, password and role) is logged into the console after it is created by the diff --git a/hub/city_model_structure/building.py b/hub/city_model_structure/building.py index 040af1c9..ea27756b 100644 --- a/hub/city_model_structure/building.py +++ b/hub/city_model_structure/building.py @@ -34,7 +34,7 @@ class Building(CityObject): self._roof_type = None self._internal_zones = None self._shell = None - self._human_readable_name = None + self._alias = None self._type = 'building' self._heating = dict() self._cooling = dict() @@ -404,16 +404,16 @@ class Building(CityObject): return False @property - def human_readable_name(self): + def alias(self): """ - Get the human-readable name for the building + Get the alias name for the building :return: str """ - return self._human_readable_name + return self._alias - @human_readable_name.setter - def human_readable_name(self, value): + @alias.setter + def alias(self, value): """ - Set the human-readable name for the building + Set the alias name for the building """ - self._human_readable_name = value + self._alias = value diff --git a/hub/helpers/auth.py b/hub/helpers/auth.py index f6cefa66..a9cc34b9 100644 --- a/hub/helpers/auth.py +++ b/hub/helpers/auth.py @@ -11,20 +11,6 @@ import re class Auth(object): - @staticmethod - def validate_password(password: str) -> bool: - """ - Validates a password - :param password: the password to validate - :return: - """ - pattern = "^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*#?&])[A-Za-z\d@$!#%*?&]{6,20}$" - pattern = re.compile(pattern) - if not re.search(pattern, password): - raise ValueError("Password must be between 6 to 20 characters and must have at least a number, an uppercase " - "letter, a lowercase letter, and a special character") - return True - @staticmethod def hash_password(password: str) -> str: """ diff --git a/hub/persistence/db_setup.py b/hub/persistence/db_setup.py index 5bdcb13c..e261f033 100644 --- a/hub/persistence/db_setup.py +++ b/hub/persistence/db_setup.py @@ -7,14 +7,15 @@ 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.hub_logger import logger class DBSetup: - def __init__(self, db_name, app_env, dotenv_path): + def __init__(self, db_name, app_env, dotenv_path, admin_password, application_uuid): """ - Creates database tables and a default admin user + Creates database tables a default admin user and a default admin app with the given password and uuid :param db_name: :param app_env: :param dotenv_path: @@ -26,17 +27,30 @@ class DBSetup: 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._create_admin_user(self._user_repo) + self._application_repo = ApplicationRepo(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) @staticmethod - def _create_admin_user(user_repo): - email = 'admin@hub.com' - password = 'HubAdmin#!98' + def _create_admin_app(application_repo, application_uuid): + name = 'AdminTool' + description = 'Admin tool to control city persistence and to test the API v1.4' + print('Creating default admin tool application...') + application = application_repo.insert(name, description, application_uuid) + if type(application) is dict: + logger.info(application) + else: + msg = f'Created Admin tool with application_uuid: {application_uuid}' + print(msg) + logger.info(msg) + + @staticmethod + def _create_admin_user(user_repo, admin_password, application_id): + password = admin_password print('Creating default admin user...') - user = user_repo.insert('Administrator', email, password, UserRoles.Admin) + user = user_repo.insert('Administrator', password, UserRoles.Admin, application_id) if type(user) is dict: logger.info(user) else: - print(f'Created Admin user with email: {email}, password: {password} and role: {UserRoles.Admin.value}') - logger.info(f'Created Admin user with email: {email}, password: {password} and role: {UserRoles.Admin.value}') - print('Remember to change the admin default password and email address with the UserFactory') + print(f'Created Admin user') + logger.info(f'Created Admin user') \ No newline at end of file diff --git a/hub/persistence/models/__init__.py b/hub/persistence/models/__init__.py index 89986d04..0779c3fe 100644 --- a/hub/persistence/models/__init__.py +++ b/hub/persistence/models/__init__.py @@ -5,3 +5,4 @@ from .heat_pump_simulation import HeatPumpTypes from .user import User, UserRoles from .user_applications import UserApplications from .application import Application +from .city_object import CityObject diff --git a/hub/persistence/models/application.py b/hub/persistence/models/application.py index d62baf8f..d6bdd6be 100644 --- a/hub/persistence/models/application.py +++ b/hub/persistence/models/application.py @@ -7,6 +7,7 @@ Project Coder Guille Gutierrez Guillermo.GutierrezMorote@concordia.ca import datetime +from sqlalchemy.dialects.postgresql import UUID from sqlalchemy import Column, Integer, String, Sequence from sqlalchemy import DateTime @@ -21,9 +22,11 @@ class Application(Base): id = Column(Integer, Sequence('application_id_seq'), primary_key=True) name = Column(String, nullable=False) description = Column(String, nullable=False) + application_uuid = Column(UUID(as_uuid=True), nullable=False) created = Column(DateTime, default=datetime.datetime.utcnow) updated = Column(DateTime, default=datetime.datetime.utcnow) - def __init__(self, name, description): + def __init__(self, name, description, application_uuid): self.name = name self.description = description + self.application_uuid = application_uuid diff --git a/hub/persistence/models/city.py b/hub/persistence/models/city.py index 3242d17f..a7ff3007 100644 --- a/hub/persistence/models/city.py +++ b/hub/persistence/models/city.py @@ -5,13 +5,12 @@ Copyright © 2022 Concordia CERC group Project Coder Peter Yefi peteryefi@gmail.com """ -from sqlalchemy import Column, Integer, String, Sequence, ForeignKey -from sqlalchemy import DateTime, PickleType, Float -from hub.persistence.db_config import Base -from sqlalchemy.dialects.postgresql import JSONB -from sqlalchemy.orm import relationship import datetime -import numpy as np + +from sqlalchemy import Column, Integer, String, Sequence, ForeignKey +from sqlalchemy import DateTime, PickleType + +from hub.persistence.db_config import Base class City(Base): @@ -21,28 +20,19 @@ class City(Base): id = Column(Integer, Sequence('city_id_seq'), primary_key=True) city = Column(PickleType, nullable=False) name = Column(String, nullable=False) - srs_name = Column(String, nullable=False) - climate_reference_city = Column(String, nullable=True) - time_zone = Column(String, nullable=True) - country_code = Column(String, nullable=False) - latitude = Column(Float) - longitude = Column(Float) - lower_corner = Column(JSONB, nullable=False) - upper_corner = Column(JSONB, nullable=False) + level_of_detail = Column(Integer, nullable=False) + climate_file = Column(String, nullable=False) + application_id = Column(Integer, ForeignKey('application.id'), nullable=False) + user_id = Column(Integer, ForeignKey('user.id'), nullable=True) hub_release = Column(String, nullable=False) - city_version = Column(Integer, nullable=False) - user_id = Column(Integer, ForeignKey('user.id')) - user = relationship("User", back_populates="cities") created = Column(DateTime, default=datetime.datetime.utcnow) updated = Column(DateTime, default=datetime.datetime.utcnow) - def __init__(self, city, name, srs_name, country_code, l_corner, u_corner, user_id): + def __init__(self, city, name, level_of_detail, climate_file, application_id, user_id, hub_release): self.city = city - self.user_id = user_id self.name = name - self.srs_name = srs_name - self.country_code = country_code - l_corner = l_corner.tolist() if type(l_corner) == np.ndarray else l_corner - u_corner = u_corner.tolist() if type(u_corner) == np.ndarray else u_corner - self.lower_corner = l_corner - self.upper_corner = u_corner + self.level_of_detail = level_of_detail + self.climate_file = climate_file + self.application_id = application_id + self.user_id = user_id + self.hub_release = hub_release diff --git a/hub/persistence/models/city_object.py b/hub/persistence/models/city_object.py new file mode 100644 index 00000000..d97391b7 --- /dev/null +++ b/hub/persistence/models/city_object.py @@ -0,0 +1,44 @@ +""" +Model representation of a city object +SPDX - License - Identifier: LGPL - 3.0 - or -later +Copyright © 2022 Concordia CERC group +Project Coder Guille Gutierrez Guillermo.GutierrezMorote@concordia.ca +""" + +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): + """ + A model representation of an application + """ + __tablename__ = "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) + alias = Column(String, nullable=True) + type = Column(String, nullable=False) + year_of_construction = Column(Integer, nullable=True) + function = Column(String, nullable=True) + usage = Column(String, nullable=True) + volume = Column(Float, nullable=False) + area = Column(Float, nullable=False) + created = Column(DateTime, default=datetime.datetime.utcnow) + updated = Column(DateTime, default=datetime.datetime.utcnow) + + def __init__(self, city_id, name, alias, object_type, year_of_construction, function, usage, volume, area): + self.city_id = city_id + self.name = name + self.alias = alias + self.type = object_type + self.year_of_construction = year_of_construction + self.function = function + self.usage = usage + self.volume = volume + self.area = area + diff --git a/hub/persistence/models/heat_pump_simulation.py b/hub/persistence/models/heat_pump_simulation.py deleted file mode 100644 index bcc89a5a..00000000 --- a/hub/persistence/models/heat_pump_simulation.py +++ /dev/null @@ -1,83 +0,0 @@ -""" -Model representation of the results of heat pump simulation -SPDX - License - Identifier: LGPL - 3.0 - or -later -Copyright © 2022 Concordia CERC group -Project Coder Peter Yefi peteryefi@gmail.com -""" - -from sqlalchemy import Column, Integer, String, Sequence -from sqlalchemy import Enum, ForeignKey, Float, DateTime -from sqlalchemy.dialects.postgresql import JSONB -from hub.persistence.db_config import Base -import enum -import datetime - - -class SimulationTypes(enum.Enum): - Parallel = 'PARALLEL' - Series = 'SERIES' - - -class HeatPumpTypes(enum.Enum): - Air = 'Air Source' - Water = 'Water to Water' - - -class HeatPumpSimulation(Base): - """A model representation of a building - - Attributes: - city_id, A reference to the city which was used to run this simulation. - hourly_electricity_demand, A JSON object that has hours and their electricity demand - daily_electricity_demand, A JSON object that has days and their electricity demand - monthly_electricity_demand, A JSON object that has months and their electricity demand - daily_fossil_fuel_consumption, A JSON object that has days and fossil fuel consumption - monthly_fossil_fuel_consumption, A JSON object that has months and fossil fuel consumption - heat_pump_type, Water or air heat pump - simulation_type, The type of heat pump simulation (parallel or series) - heat_pump_model, The model of the heat pump (either water to water or air source) - start year, HP simulation start year - end year, HP simulation end year - max_hp_energy_input, Maximum heat pump energy input - max_demand_storage_hour, Hours of storage at maximum demand - building_supply_temp, building supply temperature - temp_difference, Difference in HP and building supply temperatures - fuel_lhv, The lower heating value of fuel - fuel_price, The price of fuel - fuel_efficiency, the efficiency of fuel - fuel_density, the density of fuel - hp_supply_temp, supply temperature of heat pump - - - """ - __tablename__ = "heat_pump_simulation" - id = Column(Integer, Sequence('hp_simulation_id_seq'), primary_key=True) - city_id = Column(Integer, ForeignKey('city.id'), nullable=False) - daily_electricity_demand = Column(JSONB, nullable=False) - hourly_electricity_demand = Column(JSONB, nullable=False) - daily_fossil_fuel_consumption = Column(JSONB, nullable=False) - monthly_fossil_fuel_consumption = Column(JSONB, nullable=False) - monthly_electricity_demand = Column(JSONB, nullable=False) - heat_pump_type = Column(Enum(HeatPumpTypes), nullable=False) - simulation_type = Column(Enum(SimulationTypes), nullable=False) - heat_pump_model = Column(String, nullable=False) - start_year = Column(Integer, nullable=False) - end_year = Column(Integer, nullable=False) - max_hp_energy_input = Column(Float, nullable=False) - max_demand_storage_hour = Column(Float, nullable=False) - building_supply_temp = Column(Float, nullable=False) - temp_difference = Column(Float, nullable=False) - fuel_lhv = Column(Float, nullable=False) - fuel_price = Column(Float, nullable=False) - fuel_efficiency = Column(Float, nullable=False) - fuel_density = Column(Float, nullable=False) - hp_supply_temp = Column(Float, nullable=False) - created = Column(DateTime, default=datetime.datetime.utcnow) - - def __init__(self, city_id, hourly_elec_demand, daily_elec_demand, monthly_elec_demand, daily_fossil, monthly_fossil): - self.city_id = city_id - self.hourly_electricity_demand = hourly_elec_demand - self.daily_electricity_demand = daily_elec_demand - self.monthly_electricity_demand = monthly_elec_demand - self.daily_fossil_fuel_consumption = daily_fossil - self.monthly_fossil_fuel_consumption = monthly_fossil diff --git a/hub/persistence/models/simulation_results.py b/hub/persistence/models/simulation_results.py new file mode 100644 index 00000000..54519e54 --- /dev/null +++ b/hub/persistence/models/simulation_results.py @@ -0,0 +1,34 @@ +""" +Model representation of simulation results +SPDX - License - Identifier: LGPL - 3.0 - or -later +Copyright © 2022 Concordia CERC group +Project Coder Guille Gutierrez Guillermo.GutierrezMorote@concordia.ca +""" + +import datetime + +from sqlalchemy import Column, Integer, String, Sequence, ForeignKey, Float +from sqlalchemy import DateTime +from sqlalchemy.dialects.postgresql import JSONB + +from hub.persistence.db_config import Base + + +class CityObject(Base): + """ + A model representation of an application + """ + __tablename__ = "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) + name = Column(String, nullable=False) + values = Column(JSONB, nullable=False) + created = Column(DateTime, default=datetime.datetime.utcnow) + updated = Column(DateTime, default=datetime.datetime.utcnow) + + def __init__(self, name, values, city_id=None, city_object_id=None): + self.name = name + self.values = values + self.city_id = city_id + self.city_object_id = city_object_id diff --git a/hub/persistence/models/user.py b/hub/persistence/models/user.py index 91027b27..ce780a2f 100644 --- a/hub/persistence/models/user.py +++ b/hub/persistence/models/user.py @@ -26,22 +26,14 @@ class User(Base): __tablename__ = "user" id = Column(Integer, Sequence('user_id_seq'), primary_key=True) name = Column(String, nullable=False) - email = Column(String, nullable=False, unique=True) password = Column(String, nullable=False) role = Column(Enum(UserRoles), nullable=False, default=UserRoles.Hub_Reader) - cities = relationship("City", back_populates="user") + application_id = Column(Integer, nullable=False) created = Column(DateTime, default=datetime.datetime.utcnow) updated = Column(DateTime, default=datetime.datetime.utcnow) - @validates("email") - def validate_email(self, key, address): - pattern = r'\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b' - if not re.match(pattern, address): - raise ValueError("failed simple email validation") - return address - - def __init__(self, name, email, password, role): + def __init__(self, name, password, role, application_id): self.name = name - self.email = email self.password = password self.role = role + self.application_id = application_id diff --git a/hub/persistence/models/user_applications.py b/hub/persistence/models/user_applications.py deleted file mode 100644 index 978803f8..00000000 --- a/hub/persistence/models/user_applications.py +++ /dev/null @@ -1,30 +0,0 @@ -""" -Model representation of the user applications -SPDX - License - Identifier: LGPL - 3.0 - or -later -Copyright © 2022 Concordia CERC group -Project Coder Guille Gutierrez Guillermo.GutierrezMorote@concordia.ca -""" - -import datetime - -from sqlalchemy import Column, Integer, ForeignKey, PrimaryKeyConstraint -from sqlalchemy import DateTime - -from hub.persistence.db_config import Base - - -class UserApplications(Base): - """ - A model representation of the user applications - """ - __tablename__ = "user_applications" - user_id = Column(Integer, ForeignKey('user.id')) - application_id = Column(Integer, ForeignKey('application.id')) - - created = Column(DateTime, default=datetime.datetime.utcnow) - updated = Column(DateTime, default=datetime.datetime.utcnow) - __table_args__ = (PrimaryKeyConstraint(user_id, application_id),) - - def __init__(self, user_id, application_id): - self.user_id = user_id - self.application_id = application_id diff --git a/hub/persistence/repositories/__init__.py b/hub/persistence/repositories/__init__.py index febdaca3..c693ccad 100644 --- a/hub/persistence/repositories/__init__.py +++ b/hub/persistence/repositories/__init__.py @@ -1 +1,2 @@ from .user_repo import UserRepo +from .application_repo import ApplicationRepo diff --git a/hub/persistence/repositories/application_repo.py b/hub/persistence/repositories/application_repo.py new file mode 100644 index 00000000..ea1dca35 --- /dev/null +++ b/hub/persistence/repositories/application_repo.py @@ -0,0 +1,84 @@ +""" +Application repository with database CRUD operations +SPDX - License - Identifier: LGPL - 3.0 - or -later +Copyright © 2022 Concordia CERC group +Project Coder Guille Gutierrez Guillermo.GutierrezMorote@concordia.ca +""" + +import datetime +from typing import Union, Dict + +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 + + +class ApplicationRepo(BaseRepo): + _instance = None + + def __init__(self, db_name: str, dotenv_path: str, app_env: str): + super().__init__(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(ApplicationRepo, cls).__new__(cls) + return cls._instance + + def insert(self, name: str, description: str, application_uuid: str) -> Union[Application, Dict]: + application = self.get_by_uuid(application_uuid) + if application is None: + try: + application = Application(name=name, description=description, application_uuid=application_uuid) + self.session.add(application) + self.session.flush() + self.session.commit() + return application + except SQLAlchemyError as err: + logger.error(f'An error occurred while creating application: {err}') + else: + return {'message': f'An application with {application_uuid} application uuid, already exists'} + + def update(self, application_uuid: str, name: str, description: str) -> Union[Dict, None]: + """ + Updates an application + :param application_uuid: the application uuid of the application to be updated + :param name: the application name + :param description: the application description + :return: + """ + try: + self.session.query(Application).filter(Application.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]: + """ + 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() + except SQLAlchemyError as err: + logger.error(f'Error while fetching application by application_uuid: {err}') + + def delete_application(self, application_uuid: str): + """ + Deletes an application with the application_uuid + :param application_uuid: The application uuid + :return: None + """ + try: + self.session.query(Application).filter(Application.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_repo.py index cda51259..985ab26b 100644 --- a/hub/persistence/repositories/city_repo.py +++ b/hub/persistence/repositories/city_repo.py @@ -5,17 +5,18 @@ Copyright © 2022 Concordia CERC group Project Coder Peter Yefi peteryefi@gmail.com """ -from hub.city_model_structure.city import City -from hub.persistence import BaseRepo -from sqlalchemy.exc import SQLAlchemyError -from sqlalchemy import select -from hub.persistence.models import City as DBCity -import pickle -import requests -from urllib3.exceptions import HTTPError -from typing import Union, Dict -from hub.hub_logger import logger import datetime +import pickle +from typing import Union, Dict + +from sqlalchemy import select +from sqlalchemy.exc import SQLAlchemyError + +from hub.city_model_structure.city import City +from hub.hub_logger import logger +from hub.persistence import BaseRepo +from hub.persistence.models import City as DBCity +from hub.version import __version__ class CityRepo(BaseRepo): @@ -32,44 +33,25 @@ class CityRepo(BaseRepo): cls._instance = super(CityRepo, cls).__new__(cls) return cls._instance - def insert(self, city: City, user_id: int) -> Union[City, Dict]: - db_city = DBCity(pickle.dumps(city), city.name, city.srs_name, city.country_code, city.lower_corner, - city.upper_corner, user_id) - db_city.climate_reference_city = city.climate_reference_city - db_city.longitude = city.longitude - db_city.latitude = city.latitude - db_city.time_zone = city.time_zone - + def insert(self, city: City, application_id, user_id: int) -> Union[City, Dict]: + """ + Insert a city + :param city: The complete city instance + :param application_id: Application id owning the instance + :param user_id: User id owning the instance + :return: City and Dictionary + """ try: - # Retrieve hub project latest release - response = requests.get("https://rs-loy-gitlab.concordia.ca/api/v4/projects/2/repository/branches/master", - headers={"PRIVATE-TOKEN": self.config.hub_token}) - recent_commit = response.json()["commit"]["id"] - logger.info(f'Current commit of hub is {recent_commit}') - exiting_city = self._get_by_hub_version(recent_commit, city.name) + release = __version__ + db_city = DBCity(pickle.dumps(city), city.name, city.level_of_detail, city.climate_file, application_id, user_id, + release) - # Do not persist the same city for the same version of Hub - if exiting_city is None: - db_city.hub_release = recent_commit - cities = self.get_by_name(city.name) - # update version for the same city but different hub versions - - if len(cities) == 0: - db_city.city_version = 0 - else: - db_city.city_version = cities[-1].city_version + 1 - - # Persist city - self.session.add(db_city) - self.session.flush() - self.session.commit() - return db_city - else: - return {'message': f'Same version of {city.name} exist'} + self.session.add(db_city) + self.session.flush() + self.session.commit() + return db_city except SQLAlchemyError as err: - logger.error(f'Error while adding city: {err}') - except HTTPError as err: - logger.error(f'Error retrieving Hub latest release: {err}') + logger.error(f'An error occurred while creating city: {err}') def get_by_id(self, city_id: int) -> DBCity: """ @@ -82,16 +64,16 @@ class CityRepo(BaseRepo): except SQLAlchemyError as err: logger.error(f'Error while fetching city: {err}') - def _get_by_hub_version(self, hub_commit: str, city_name: str) -> City: + def _get_by_hub_version(self, hub_release: str, city_name: str) -> City: """ - Fetch a City based on the name and hub project recent commit - :param hub_commit: the latest hub commit + Fetch a City based on the name and hub project + :param hub_release: the hub release :param city_name: the name of the city :return: a city """ try: return self.session.execute(select(DBCity) - .where(DBCity.hub_release == hub_commit, DBCity.name == city_name)).first() + .where(DBCity.hub_release == hub_release, DBCity.name == city_name)).first() 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_repo.py index d4979c12..2ff5a839 100644 --- a/hub/persistence/repositories/user_repo.py +++ b/hub/persistence/repositories/user_repo.py @@ -30,16 +30,15 @@ class UserRepo(BaseRepo): cls._instance = super(UserRepo, cls).__new__(cls) return cls._instance - def insert(self, name: str, email: str, password: str, role: UserRoles) -> Union[User, Dict]: - user = self.get_by_email(email) + def insert(self, name: str, password: str, role: UserRoles, application_id: int) -> Union[User, Dict]: + user = self.get_by_name_and_application(name, application_id) if user is None: try: - if Auth.validate_password(password): - user = User(name=name, email=email, password=Auth.hash_password(password), role=role) - self.session.add(user) - self.session.flush() - self.session.commit() - return user + user = User(name=name, password=Auth.hash_password(password), role=role, application_id=application_id) + self.session.add(user) + self.session.flush() + self.session.commit() + return user except SQLAlchemyError as err: logger.error(f'An error occurred while creating user: {err}') else: diff --git a/hub/version.py b/hub/version.py new file mode 100644 index 00000000..d8aa3de9 --- /dev/null +++ b/hub/version.py @@ -0,0 +1 @@ +__version__ = '0.1.7.3' diff --git a/pyproject.toml b/pyproject.toml index 0588c800..8c148403 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,49 +4,5 @@ requires = ["setuptools>=61.0.0", "wheel"] build-backend = "setuptools.build_meta" -[project] -name = "cerc_hub" -version = "0.1.7.3" -description = "CERC Hub consist in a set of classes (Central data model), importers and exporters to help researchers to create better and sustainable cities" -readme = "README.md" -authors = [{ name = "Guillermo Gutierrez", email = "Guillermo.GutierrezMorote@concordia.ca" }] -license = { file = "LICENSE.md" } -classifiers = [ - "License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)", - "Programming Language :: Python", - "Programming Language :: Python :: 3", -] -keywords = ["city", "hub", "cerc"] -dependencies = [ - "xmltodict", - "numpy", - "trimesh", - "pyproj", - "pandas", - "requests", - "esoreader", - "geomeppy", - "PyWavefront", - "xlrd", - "openpyxl", - "networkx", - "parseidf==1.0.0", - "ply", - "rhino3dm==7.7.0", - "scipy", - "PyYAML", - "pyecore==0.12.2", - "python-dotenv", - "SQLAlchemy", - "bcrypt==4.0.1", - "shapely", - "geopandas", - "triangle" -] -requires-python = ">=3.9" - -[project.optional-dependencies] -dev = ["pip-tools", "pytest"] - -[project.urls] -Homepage = "https://rs-loy-gitlab.concordia.ca/Guille/hub" +[options.packages.find_namespace] +where = "hub" \ No newline at end of file diff --git a/setup.py b/setup.py index f143896c..44e91fa7 100644 --- a/setup.py +++ b/setup.py @@ -1,32 +1,90 @@ -from setuptools import setup, find_packages +from setuptools import setup, find_packages, convert_path import os.path import glob +main_ns = {} +version = convert_path('hub/version.py') +with open(version) as f: + exec(f.read(), main_ns) + setup( - name='hub', - version="0.1", - packages=find_packages(exclude="unittests"), + name='cerc-hub', + version=main_ns['__version__'], + 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", + classifiers=[ + "License :: OSI Approved :: GNU Library or Lesser General Public License (LGPL)", + "Programming Language :: Python", + "Programming Language :: Python :: 3", + ], + include_package_data=True, + packages=['hub', + 'hub.catalog_factories', + 'hub.catalog_factories.construction', + 'hub.catalog_factories.data_models', + 'hub.catalog_factories.data_models.construction', + 'hub.catalog_factories.data_models.greenery', + 'hub.catalog_factories.data_models.usages', + 'hub.catalog_factories.greenery', + 'hub.catalog_factories.greenery.ecore_greenery', + 'hub.catalog_factories.usage', + 'hub.city_model_structure', + 'hub.city_model_structure.attributes', + 'hub.city_model_structure.building_demand', + 'hub.city_model_structure.energy_systems', + 'hub.city_model_structure.greenery', + 'hub.city_model_structure.iot', + 'hub.city_model_structure.transport', + 'hub.config', + 'hub.data', + 'hub.exports', + 'hub.exports.building_energy', + 'hub.exports.building_energy.idf_files', + 'hub.exports.building_energy.insel', + 'hub.exports.energy_systems', + 'hub.exports.formats', + 'hub.helpers', + 'hub.hub_logger', + 'hub.imports', + 'hub.imports.construction', + 'hub.imports.construction.helpers', + 'hub.imports.construction.data_classes', + 'hub.imports.energy_systems', + 'hub.imports.geometry', + 'hub.imports.geometry.citygml_classes', + 'hub.imports.geometry.helpers', + 'hub.imports.usage', + 'hub.imports.weather', + 'hub.imports.weather.helpers', + 'hub.persistence', + 'hub.persistence.models', + 'hub.persistence.repositories', + 'hub.imports' + ], data_files=[ - ('config', [os.path.join('hub/config', 'configuration.ini')]), - ('greenery', glob.glob('hub/catalog_factories/greenery/ecore_greenery/*.ecore')), - ('data', glob.glob('hub/data/construction/*.xml')), - ('data', glob.glob('hub/data/customized_imports/*.xml')), - ('data', glob.glob('hub/data/energy_systems/*.xml')), - ('data', glob.glob('hub/data/energy_systems/*.insel')), - ('data', glob.glob('hub/data/energy_systems/*.xlsx')), - ('data', glob.glob('hub/data/energy_systems/*.txt')), - ('data', glob.glob('hub/data/energy_systems/*.yaml')), - ('data', glob.glob('hub/data/greenery/*.xml')), - ('data', glob.glob('hub/data/life_cycle_assessment/*.xml')), - ('data', glob.glob('hub/data/schedules/*.xml')), - ('data', glob.glob('hub/data/schedules/*.xlsx')), - ('data', glob.glob('hub/data/schedules/idf_files/*.idf')), - ('data', glob.glob('hub/data/sensors/*.json')), - ('data', glob.glob('hub/data/usage/*.xml')), - ('data', glob.glob('hub/data/usage/*.xlsx')), - ('data', glob.glob('hub/data/weather/*.dat')), - ('data', glob.glob('hub/data/weather/epw/*.epw')), - ('data', glob.glob('hub/data/weather/*.dat')) + ('hub/config', glob.glob('hub/config/*.ini')), + ('hub/catalog_factories/greenery/ecore_greenery', glob.glob('hub/catalog_factories/greenery/ecore_greenery/*.ecore')), + ('hub/data/construction.', glob.glob('hub/data/construction/*.xml')), + ('hub/data/customized_imports/', glob.glob('hub/data/customized_imports/*.xml')), + ('hub/data/energy_systems/', glob.glob('hub/data/energy_systems/*.xml')), + ('hub/data/energy_systems/', glob.glob('hub/data/energy_systems/*.insel')), + ('hub/data/energy_systems/', glob.glob('hub/data/energy_systems/*.xlsx')), + ('hub/data/energy_systems/*', glob.glob('hub/data/energy_systems/*.txt')), + ('hub/data/energy_systems/*', glob.glob('hub/data/energy_systems/*.yaml')), + ('hub/data/greenery/', glob.glob('hub/data/greenery/*.xml')), + ('hub/data/life_cycle_assessment/', glob.glob('hub/data/life_cycle_assessment/*.xml')), + ('hub/data/schedules/', glob.glob('hub/data/schedules/*.xml')), + ('hub/data/schedules/', glob.glob('hub/data/schedules/*.xlsx')), + ('hub/data/schedules/idf_files/', glob.glob('hub/data/schedules/idf_files/*.idf')), + ('hub/data/sensors/', glob.glob('hub/data/sensors/*.json')), + ('hub/data/usage/', glob.glob('hub/data/usage/*.xml')), + ('hub/data/usage/', glob.glob('hub/data/usage/*.xlsx')), + ('hub/data/weather/', glob.glob('hub/data/weather/*.dat')), + ('hub/data/weather/epw/', glob.glob('hub/data/weather/epw/*.epw')), + ('hub/data/weather/', glob.glob('hub/data/weather/*.dat')), + ('hub/exports/building_energy/idf_files', glob.glob('hub/exports/building_energy/idf_files/*.idf')) ], setup_requires=['setuptools'] )