From cbb286e46db05a169ca399010b44a7d7227bd4d6 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Mon, 18 Jan 2021 20:55:30 +1100 Subject: [PATCH] Add API for stock item test report --- InvenTree/InvenTree/urls.py | 2 + InvenTree/report/api.py | 158 ++++++++++++++++++++++++++++++++ InvenTree/report/serializers.py | 23 +++++ 3 files changed, 183 insertions(+) create mode 100644 InvenTree/report/api.py create mode 100644 InvenTree/report/serializers.py diff --git a/InvenTree/InvenTree/urls.py b/InvenTree/InvenTree/urls.py index d8a64708a9..503e373641 100644 --- a/InvenTree/InvenTree/urls.py +++ b/InvenTree/InvenTree/urls.py @@ -29,6 +29,7 @@ from stock.api import stock_api_urls from build.api import build_api_urls from order.api import order_api_urls from label.api import label_api_urls +from report.api import report_api_urls from django.conf import settings from django.conf.urls.static import static @@ -60,6 +61,7 @@ apipatterns = [ url(r'^build/', include(build_api_urls)), url(r'^order/', include(order_api_urls)), url(r'^label/', include(label_api_urls)), + url(r'^report/', include(report_api_urls)), # User URLs url(r'^user/', include(user_urls)), diff --git a/InvenTree/report/api.py b/InvenTree/report/api.py new file mode 100644 index 0000000000..ed70da752b --- /dev/null +++ b/InvenTree/report/api.py @@ -0,0 +1,158 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +import sys + +from django.utils.translation import ugettext as _ +from django.conf.urls import url, include + +from django_filters.rest_framework import DjangoFilterBackend + +from rest_framework import generics, filters +from rest_framework.response import Response + +import InvenTree.helpers + +from stock.models import StockItem + +from .models import TestReport +from .serializers import TestReportSerializer + + +class ReportListView(generics.ListAPIView): + """ + Generic API class for report templates + """ + + filter_backends = [ + DjangoFilterBackend, + filters.SearchFilter, + ] + + filter_fields = [ + 'enabled', + ] + + search_fields = [ + 'name', + 'description', + ] + + +class StockItemReportMixin: + """ + Mixin for extracting stock items from query params + """ + + def get_items(self): + """ + Return a list of requested stock items + """ + + items = [] + + params = self.request.query_params + + if 'items[]' in params: + items = params.getlist('items[]', []) + elif 'item' in params: + items = [params.get('item', None)] + + if type(items) not in [list, tuple]: + item = [items] + + valid_ids = [] + + for item in items: + try: + valid_ids.append(int(item)) + except (ValueError): + pass + + # List of StockItems which match provided values + valid_items = StockItem.objects.filter(pk__in=valid_ids) + + return valid_items + + +class StockItemTestReportList(ReportListView, StockItemReportMixin): + """ + API endpoint for viewing list of TestReport objects. + + Filterable by: + + - enabled: Filter by enabled / disabled status + - item: Filter by single stock item + - items: Filter by list of stock items + + """ + + queryset = TestReport.objects.all() + serializer_class = TestReportSerializer + + def filter_queryset(self, queryset): + + queryset = super().filter_queryset(queryset) + + # List of StockItem objects to match against + items = self.get_items() + + if len(items) > 0: + """ + We wish to filter by stock items. + + We need to compare the 'filters' string of each report, + and see if it matches against each of the specified stock items. + + TODO: In the future, perhaps there is a way to make this more efficient. + """ + + valid_report_ids = set() + + for report in queryset.all(): + + matches = True + + # Filter string defined for the report object + filters = InvenTree.helpers.validateFilterString(report.filters) + + for item in items: + item_query = StockItem.objects.filter(pk=item.pk) + + if not item_query.filter(**filters).exists(): + matches = False + break + + if matches: + valid_report_ids.add(report.pk) + else: + continue + + # Reduce queryset to only valid matches + queryset = queryset.filter(pk__in=[pk for pk in valid_report_ids]) + return queryset + + +class StockItemTestReportDetail(generics.RetrieveUpdateDestroyAPIView): + """ + API endpoint for a single TestReport object + """ + + queryset = TestReport.objects.all() + serializer_class = TestReportSerializer + + +report_api_urls = [ + + # Stock item test reports + url(r'test/', include([ + # Detail views + url(r'^(?P\d+)/', include([ + #url(r'print/?', StockItemTestReportPrint.as_view(), name='api-stockitem-testreport-print'), + url(r'^.*$', StockItemTestReportDetail.as_view(), name='api-stockitem-testreport-detail'), + ])), + + # List view + url(r'^.*$', StockItemTestReportList.as_view(), name='api-stockitem-testreport-list'), + ])), +] \ No newline at end of file diff --git a/InvenTree/report/serializers.py b/InvenTree/report/serializers.py new file mode 100644 index 0000000000..0cd4d4f40a --- /dev/null +++ b/InvenTree/report/serializers.py @@ -0,0 +1,23 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from InvenTree.serializers import InvenTreeModelSerializer +from InvenTree.serializers import InvenTreeAttachmentSerializerField + +from .models import TestReport + + +class TestReportSerializer(InvenTreeModelSerializer): + + template = InvenTreeAttachmentSerializerField(required=True) + + class Meta: + model = TestReport + fields = [ + 'pk', + 'name', + 'description', + 'template', + 'filters', + 'enabled', + ]