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

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)