diff --git a/InvenTree/InvenTree/api.py b/InvenTree/InvenTree/api.py index 6cdfbf0397..af8290b236 100644 --- a/InvenTree/InvenTree/api.py +++ b/InvenTree/InvenTree/api.py @@ -5,7 +5,9 @@ Main JSON interface views # -*- coding: utf-8 -*- from __future__ import unicode_literals +from django.utils.translation import ugettext as _ from django.http import JsonResponse + from rest_framework.response import Response from rest_framework.views import APIView @@ -14,6 +16,11 @@ from .version import inventreeVersion, inventreeInstanceName from plugins import plugins as inventree_plugins +# Load barcode plugins +print("INFO: Loading plugins") + +barcode_plugins = inventree_plugins.load_barcode_plugins() + class InfoView(AjaxView): """ Simple JSON endpoint for InvenTree information. @@ -45,19 +52,46 @@ class BarcodeScanView(APIView): def post(self, request, *args, **kwargs): - data = { - 'barcode': 'Hello world', - } + response = None - plugins = inventree_plugins.load_barcode_plugins() + barcode_data = request.data - for plugin in plugins: - print("Testing plugin:", plugin.PLUGIN_NAME) - if plugin().validate_barcode(request.data): - print("success!") + print("Barcode data:") + print(barcode_data) - return Response({ - 'success': 'OK', - 'data': data, - 'post': request.data, - }) + if type(barcode_data) is not dict: + response = { + 'error': _('Barcode data could not be parsed'), + } + + else: + # Look for a barcode plugin that knows how to handle the data + for plugin_class in barcode_plugins: + + plugin = plugin_class() + + if plugin.validate_barcode(barcode_data): + + # Plugin should return a dict response + response = plugin.decode_barcode(barcode_data) + + if type(response) is dict: + response['success'] = _('Barcode successfully decoded') + else: + response = { + 'error': _('Barcode plugin returned incorrect response') + } + + response['plugin'] = plugin.get_name() + + break + + if response is None: + response = { + 'error': _('Unknown barcode format'), + } + + # Include the original barcode data + response['barcode_data'] = barcode_data + + return Response(response) diff --git a/InvenTree/InvenTree/helpers.py b/InvenTree/InvenTree/helpers.py index 6b619b4aa2..b9a4d73740 100644 --- a/InvenTree/InvenTree/helpers.py +++ b/InvenTree/InvenTree/helpers.py @@ -172,7 +172,7 @@ def WrapWithQuotes(text, quote='"'): return text -def MakeBarcode(object_type, object_id, object_url, data={}): +def MakeBarcode(object_name, object_data): """ Generate a string for a barcode. Adds some global InvenTree parameters. Args: @@ -185,13 +185,12 @@ def MakeBarcode(object_type, object_id, object_url, data={}): json string of the supplied data plus some other data """ - # Add in some generic InvenTree data - data['type'] = object_type - data['id'] = object_id - data['url'] = object_url - data['tool'] = 'InvenTree' - data['instance'] = inventreeInstanceName() - data['version'] = inventreeVersion() + data = { + 'tool': 'InvenTree', + 'version': inventreeVersion(), + 'instance': inventreeInstanceName(), + object_name: object_data + } return json.dumps(data, sort_keys=True) diff --git a/InvenTree/InvenTree/tests.py b/InvenTree/InvenTree/tests.py index d93a40e631..203748de3e 100644 --- a/InvenTree/InvenTree/tests.py +++ b/InvenTree/InvenTree/tests.py @@ -113,15 +113,15 @@ class TestMakeBarcode(TestCase): def test_barcode(self): - data = { - 'animal': 'cat', - 'legs': 3, - 'noise': 'purr' - } + bc = helpers.MakeBarcode( + "part", + { + "id": 3, + "url": "www.google.com", + } + ) - bc = helpers.MakeBarcode("part", 3, "www.google.com", data) - - self.assertIn('animal', bc) + self.assertIn('part', bc) self.assertIn('tool', bc) self.assertIn('"tool": "InvenTree"', bc) diff --git a/InvenTree/part/models.py b/InvenTree/part/models.py index c182cc6583..ca5b8f11c2 100644 --- a/InvenTree/part/models.py +++ b/InvenTree/part/models.py @@ -478,11 +478,11 @@ class Part(models.Model): """ Return a JSON string for formatting a barcode for this Part object """ return helpers.MakeBarcode( - "Part", - self.id, - reverse('api-part-detail', kwargs={'pk': self.id}), + "part", { - 'name': self.name, + "id": self.id, + "name": self.full_name, + "url": reverse('api-part-detail', kwargs={'pk': self.id}), } ) diff --git a/InvenTree/plugins/barcode/barcode.py b/InvenTree/plugins/barcode/barcode.py index a2d3c6652e..447a8d4edc 100644 --- a/InvenTree/plugins/barcode/barcode.py +++ b/InvenTree/plugins/barcode/barcode.py @@ -1,19 +1,25 @@ # -*- coding: utf-8 -*- +import plugins.plugin as plugin -class BarcodePlugin: + +class BarcodePlugin(plugin.InvenTreePlugin): """ The BarcodePlugin class is the base class for any barcode plugin. """ - # Override this for each actual plugin - PLUGIN_NAME = '' - def validate_barcode(self, barcode_data): """ Default implementation returns False """ return False + def decode_barcode(self, barcode_data): + """ + Decode the barcode, and craft a response + """ + + return None + def __init__(self): - pass + plugin.InvenTreePlugin.__init__(self) diff --git a/InvenTree/plugins/barcode/inventree.py b/InvenTree/plugins/barcode/inventree.py index f983487a41..b13e0643a3 100644 --- a/InvenTree/plugins/barcode/inventree.py +++ b/InvenTree/plugins/barcode/inventree.py @@ -8,7 +8,24 @@ class InvenTreeBarcodePlugin(barcode.BarcodePlugin): PLUGIN_NAME = "InvenTreeBarcodePlugin" def validate_barcode(self, barcode_data): + """ + An "InvenTree" barcode must include the following tags: - print("testing") + { + 'tool': 'InvenTree', + 'version': + } + + """ + + for key in ['tool', 'version']: + if key not in barcode_data.keys(): + return False + + if not barcode_data['tool'] == 'InvenTree': + return False return True + + def decode_barcode(self, barcode_data): + pass diff --git a/InvenTree/plugins/plugin.py b/InvenTree/plugins/plugin.py new file mode 100644 index 0000000000..ec40b6d4cf --- /dev/null +++ b/InvenTree/plugins/plugin.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- + + +class InvenTreePlugin(): + """ + Base class for a Barcode plugin + """ + + # Override the plugin name for each concrete plugin instance + PLUGIN_NAME = '' + + def get_name(self): + return self.PLUGIN_NAME + + def __init__(self): + pass diff --git a/InvenTree/plugins/plugins.py b/InvenTree/plugins/plugins.py index a2f1c836aa..03e127933e 100644 --- a/InvenTree/plugins/plugins.py +++ b/InvenTree/plugins/plugins.py @@ -52,4 +52,12 @@ def load_barcode_plugins(): Return a list of all registered barcode plugins """ - return get_plugins(barcode, BarcodePlugin) + plugins = get_plugins(barcode, BarcodePlugin) + + if len(plugins) > 0: + print("Discovered {n} barcode plugins:".format(n=len(plugins))) + + for bp in plugins: + print(" - {bp}".format(bp=bp.PLUGIN_NAME)) + + return plugins diff --git a/InvenTree/stock/models.py b/InvenTree/stock/models.py index ff0cf7b21a..f7d1c0b147 100644 --- a/InvenTree/stock/models.py +++ b/InvenTree/stock/models.py @@ -44,11 +44,11 @@ class StockLocation(InvenTreeTree): """ Return a JSON string for formatting a barcode for this StockLocation object """ return helpers.MakeBarcode( - 'StockLocation', - self.id, - reverse('api-location-detail', kwargs={'pk': self.id}), + 'stocklocation', { - 'name': self.name, + "id": self.id, + "name": self.name, + "url": reverse('api-location-detail', kwargs={'pk': self.id}), } ) @@ -288,12 +288,10 @@ class StockItem(MPTTModel): """ return helpers.MakeBarcode( - 'StockItem', - self.id, - reverse('api-stock-detail', kwargs={'pk': self.id}), + "stockitem", { - 'part_id': self.part.id, - 'part_name': self.part.full_name + "id": self.id, + "url": reverse('api-stock-detail', kwargs={'pk': self.id}), } )