Added Hub GitLab commit to manager versions of the same city
This commit is contained in:
parent
3ed115f5f3
commit
1881c1db78
|
@ -11,8 +11,7 @@ from persistence.models import Building
|
||||||
from persistence.models import City
|
from persistence.models import City
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
config = BaseConfiguration()
|
config = BaseConfiguration(db_name='peteryefi')
|
||||||
engine = create_engine(config.conn_string())
|
engine = create_engine(config.conn_string())
|
||||||
City.__table__.create(bind=engine, checkfirst=True)
|
City.__table__.create(bind=engine, checkfirst=True)
|
||||||
Building.__table__.create(bind=engine, checkfirst=True)
|
|
||||||
|
|
||||||
|
|
|
@ -4,10 +4,21 @@ from typing import Dict, List, Union
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
|
||||||
|
|
||||||
class CityUtil:
|
class CityUtil(object):
|
||||||
|
# holds a single instance of this class
|
||||||
|
_instance = None
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
pass
|
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]:
|
def _class_object_to_dict(self, class_obj) -> Union[Dict, None]:
|
||||||
"""
|
"""
|
||||||
converts a class object to a dictionary
|
converts a class object to a dictionary
|
||||||
|
@ -34,6 +45,16 @@ class CityUtil:
|
||||||
return building_dict[key]
|
return building_dict[key]
|
||||||
return None
|
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:
|
def _serialize_points(self, points) -> List:
|
||||||
"""
|
"""
|
||||||
Deserializes arry of Point objects to array of dictionarier
|
Deserializes arry of Point objects to array of dictionarier
|
||||||
|
|
|
@ -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 ###
|
### Database Configuration Parameter ###
|
||||||
A .env file (or environment variables) with configuration parameters described below are needed to establish a database connection:
|
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_USER=postgres-database-user
|
||||||
DB_PASSWORD=postgres-database-password
|
DB_PASSWORD=postgres-database-password
|
||||||
DB_HOST=database-host
|
DB_HOST=database-host
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
from .Base import BaseRepo
|
from .base import BaseRepo
|
||||||
from .repositories.CityRepo import CityRepo
|
from .repositories.city_repo import CityRepo
|
||||||
from .repositories.BuildingRepo import BuildingRepo
|
from .repositories.building_repo import BuildingRepo
|
||||||
|
from .repositories.city_repo import CityRepo
|
|
@ -11,7 +11,8 @@ from sqlalchemy.orm import Session
|
||||||
|
|
||||||
|
|
||||||
class BaseRepo:
|
class BaseRepo:
|
||||||
def __init__(self):
|
def __init__(self, db_name):
|
||||||
config = BaseConfiguration()
|
config = BaseConfiguration(db_name)
|
||||||
engine = create_engine(config.conn_string())
|
engine = create_engine(config.conn_string())
|
||||||
|
self.config = config
|
||||||
self.session = Session(engine)
|
self.session = Session(engine)
|
|
@ -20,12 +20,13 @@ class BaseConfiguration(object):
|
||||||
"""
|
"""
|
||||||
Base configuration class to hold common persistence configuration
|
Base configuration class to hold common persistence configuration
|
||||||
"""
|
"""
|
||||||
def __init__(self):
|
def __init__(self, db_name: str):
|
||||||
self._db_name = os.getenv('DB_NAME')
|
self._db_name = db_name
|
||||||
self._db_host = os.getenv('DB_HOST')
|
self._db_host = os.getenv('DB_HOST')
|
||||||
self._db_user = os.getenv('DB_USER')
|
self._db_user = os.getenv('DB_USER')
|
||||||
self._db_pass = os.getenv('DB_PASSWORD')
|
self._db_pass = os.getenv('DB_PASSWORD')
|
||||||
self._db_port = os.getenv('DB_PORT')
|
self._db_port = os.getenv('DB_PORT')
|
||||||
|
self.hub_token = os.getenv('HUB_TOKEN')
|
||||||
|
|
||||||
def conn_string(self):
|
def conn_string(self):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -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")
|
|
|
@ -1,2 +1,3 @@
|
||||||
from .Building import Building
|
from .building import Building
|
||||||
from .City import City
|
from .city import City
|
||||||
|
from .city import City
|
||||||
|
|
30
persistence/models/city.py
Normal file
30
persistence/models/city.py
Normal 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)
|
||||||
|
|
|
@ -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}')
|
|
|
@ -15,8 +15,8 @@ from sqlalchemy import select
|
||||||
|
|
||||||
class BuildingRepo(BaseRepo):
|
class BuildingRepo(BaseRepo):
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self, db_name):
|
||||||
super().__init__()
|
super().__init__(db_name)
|
||||||
self._city_util = CityUtil()
|
self._city_util = CityUtil()
|
||||||
|
|
||||||
def insert(self, building: CityBuilding, city_id: int) -> Building:
|
def insert(self, building: CityBuilding, city_id: int) -> Building:
|
120
persistence/repositories/city_repo.py
Normal file
120
persistence/repositories/city_repo.py
Normal 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}')
|
Loading…
Reference in New Issue
Block a user