Add support for label batch "quick entry" API
plus other general improvements to API core/master views and config
This commit is contained in:
parent
c520dc23ba
commit
bd09acd0fd
|
@ -25,3 +25,5 @@ Tailbone Web API - Batches
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from __future__ import unicode_literals, absolute_import
|
from __future__ import unicode_literals, absolute_import
|
||||||
|
|
||||||
|
from .core import BatchAPIMasterView
|
||||||
|
|
108
tailbone/api/batch/core.py
Normal file
108
tailbone/api/batch/core.py
Normal file
|
@ -0,0 +1,108 @@
|
||||||
|
# -*- coding: utf-8; -*-
|
||||||
|
################################################################################
|
||||||
|
#
|
||||||
|
# Rattail -- Retail Software Framework
|
||||||
|
# Copyright © 2010-2019 Lance Edgar
|
||||||
|
#
|
||||||
|
# This file is part of Rattail.
|
||||||
|
#
|
||||||
|
# Rattail is free software: you can redistribute it and/or modify it under the
|
||||||
|
# terms of the GNU General Public License as published by the Free Software
|
||||||
|
# Foundation, either version 3 of the License, or (at your option) any later
|
||||||
|
# version.
|
||||||
|
#
|
||||||
|
# Rattail is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||||
|
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
||||||
|
# details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License along with
|
||||||
|
# Rattail. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
################################################################################
|
||||||
|
"""
|
||||||
|
Tailbone Web API - Batch Views
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import unicode_literals, absolute_import
|
||||||
|
|
||||||
|
import six
|
||||||
|
|
||||||
|
from rattail.util import load_object
|
||||||
|
|
||||||
|
from tailbone.api import APIMasterView
|
||||||
|
|
||||||
|
|
||||||
|
class BatchAPIMasterView(APIMasterView):
|
||||||
|
"""
|
||||||
|
Base class for all API views which are meant to handle "batch" *and/or*
|
||||||
|
"batch row" data.
|
||||||
|
"""
|
||||||
|
supports_quick_entry = False
|
||||||
|
|
||||||
|
def __init__(self, request, **kwargs):
|
||||||
|
super(BatchAPIMasterView, self).__init__(request, **kwargs)
|
||||||
|
self.handler = self.get_handler()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_batch_class(cls):
|
||||||
|
model_class = cls.get_model_class()
|
||||||
|
if hasattr(model_class, '__batch_class__'):
|
||||||
|
return model_class.__batch_class__
|
||||||
|
return model_class
|
||||||
|
|
||||||
|
def get_handler(self):
|
||||||
|
"""
|
||||||
|
Returns a `BatchHandler` instance for the view. All (?) custom batch
|
||||||
|
API views should define a default handler class; however this may in all
|
||||||
|
(?) cases be overridden by config also. The specific setting required
|
||||||
|
to do so will depend on the 'key' for the type of batch involved, e.g.
|
||||||
|
assuming the 'vendor_catalog' batch:
|
||||||
|
|
||||||
|
.. code-block:: ini
|
||||||
|
|
||||||
|
[rattail.batch]
|
||||||
|
vendor_catalog.handler = myapp.batch.vendorcatalog:CustomCatalogHandler
|
||||||
|
|
||||||
|
Note that the 'key' for a batch is generally the same as its primary
|
||||||
|
table name, although technically it is whatever value returns from the
|
||||||
|
``batch_key`` attribute of the main batch model class.
|
||||||
|
"""
|
||||||
|
key = self.get_batch_class().batch_key
|
||||||
|
spec = self.rattail_config.get('rattail.batch', '{}.handler'.format(key),
|
||||||
|
default=self.default_handler_spec)
|
||||||
|
return load_object(spec)(self.rattail_config)
|
||||||
|
|
||||||
|
def quick_entry(self):
|
||||||
|
"""
|
||||||
|
View for handling "quick entry" user input, for a batch.
|
||||||
|
"""
|
||||||
|
data = self.request.json_body
|
||||||
|
|
||||||
|
uuid = data['batch_uuid']
|
||||||
|
batch = self.Session.query(self.get_batch_class()).get(uuid)
|
||||||
|
if not batch:
|
||||||
|
raise self.notfound()
|
||||||
|
|
||||||
|
entry = data['quick_entry']
|
||||||
|
|
||||||
|
try:
|
||||||
|
row = self.handler.quick_entry(self.Session(), batch, entry)
|
||||||
|
except Exception as error:
|
||||||
|
return {'error': six.text_type(error)}
|
||||||
|
|
||||||
|
result = self._get(obj=row)
|
||||||
|
result['ok'] = True
|
||||||
|
return result
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def _batch_defaults(cls, config):
|
||||||
|
route_prefix = cls.get_route_prefix()
|
||||||
|
url_prefix = cls.get_url_prefix()
|
||||||
|
|
||||||
|
# quick entry
|
||||||
|
if cls.supports_quick_entry:
|
||||||
|
config.add_route('{}.quick_entry'.format(route_prefix), '{}/quick-entry'.format(url_prefix),
|
||||||
|
request_method=('OPTIONS', 'POST'))
|
||||||
|
config.add_view(cls, attr='quick_entry', route_name='{}.quick_entry'.format(route_prefix),
|
||||||
|
renderer='json')
|
|
@ -34,6 +34,7 @@ from rattail.time import localtime
|
||||||
from cornice import resource
|
from cornice import resource
|
||||||
|
|
||||||
from tailbone.api import APIMasterView
|
from tailbone.api import APIMasterView
|
||||||
|
from tailbone.api.batch import BatchAPIMasterView
|
||||||
|
|
||||||
|
|
||||||
class LabelBatchViews(APIMasterView):
|
class LabelBatchViews(APIMasterView):
|
||||||
|
@ -107,9 +108,14 @@ class LabelBatchViews(APIMasterView):
|
||||||
config.add_cornice_resource(batch_resource)
|
config.add_cornice_resource(batch_resource)
|
||||||
|
|
||||||
|
|
||||||
class LabelBatchRowViews(APIMasterView):
|
class LabelBatchRowViews(BatchAPIMasterView):
|
||||||
|
|
||||||
model_class = model.LabelBatchRow
|
model_class = model.LabelBatchRow
|
||||||
|
default_handler_spec = 'rattail.batch.labels:LabelBatchHandler'
|
||||||
|
supports_quick_entry = True
|
||||||
|
route_prefix = 'api.label_batch_rows'
|
||||||
|
permission_prefix = 'labels.batch'
|
||||||
|
url_prefix = '/label-batch-rows'
|
||||||
|
|
||||||
def normalize(self, row):
|
def normalize(self, row):
|
||||||
batch = row.batch
|
batch = row.batch
|
||||||
|
@ -124,7 +130,10 @@ class LabelBatchRowViews(APIMasterView):
|
||||||
'batch_description': batch.description,
|
'batch_description': batch.description,
|
||||||
'sequence': row.sequence,
|
'sequence': row.sequence,
|
||||||
'item_id': row.item_id,
|
'item_id': row.item_id,
|
||||||
|
'upc': six.text_type(row.upc),
|
||||||
|
'upc_pretty': row.upc.pretty() if row.upc else None,
|
||||||
'description': row.description,
|
'description': row.description,
|
||||||
|
'full_description': row.product.full_description if row.product else row.description,
|
||||||
'status_code': row.status_code,
|
'status_code': row.status_code,
|
||||||
'status_display': row.STATUS.get(row.status_code, six.text_type(row.status_code)),
|
'status_display': row.STATUS.get(row.status_code, six.text_type(row.status_code)),
|
||||||
}
|
}
|
||||||
|
@ -137,13 +146,16 @@ class LabelBatchRowViews(APIMasterView):
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def defaults(cls, config):
|
def defaults(cls, config):
|
||||||
|
permission_prefix = cls.get_permission_prefix()
|
||||||
|
|
||||||
# label batch rows
|
# label batch rows
|
||||||
resource.add_view(cls.collection_get, permission='labels.batch.view')
|
resource.add_view(cls.collection_get, permission='{}.view'.format(permission_prefix))
|
||||||
resource.add_view(cls.get, permission='labels.batch.view')
|
resource.add_view(cls.get, permission='{}.view'.format(permission_prefix))
|
||||||
rows_resource = resource.add_resource(cls, collection_path='/label-batch-rows', path='/label-batch-row/{uuid}')
|
rows_resource = resource.add_resource(cls, collection_path='/label-batch-rows', path='/label-batch-row/{uuid}')
|
||||||
config.add_cornice_resource(rows_resource)
|
config.add_cornice_resource(rows_resource)
|
||||||
|
|
||||||
|
cls._batch_defaults(config)
|
||||||
|
|
||||||
|
|
||||||
def includeme(config):
|
def includeme(config):
|
||||||
LabelBatchViews.defaults(config)
|
LabelBatchViews.defaults(config)
|
||||||
|
|
|
@ -62,6 +62,40 @@ class APIMasterView(APIView):
|
||||||
return cls.normalized_model_name
|
return cls.normalized_model_name
|
||||||
return cls.get_model_class().__name__.lower()
|
return cls.get_model_class().__name__.lower()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_route_prefix(cls):
|
||||||
|
"""
|
||||||
|
Returns a prefix which (by default) applies to all routes provided by
|
||||||
|
this view class.
|
||||||
|
"""
|
||||||
|
prefix = getattr(cls, 'route_prefix', None)
|
||||||
|
if prefix:
|
||||||
|
return prefix
|
||||||
|
model_name = cls.get_normalized_model_name()
|
||||||
|
return 'api.{}s'.format(model_name)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_permission_prefix(cls):
|
||||||
|
"""
|
||||||
|
Returns a prefix which (by default) applies to all permissions
|
||||||
|
leveraged by this view class.
|
||||||
|
"""
|
||||||
|
prefix = getattr(cls, 'permission_prefix')
|
||||||
|
if prefix:
|
||||||
|
return prefix
|
||||||
|
return cls.get_route_prefix()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_url_prefix(cls):
|
||||||
|
"""
|
||||||
|
Returns a prefix which (by default) applies to all URLs provided by
|
||||||
|
this view class.
|
||||||
|
"""
|
||||||
|
prefix = getattr(cls, 'url_prefix', None)
|
||||||
|
if prefix:
|
||||||
|
return prefix
|
||||||
|
return '/{}'.format(cls.get_route_prefix())
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_object_key(cls):
|
def get_object_key(cls):
|
||||||
if hasattr(cls, 'object_key'):
|
if hasattr(cls, 'object_key'):
|
||||||
|
|
Loading…
Reference in a new issue