You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
261 lines
9.0 KiB
Python
261 lines
9.0 KiB
Python
6 years ago
|
from abc import ABCMeta, abstractmethod
|
||
|
from inspect import Parameter
|
||
|
import typing
|
||
|
|
||
|
from sqlalchemy.orm import Session
|
||
|
from molten import BaseApp, HTTPError, HTTP_409, HTTP_404
|
||
|
from .errors import EntityNotFound
|
||
|
from .model import StoreModel, GeographyModel, AlertModel
|
||
|
from .schema import Alert, Geography, Link, Store, APIResponse
|
||
|
|
||
|
|
||
|
class BaseManager(metaclass=ABCMeta):
|
||
|
"""Base instance for Model managers"""
|
||
|
|
||
|
def __init__(self, session: Session, app: BaseApp):
|
||
|
self.session = session
|
||
|
self.app = app
|
||
|
|
||
|
@abstractmethod
|
||
|
def _model_from_schema(self, schema):
|
||
|
"""Converts a Schema instance into a SQLAlchemy ORM model instance"""
|
||
|
pass
|
||
|
|
||
|
@abstractmethod
|
||
|
def _schema_from_model(self, result):
|
||
|
"""Converts a SQLAlchemy results proxy into a Schema instance"""
|
||
|
pass
|
||
|
|
||
|
|
||
|
class StoreManager(BaseManager):
|
||
|
"""A `StoreManager` is accountable for the CRUD operations associated with a Store"""
|
||
|
|
||
|
def _schema_from_model(self, result: StoreModel) -> Store:
|
||
|
_store = Store(
|
||
|
id=result.id,
|
||
|
href=self.app.reverse_uri("get_store_by_id", id=result.id),
|
||
|
createdDate=result.created_date,
|
||
|
modifiedDate=result.modified_date,
|
||
|
name=result.name,
|
||
|
number=result.number,
|
||
|
address=result.address,
|
||
|
city=result.city,
|
||
|
state=result.state,
|
||
|
zip=result.zip,
|
||
|
lat=result.lat,
|
||
|
long=result.long,
|
||
|
tdlinx=result.tdlinx,
|
||
|
geography=Link(
|
||
|
href=self.app.reverse_uri("get_geo_by_id", id=result.geography_id)
|
||
|
)
|
||
|
if result.geography_id
|
||
|
else None,
|
||
|
alerts=Link(
|
||
|
href=self.app.reverse_uri("get_store_alerts", id=result.id)
|
||
|
)
|
||
|
)
|
||
|
return _store
|
||
|
|
||
|
def _model_from_schema(self, store: Store) -> StoreModel:
|
||
|
_store_model = StoreModel(
|
||
|
id=store.id,
|
||
|
name=store.name,
|
||
|
number=store.number,
|
||
|
address=store.address,
|
||
|
city=store.city,
|
||
|
state=store.state,
|
||
|
zip=store.zip,
|
||
|
lat=store.lat,
|
||
|
long=store.long,
|
||
|
tdlinx=store.tdlinx,
|
||
|
)
|
||
|
return _store_model
|
||
|
|
||
|
def get_stores(self) -> typing.List[Store]:
|
||
|
"""Retrieves a list of Store representations"""
|
||
|
results = self.session.query(StoreModel).order_by(StoreModel.id).all()
|
||
|
_stores = [self._schema_from_model(result) for result in results]
|
||
|
return _stores
|
||
|
|
||
|
def get_store_by_id(self, id: int) -> Store:
|
||
|
"""Retrieves a store representation by id"""
|
||
|
result = self.session.query(StoreModel).filter_by(id=id).one_or_none()
|
||
|
if result is None:
|
||
|
return result
|
||
|
|
||
|
_store = self._schema_from_model(result)
|
||
|
|
||
|
return _store
|
||
|
|
||
|
def create_store(self, store: Store) -> Store:
|
||
|
"""Creates a new store resource and returns its representation"""
|
||
|
result = self.session.query(StoreModel).filter_by(id=store.id).one_or_none()
|
||
|
if result is not None:
|
||
|
raise HTTPError(
|
||
|
HTTP_409,
|
||
|
{
|
||
|
"status": 409,
|
||
|
"message": f"A store with id: {store.id} already exists",
|
||
|
},
|
||
|
)
|
||
|
|
||
|
store_model = self._model_from_schema(store)
|
||
|
|
||
|
self.session.add(store_model)
|
||
|
self.session.flush()
|
||
|
|
||
|
_store = self._schema_from_model(store_model)
|
||
|
|
||
|
return _store
|
||
|
|
||
|
def update_store(self, store: Store) -> Store:
|
||
|
result = self.session.query(StoreModel).filter_by(id=store.id).one_or_none()
|
||
|
_updates = self._model_from_schema(store)
|
||
|
self.session.merge(_updates)
|
||
|
self.session.flush()
|
||
|
_store = self._schema_from_model(result)
|
||
|
return _store
|
||
|
|
||
|
def delete_store_by_id(self, id):
|
||
|
_store = self.session.query(StoreModel).filter_by(id=id).one_or_none()
|
||
|
if _store is not None:
|
||
|
self.session.delete(_store)
|
||
|
return
|
||
|
|
||
|
def get_stores_by_geo(self, geo_id) -> typing.List[Store]:
|
||
|
results = self.session.query(StoreModel).filter_by(geography_id=geo_id).all()
|
||
|
_stores = [self._schema_from_model(store) for store in results]
|
||
|
return _stores
|
||
|
|
||
|
|
||
|
class GeographyManager(BaseManager):
|
||
|
|
||
|
def _model_from_schema(self, schema: Geography) -> GeographyModel:
|
||
|
pass
|
||
|
|
||
|
def _schema_from_model(self, result: GeographyModel) -> Geography:
|
||
|
_geography = Geography(
|
||
|
id=result.id,
|
||
|
href=self.app.reverse_uri("get_geo_by_id", id=result.id),
|
||
|
createdDate=result.created_date,
|
||
|
modifiedDate=result.modified_date,
|
||
|
name=result.name,
|
||
|
stores=Link(href=self.app.reverse_uri('get_geo_stores', id=result.id)),
|
||
|
alerts=Link(href=self.app.reverse_uri('get_geo_alerts', id=result.id)),
|
||
|
)
|
||
|
return _geography
|
||
|
|
||
|
def get_geographies(self) -> typing.List[Geography]:
|
||
|
results = self.session.query(GeographyModel).all()
|
||
|
return [self._schema_from_model(geo) for geo in results]
|
||
|
|
||
|
def get_geo_by_id(self, id: int) -> Geography:
|
||
|
result = self.session.query(GeographyModel).one_or_none()
|
||
|
if result is None:
|
||
|
raise HTTPError(
|
||
|
HTTP_404,
|
||
|
APIResponse(
|
||
|
status=404,
|
||
|
message=f"The resource you are looking for /geographies/{id} does not exist",
|
||
|
),
|
||
|
)
|
||
|
|
||
|
return self._schema_from_model(result)
|
||
|
|
||
|
|
||
|
class AlertManager(BaseManager):
|
||
|
|
||
|
def _model_from_schema(self, schema: Alert) -> AlertModel:
|
||
|
_alert_model = AlertModel(
|
||
|
promo_name=schema.promoName,
|
||
|
response=schema.response,
|
||
|
valid=schema.valid
|
||
|
)
|
||
|
|
||
|
def _schema_from_model(self, result: AlertModel) -> Alert:
|
||
|
_alert = Alert(
|
||
|
id=result.id,
|
||
|
href=self.app.reverse_uri('get_alert_by_id', id=result.id),
|
||
|
createdDate=result.created_date,
|
||
|
modifiedDate=result.modified_date,
|
||
|
promoName=result.promo_name,
|
||
|
response=result.response,
|
||
|
valid=result.valid,
|
||
|
store=Link(href=self.app.reverse_uri('get_store_by_id', id=result.store.id))
|
||
|
)
|
||
|
return _alert
|
||
|
|
||
|
def get_alerts(self) -> typing.List[Alert]:
|
||
|
results = self.session.query(AlertModel).all()
|
||
|
alerts = [self._schema_from_model(alert) for alert in results]
|
||
|
return alerts
|
||
|
|
||
|
def get_alert_by_id(self, alert_id) -> Alert:
|
||
|
result = self.session.query(AlertModel).filter_by(id=alert_id).one_or_none()
|
||
|
if result is None:
|
||
|
raise EntityNotFound(f"Alert: {alert_id} does not exist")
|
||
|
alert = self._schema_from_model(result)
|
||
|
return alert
|
||
|
|
||
|
def get_alerts_at_store(self, store_id) -> typing.List[Alert]:
|
||
|
store_check = self.session.query(StoreModel.id).filter_by(id=store_id).exists()
|
||
|
|
||
|
if not self.session.query(store_check).scalar():
|
||
|
raise EntityNotFound(f'Store {store_id} does not exist')
|
||
|
|
||
|
results = self.session.query(AlertModel).filter_by(store_id=store_id, active=True).all()
|
||
|
alerts = [self._schema_from_model(alert) for alert in results]
|
||
|
return alerts
|
||
|
|
||
|
def get_alerts_at_geo(self, geo_id) -> typing.List[Alert]:
|
||
|
geo_check = self.session.query(GeographyModel.id).filter_by(id=geo_id).exists()
|
||
|
|
||
|
if not self.session.query(geo_check).scalar():
|
||
|
raise EntityNotFound(f'Geography {geo_id} does not exist')
|
||
|
|
||
|
_subquery = self.session.query(StoreModel.id).filter(StoreModel.geography_id==geo_id)
|
||
|
results = self.session.query(AlertModel).filter(AlertModel.store_id.in_(_subquery)).all()
|
||
|
alerts = [self._schema_from_model(alert) for alert in results]
|
||
|
return alerts
|
||
|
|
||
|
def create_alert_at_store(self, store_id, alert: Alert) -> Alert:
|
||
|
store = self.session.query(StoreModel).filter_by(id=store_id).one()
|
||
|
alert_model = self._model_from_schema(alert)
|
||
|
alert_model.store = store
|
||
|
self.session.add(alert_model)
|
||
|
self.session.flush()
|
||
|
_alert = self._schema_from_model(alert_model)
|
||
|
return _alert
|
||
|
|
||
|
|
||
|
class StoreManagerComponent:
|
||
|
is_cacheable = True
|
||
|
is_singleton = False
|
||
|
|
||
|
def can_handle_parameter(self, parameter: Parameter) -> bool:
|
||
|
return parameter.annotation is StoreManager
|
||
|
|
||
|
def resolve(self, session: Session, app: BaseApp) -> StoreManager: # type: ignore
|
||
|
return StoreManager(session, app)
|
||
|
|
||
|
|
||
|
class GeographyManagerComponent:
|
||
|
is_cacheable = True
|
||
|
is_singleton = False
|
||
|
|
||
|
def can_handle_parameter(self, parameter: Parameter) -> bool:
|
||
|
return parameter.annotation is GeographyManager
|
||
|
|
||
|
def resolve(self, session: Session, app: BaseApp) -> StoreManager: # type: ignore
|
||
|
return GeographyManager(session, app)
|
||
|
|
||
|
|
||
|
class AlertManagerComponent:
|
||
|
is_cacheable = True
|
||
|
is_singleton = False
|
||
|
|
||
|
def can_handle_parameter(self, parameter: Parameter) -> bool:
|
||
|
return parameter.annotation is AlertManager
|
||
|
|
||
|
def resolve(self, session: Session, app: BaseApp) -> AlertManager: # type: ignore
|
||
|
return AlertManager(session, app)
|