Added Hub GitLab commit to manager versions of the same city

This commit is contained in:
Peter Yefi 2022-11-11 16:16:31 -05:00
parent 3ed115f5f3
commit 1881c1db78
13 changed files with 188 additions and 102 deletions

View File

@ -11,8 +11,7 @@ from persistence.models import Building
from persistence.models import City
if __name__ == '__main__':
config = BaseConfiguration()
config = BaseConfiguration(db_name='peteryefi')
engine = create_engine(config.conn_string())
City.__table__.create(bind=engine, checkfirst=True)
Building.__table__.create(bind=engine, checkfirst=True)

View File

@ -4,10 +4,21 @@ from typing import Dict, List, Union
import numpy as np
class CityUtil:
class CityUtil(object):
# holds a single instance of this class
_instance = None
def __init__(self):
pass
def __new__(cls):
"""
Implemented for a singleton pattern
"""
if cls._instance is None:
cls._instance = super(CityUtil, cls).__new__(cls)
return cls._instance
def _class_object_to_dict(self, class_obj) -> Union[Dict, None]:
"""
converts a class object to a dictionary
@ -34,6 +45,16 @@ class CityUtil:
return building_dict[key]
return None
def object_list_to_dict_list(self, object_list):
"""
converts a list of objects to a list of dictionaries
:param object_list: a list of objects
:return:
"""
if object_list is None:
return []
return [self._class_object_to_dict(obj) for obj in object_list]
def _serialize_points(self, points) -> List:
"""
Deserializes arry of Point objects to array of dictionarier

View File

@ -21,7 +21,6 @@ This Python file is in the root of Hub and should be run to create all the requi
### Database Configuration Parameter ###
A .env file (or environment variables) with configuration parameters described below are needed to establish a database connection:
```
DB_NAME=postgres-database-name
DB_USER=postgres-database-user
DB_PASSWORD=postgres-database-password
DB_HOST=database-host

View File

@ -1,3 +1,4 @@
from .Base import BaseRepo
from .repositories.CityRepo import CityRepo
from .repositories.BuildingRepo import BuildingRepo
from .base import BaseRepo
from .repositories.city_repo import CityRepo
from .repositories.building_repo import BuildingRepo
from .repositories.city_repo import CityRepo

View File

@ -11,7 +11,8 @@ from sqlalchemy.orm import Session
class BaseRepo:
def __init__(self):
config = BaseConfiguration()
def __init__(self, db_name):
config = BaseConfiguration(db_name)
engine = create_engine(config.conn_string())
self.config = config
self.session = Session(engine)

View File

@ -20,12 +20,13 @@ class BaseConfiguration(object):
"""
Base configuration class to hold common persistence configuration
"""
def __init__(self):
self._db_name = os.getenv('DB_NAME')
def __init__(self, db_name: str):
self._db_name = db_name
self._db_host = os.getenv('DB_HOST')
self._db_user = os.getenv('DB_USER')
self._db_pass = os.getenv('DB_PASSWORD')
self._db_port = os.getenv('DB_PORT')
self.hub_token = os.getenv('HUB_TOKEN')
def conn_string(self):
"""

View File

@ -1,32 +0,0 @@
"""
Model representation of a City
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, Float
from persistence.db_config import Base
from sqlalchemy.orm import relationship
class City(Base):
"""A model representation of a city
Attributes:
name The name of the city.
uid A unique identifier for each city object
location The location of the building.
country_code The country where the city is located.
latitude The GPS latitude location of the city.
longitude The GPS longitude location of the city.
building A relationship for fetching all buildings in the city
"""
__tablename__ = "city"
id = Column(Integer, Sequence('city_id_seq'), primary_key=True)
name = Column(String, nullable=False)
time_zone = Column(String, nullable=True)
country_code = Column(String, nullable=False)
latitude = Column(Float)
longitude = Column(Float)
buildings = relationship('Building', backref='city', lazy=True, cascade="all, delete-orphan")

View File

@ -1,2 +1,3 @@
from .Building import Building
from .City import City
from .building import Building
from .city import City
from .city import City

View File

@ -0,0 +1,30 @@
"""
Model representation of a City
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, PickleType, Float
from persistence.db_config import Base
from sqlalchemy.dialects.postgresql import JSONB
class City(Base):
"""A model representation of a city
"""
__tablename__ = "city"
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)
hub_release = Column(String, nullable=False)
city_version = Column(Integer, nullable=False)

View File

@ -1,55 +0,0 @@
"""
City 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 city_model_structure.city import City
from persistence.models import City as ModelCity
from persistence import BaseRepo
from sqlalchemy.exc import SQLAlchemyError
from sqlalchemy import select
class CityRepo(BaseRepo):
def __init__(self):
super().__init__()
def insert(self, city: City):
model_city = ModelCity()
model_city.name = city.name
model_city.longitude = city.longitude
model_city.latitude = city.latitude
model_city.country_code = city.country_code
model_city.time_zone = city.time_zone
try:
self.session.add(model_city)
self.session.flush()
self.session.commit()
return model_city
except SQLAlchemyError as err:
print(f'Error while adding city: {err}')
def get_by_id(self, city_id: int) -> ModelCity:
"""
Fetch a City based on the id
:param city_id: the city id
:return: a city
"""
try:
return self.session.execute(select(ModelCity).where(ModelCity.id == city_id)).first()[0]
except SQLAlchemyError as err:
print(f'Error while fetching city: {err}')
def get_by_name(self, city_name: str) -> [ModelCity]:
"""
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(ModelCity).where(ModelCity.name == city_name))
return [building[0] for building in result_set]
except SQLAlchemyError as err:
print(f'Error while fetching city by name: {err}')

View File

@ -15,8 +15,8 @@ from sqlalchemy import select
class BuildingRepo(BaseRepo):
def __init__(self):
super().__init__()
def __init__(self, db_name):
super().__init__(db_name)
self._city_util = CityUtil()
def insert(self, building: CityBuilding, city_id: int) -> Building:

View File

@ -0,0 +1,120 @@
"""
City 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 city_model_structure.city import City
from persistence import BaseRepo
from sqlalchemy.exc import SQLAlchemyError
from sqlalchemy import select
from helpers.city_util import CityUtil
from persistence.models import City
import pickle
import requests
from urllib3.exceptions import HTTPError
from typing import Union, Dict
class CityRepo(BaseRepo):
def __init__(self, db_name):
super().__init__(db_name)
self._city_util = CityUtil()
def insert(self, city: City) -> Union[City, Dict]:
model_city = City()
model_city.name = city.name
model_city.climate_reference_city = city.climate_reference_city
model_city.srs_name = city.srs_name
model_city.longitude = city.longitude
model_city.latitude = city.latitude
model_city.country_code = city.country_code
model_city.time_zone = city.time_zone
model_city.lower_corner = city.lower_corner.tolist()
model_city.upper_corner = city.upper_corner.tolist()
model_city.city = pickle.dumps(city)
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"]
exiting_city = self._get_by_hub_version(recent_commit, city.name)
# Do not persist the same city for the same version of Hub
if exiting_city is None:
model_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:
model_city.city_version = 0
else:
model_city.city_version = cities[-1].city_version + 1
# Persist city
self.session.add(model_city)
self.session.flush()
self.session.commit()
return model_city
else:
return {'message': f'Same version of {city.name} exist'}
except SQLAlchemyError as err:
print(f'Error while adding city: {err}')
except HTTPError as err:
print(f'Error retrieving Hub latest release: {err}')
def get_by_id(self, city_id: int) -> City:
"""
Fetch a City based on the id
:param city_id: the city id
:return: a city
"""
try:
return self.session.execute(select(City).where(City.id == city_id)).first()[0]
except SQLAlchemyError as err:
print(f'Error while fetching city: {err}')
def _get_by_hub_version(self, hub_commit: str, city_name: str) -> City:
"""
Fetch a City based on the name and hub project recent commit
:param hub_commit: the latest hub commit
:param city_name: the name of the city
:return: a city
"""
try:
return self.session.execute(select(City)
.where(City.hub_release == hub_commit, City.name == city_name)).first()
except SQLAlchemyError as err:
print(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(City).filter(City.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,
})
self.session.commit()
except SQLAlchemyError as err:
print(f'Error while updating city: {err}')
def get_by_name(self, city_name: str) -> [City]:
"""
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(City).where(City.name == city_name))
return [building[0] for building in result_set]
except SQLAlchemyError as err:
print(f'Error while fetching city by name: {err}')