Add support for label batch "quick entry" API

plus other general improvements to API core/master views and config
This commit is contained in:
Lance Edgar 2019-11-11 11:26:42 -06:00
parent c520dc23ba
commit bd09acd0fd
4 changed files with 159 additions and 3 deletions

View file

@ -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
View 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')

View file

@ -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)

View file

@ -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'):