2
0
mirror of https://github.com/inventree/InvenTree.git synced 2025-07-17 18:26:32 +00:00

[CI] Enable python autoformat (#6169)

* Squashed commit of the following:

commit f5cf7b2e78
Author: Matthias Mair <code@mjmair.com>
Date:   Sun Jan 7 20:36:57 2024 +0100

    fixed reqs

commit 9d845bee98
Author: Matthias Mair <code@mjmair.com>
Date:   Sun Jan 7 20:32:35 2024 +0100

    disable autofix/format

commit aff5f27148
Author: Matthias Mair <code@mjmair.com>
Date:   Sun Jan 7 20:28:50 2024 +0100

    adjust checks

commit 47271cf1ef
Author: Matthias Mair <code@mjmair.com>
Date:   Sun Jan 7 20:28:22 2024 +0100

    reorder order of operations

commit e1bf178b40
Author: Matthias Mair <code@mjmair.com>
Date:   Sun Jan 7 20:01:09 2024 +0100

    adapted ruff settings to better fit code base

commit ad7d88a6f4
Author: Matthias Mair <code@mjmair.com>
Date:   Sun Jan 7 19:59:45 2024 +0100

    auto fixed docstring

commit a2e54a760e
Author: Matthias Mair <code@mjmair.com>
Date:   Sun Jan 7 19:46:35 2024 +0100

    fix getattr useage

commit cb80c73bc6
Author: Matthias Mair <code@mjmair.com>
Date:   Sun Jan 7 19:25:09 2024 +0100

    fix requirements file

commit b7780bbd21
Author: Matthias Mair <code@mjmair.com>
Date:   Sun Jan 7 18:42:28 2024 +0100

    fix removed sections

commit 71f1681f55
Author: Matthias Mair <code@mjmair.com>
Date:   Sun Jan 7 18:41:21 2024 +0100

    fix djlint syntax

commit a0bcf1bcce
Author: Matthias Mair <code@mjmair.com>
Date:   Sun Jan 7 18:35:28 2024 +0100

    remove flake8 from code base

commit 22475b31cc
Author: Matthias Mair <code@mjmair.com>
Date:   Sun Jan 7 18:34:56 2024 +0100

    remove flake8 from code base

commit 0413350f14
Author: Matthias Mair <code@mjmair.com>
Date:   Sun Jan 7 18:24:39 2024 +0100

    moved ruff section

commit d90c48a0bf
Author: Matthias Mair <code@mjmair.com>
Date:   Sun Jan 7 18:24:24 2024 +0100

    move djlint config to pyproject

commit c5ce55d511
Author: Matthias Mair <code@mjmair.com>
Date:   Sun Jan 7 18:20:39 2024 +0100

    added isort again

commit 42a41d23af
Author: Matthias Mair <code@mjmair.com>
Date:   Sun Jan 7 18:19:02 2024 +0100

    move config section

commit 8569233181
Author: Matthias Mair <code@mjmair.com>
Date:   Sun Jan 7 18:17:52 2024 +0100

    fix codespell error

commit 2897c6704d
Author: Matthias Mair <code@mjmair.com>
Date:   Sun Jan 7 17:29:21 2024 +0100

    replaced flake8 with ruff
    mostly for speed improvements

* enable autoformat

* added autofixes

* switched to single quotes everywhere

* switched to ruff for import sorting

* fix wrong url response

* switched to pathlib for lookup

* fixed lookup

* Squashed commit of the following:

commit d3b795824b
Author: Matthias Mair <code@mjmair.com>
Date:   Sun Jan 7 22:56:17 2024 +0100

    fixed source path

commit 0bac0c19b8
Author: Matthias Mair <code@mjmair.com>
Date:   Sun Jan 7 22:47:53 2024 +0100

    fixed req

commit 9f61f01d9c
Author: Matthias Mair <code@mjmair.com>
Date:   Sun Jan 7 22:45:18 2024 +0100

    added missing toml req

commit 91b71ed24a
Author: Matthias Mair <code@mjmair.com>
Date:   Sun Jan 7 20:49:50 2024 +0100

    moved isort config

commit 12460b0419
Author: Matthias Mair <code@mjmair.com>
Date:   Sun Jan 7 20:43:22 2024 +0100

    remove flake8 section from setup.cfg

commit f5cf7b2e78
Author: Matthias Mair <code@mjmair.com>
Date:   Sun Jan 7 20:36:57 2024 +0100

    fixed reqs

commit 9d845bee98
Author: Matthias Mair <code@mjmair.com>
Date:   Sun Jan 7 20:32:35 2024 +0100

    disable autofix/format

commit aff5f27148
Author: Matthias Mair <code@mjmair.com>
Date:   Sun Jan 7 20:28:50 2024 +0100

    adjust checks

commit 47271cf1ef
Author: Matthias Mair <code@mjmair.com>
Date:   Sun Jan 7 20:28:22 2024 +0100

    reorder order of operations

commit e1bf178b40
Author: Matthias Mair <code@mjmair.com>
Date:   Sun Jan 7 20:01:09 2024 +0100

    adapted ruff settings to better fit code base

commit ad7d88a6f4
Author: Matthias Mair <code@mjmair.com>
Date:   Sun Jan 7 19:59:45 2024 +0100

    auto fixed docstring

commit a2e54a760e
Author: Matthias Mair <code@mjmair.com>
Date:   Sun Jan 7 19:46:35 2024 +0100

    fix getattr useage

commit cb80c73bc6
Author: Matthias Mair <code@mjmair.com>
Date:   Sun Jan 7 19:25:09 2024 +0100

    fix requirements file

commit b7780bbd21
Author: Matthias Mair <code@mjmair.com>
Date:   Sun Jan 7 18:42:28 2024 +0100

    fix removed sections

commit 71f1681f55
Author: Matthias Mair <code@mjmair.com>
Date:   Sun Jan 7 18:41:21 2024 +0100

    fix djlint syntax

commit a0bcf1bcce
Author: Matthias Mair <code@mjmair.com>
Date:   Sun Jan 7 18:35:28 2024 +0100

    remove flake8 from code base

commit 22475b31cc
Author: Matthias Mair <code@mjmair.com>
Date:   Sun Jan 7 18:34:56 2024 +0100

    remove flake8 from code base

commit 0413350f14
Author: Matthias Mair <code@mjmair.com>
Date:   Sun Jan 7 18:24:39 2024 +0100

    moved ruff section

commit d90c48a0bf
Author: Matthias Mair <code@mjmair.com>
Date:   Sun Jan 7 18:24:24 2024 +0100

    move djlint config to pyproject

commit c5ce55d511
Author: Matthias Mair <code@mjmair.com>
Date:   Sun Jan 7 18:20:39 2024 +0100

    added isort again

commit 42a41d23af
Author: Matthias Mair <code@mjmair.com>
Date:   Sun Jan 7 18:19:02 2024 +0100

    move config section

commit 8569233181
Author: Matthias Mair <code@mjmair.com>
Date:   Sun Jan 7 18:17:52 2024 +0100

    fix codespell error

commit 2897c6704d
Author: Matthias Mair <code@mjmair.com>
Date:   Sun Jan 7 17:29:21 2024 +0100

    replaced flake8 with ruff
    mostly for speed improvements

* fix coverage souce format

---------

Co-authored-by: Oliver Walters <oliver.henry.walters@gmail.com>
This commit is contained in:
Matthias Mair
2024-01-11 01:28:58 +01:00
committed by GitHub
parent 9715af564f
commit 4b14986591
257 changed files with 13422 additions and 12200 deletions

View File

@@ -7,6 +7,7 @@ import label.models
class LabelAdmin(admin.ModelAdmin):
"""Admin class for the various label models"""
list_display = ('name', 'description', 'label', 'filters', 'enabled')

View File

@@ -18,8 +18,7 @@ import label.models
import label.serializers
from InvenTree.api import MetadataView
from InvenTree.filters import InvenTreeSearchFilter
from InvenTree.mixins import (ListCreateAPI, RetrieveAPI,
RetrieveUpdateDestroyAPI)
from InvenTree.mixins import ListCreateAPI, RetrieveAPI, RetrieveUpdateDestroyAPI
from part.models import Part
from plugin.builtin.labels.inventree_label import InvenTreeLabelPlugin
from plugin.registry import registry
@@ -59,7 +58,7 @@ class LabelFilterMixin:
for id in ids:
try:
valid_ids.append(int(id))
except (ValueError):
except ValueError:
pass
# Filter queryset by matching ID values
@@ -120,34 +119,23 @@ class LabelListView(LabelFilterMixin, ListCreateAPI):
return queryset
filter_backends = [
DjangoFilterBackend,
InvenTreeSearchFilter
]
filter_backends = [DjangoFilterBackend, InvenTreeSearchFilter]
filterset_fields = [
'enabled',
]
filterset_fields = ['enabled']
search_fields = [
'name',
'description',
]
search_fields = ['name', 'description']
@method_decorator(cache_page(5), name='dispatch')
class LabelPrintMixin(LabelFilterMixin):
"""Mixin for printing labels."""
rolemap = {
"GET": "view",
"POST": "view",
}
rolemap = {'GET': 'view', 'POST': 'view'}
def check_permissions(self, request):
"""Override request method to GET so that also non superusers can print using a post request."""
if request.method == "POST":
request = clone_request(request, "GET")
if request.method == 'POST':
request = clone_request(request, 'GET')
return super().check_permissions(request)
@method_decorator(never_cache)
@@ -161,7 +149,9 @@ class LabelPrintMixin(LabelFilterMixin):
plugin = self.get_plugin(self.request)
kwargs.setdefault('context', self.get_serializer_context())
serializer = plugin.get_printing_options_serializer(self.request, *args, **kwargs)
serializer = plugin.get_printing_options_serializer(
self.request, *args, **kwargs
)
# if no serializer is defined, return an empty serializer
if not serializer:
@@ -171,8 +161,12 @@ class LabelPrintMixin(LabelFilterMixin):
def get(self, request, *args, **kwargs):
"""Perform a GET request against this endpoint to print labels"""
common.models.InvenTreeUserSetting.set_setting('DEFAULT_' + self.ITEM_KEY.upper() + '_LABEL_TEMPLATE',
self.get_object().pk, None, user=request.user)
common.models.InvenTreeUserSetting.set_setting(
'DEFAULT_' + self.ITEM_KEY.upper() + '_LABEL_TEMPLATE',
self.get_object().pk,
None,
user=request.user,
)
return self.print(request, self.get_items())
def post(self, request, *args, **kwargs):
@@ -205,8 +199,10 @@ class LabelPrintMixin(LabelFilterMixin):
if not plugin.is_active():
raise ValidationError(f"Plugin '{plugin_key}' is not enabled")
if not plugin.mixin_enabled("labels"):
raise ValidationError(f"Plugin '{plugin_key}' is not a label printing plugin")
if not plugin.mixin_enabled('labels'):
raise ValidationError(
f"Plugin '{plugin_key}' is not a label printing plugin"
)
# Only return the plugin if it is enabled and has the label printing mixin
return plugin
@@ -228,18 +224,24 @@ class LabelPrintMixin(LabelFilterMixin):
raise ValidationError('Label has invalid dimensions')
# if the plugin returns a serializer, validate the data
if serializer := plugin.get_printing_options_serializer(request, data=request.data, context=self.get_serializer_context()):
if serializer := plugin.get_printing_options_serializer(
request, data=request.data, context=self.get_serializer_context()
):
serializer.is_valid(raise_exception=True)
# At this point, we offload the label(s) to the selected plugin.
# The plugin is responsible for handling the request and returning a response.
result = plugin.print_labels(label, items_to_print, request, printing_options=request.data)
result = plugin.print_labels(
label, items_to_print, request, printing_options=request.data
)
if isinstance(result, JsonResponse):
result['plugin'] = plugin.plugin_slug()
return result
raise ValidationError(f"Plugin '{plugin.plugin_slug()}' returned invalid response type '{type(result)}'")
raise ValidationError(
f"Plugin '{plugin.plugin_slug()}' returned invalid response type '{type(result)}'"
)
class StockItemLabelMixin:
@@ -261,16 +263,19 @@ class StockItemLabelList(StockItemLabelMixin, LabelListView):
- item: Filter by single stock item
- items: Filter by list of stock items
"""
pass
class StockItemLabelDetail(StockItemLabelMixin, RetrieveUpdateDestroyAPI):
"""API endpoint for a single StockItemLabel object."""
pass
class StockItemLabelPrint(StockItemLabelMixin, LabelPrintMixin, RetrieveAPI):
"""API endpoint for printing a StockItemLabel object."""
pass
@@ -293,21 +298,25 @@ class StockLocationLabelList(StockLocationLabelMixin, LabelListView):
- location: Filter by a single stock location
- locations: Filter by list of stock locations
"""
pass
class StockLocationLabelDetail(StockLocationLabelMixin, RetrieveUpdateDestroyAPI):
"""API endpoint for a single StockLocationLabel object."""
pass
class StockLocationLabelPrint(StockLocationLabelMixin, LabelPrintMixin, RetrieveAPI):
"""API endpoint for printing a StockLocationLabel object."""
pass
class PartLabelMixin:
"""Mixin for PartLabel endpoints"""
queryset = label.models.PartLabel.objects.all()
serializer_class = label.serializers.PartLabelSerializer
@@ -317,16 +326,19 @@ class PartLabelMixin:
class PartLabelList(PartLabelMixin, LabelListView):
"""API endpoint for viewing list of PartLabel objects."""
pass
class PartLabelDetail(PartLabelMixin, RetrieveUpdateDestroyAPI):
"""API endpoint for a single PartLabel object."""
pass
class PartLabelPrint(PartLabelMixin, LabelPrintMixin, RetrieveAPI):
"""API endpoint for printing a PartLabel object."""
pass
@@ -342,70 +354,147 @@ class BuildLineLabelMixin:
class BuildLineLabelList(BuildLineLabelMixin, LabelListView):
"""API endpoint for viewing a list of BuildLineLabel objects"""
pass
class BuildLineLabelDetail(BuildLineLabelMixin, RetrieveUpdateDestroyAPI):
"""API endpoint for a single BuildLineLabel object"""
pass
class BuildLineLabelPrint(BuildLineLabelMixin, LabelPrintMixin, RetrieveAPI):
"""API endpoint for printing a BuildLineLabel object"""
pass
label_api_urls = [
# Stock item labels
re_path(r'stock/', include([
# Detail views
path(r'<int:pk>/', include([
re_path(r'print/?', StockItemLabelPrint.as_view(), name='api-stockitem-label-print'),
re_path(r'metadata/', MetadataView.as_view(), {'model': label.models.StockItemLabel}, name='api-stockitem-label-metadata'),
re_path(r'^.*$', StockItemLabelDetail.as_view(), name='api-stockitem-label-detail'),
])),
# List view
re_path(r'^.*$', StockItemLabelList.as_view(), name='api-stockitem-label-list'),
])),
re_path(
r'stock/',
include([
# Detail views
path(
r'<int:pk>/',
include([
re_path(
r'print/?',
StockItemLabelPrint.as_view(),
name='api-stockitem-label-print',
),
re_path(
r'metadata/',
MetadataView.as_view(),
{'model': label.models.StockItemLabel},
name='api-stockitem-label-metadata',
),
re_path(
r'^.*$',
StockItemLabelDetail.as_view(),
name='api-stockitem-label-detail',
),
]),
),
# List view
re_path(
r'^.*$', StockItemLabelList.as_view(), name='api-stockitem-label-list'
),
]),
),
# Stock location labels
re_path(r'location/', include([
# Detail views
path(r'<int:pk>/', include([
re_path(r'print/?', StockLocationLabelPrint.as_view(), name='api-stocklocation-label-print'),
re_path(r'metadata/', MetadataView.as_view(), {'model': label.models.StockLocationLabel}, name='api-stocklocation-label-metadata'),
re_path(r'^.*$', StockLocationLabelDetail.as_view(), name='api-stocklocation-label-detail'),
])),
# List view
re_path(r'^.*$', StockLocationLabelList.as_view(), name='api-stocklocation-label-list'),
])),
re_path(
r'location/',
include([
# Detail views
path(
r'<int:pk>/',
include([
re_path(
r'print/?',
StockLocationLabelPrint.as_view(),
name='api-stocklocation-label-print',
),
re_path(
r'metadata/',
MetadataView.as_view(),
{'model': label.models.StockLocationLabel},
name='api-stocklocation-label-metadata',
),
re_path(
r'^.*$',
StockLocationLabelDetail.as_view(),
name='api-stocklocation-label-detail',
),
]),
),
# List view
re_path(
r'^.*$',
StockLocationLabelList.as_view(),
name='api-stocklocation-label-list',
),
]),
),
# Part labels
re_path(r'^part/', include([
# Detail views
path(r'<int:pk>/', include([
re_path(r'^print/', PartLabelPrint.as_view(), name='api-part-label-print'),
re_path(r'^metadata/', MetadataView.as_view(), {'model': label.models.PartLabel}, name='api-part-label-metadata'),
re_path(r'^.*$', PartLabelDetail.as_view(), name='api-part-label-detail'),
])),
# List view
re_path(r'^.*$', PartLabelList.as_view(), name='api-part-label-list'),
])),
re_path(
r'^part/',
include([
# Detail views
path(
r'<int:pk>/',
include([
re_path(
r'^print/',
PartLabelPrint.as_view(),
name='api-part-label-print',
),
re_path(
r'^metadata/',
MetadataView.as_view(),
{'model': label.models.PartLabel},
name='api-part-label-metadata',
),
re_path(
r'^.*$', PartLabelDetail.as_view(), name='api-part-label-detail'
),
]),
),
# List view
re_path(r'^.*$', PartLabelList.as_view(), name='api-part-label-list'),
]),
),
# BuildLine labels
re_path(r'^buildline/', include([
# Detail views
path(r'<int:pk>/', include([
re_path(r'^print/', BuildLineLabelPrint.as_view(), name='api-buildline-label-print'),
re_path(r'^metadata/', MetadataView.as_view(), {'model': label.models.BuildLineLabel}, name='api-buildline-label-metadata'),
re_path(r'^.*$', BuildLineLabelDetail.as_view(), name='api-buildline-label-detail'),
])),
# List view
re_path(r'^.*$', BuildLineLabelList.as_view(), name='api-buildline-label-list'),
])),
re_path(
r'^buildline/',
include([
# Detail views
path(
r'<int:pk>/',
include([
re_path(
r'^print/',
BuildLineLabelPrint.as_view(),
name='api-buildline-label-print',
),
re_path(
r'^metadata/',
MetadataView.as_view(),
{'model': label.models.BuildLineLabel},
name='api-buildline-label-metadata',
),
re_path(
r'^.*$',
BuildLineLabelDetail.as_view(),
name='api-buildline-label-detail',
),
]),
),
# List view
re_path(
r'^.*$', BuildLineLabelList.as_view(), name='api-buildline-label-list'
),
]),
),
]

View File

@@ -14,7 +14,7 @@ from django.db.utils import IntegrityError, OperationalError, ProgrammingError
import InvenTree.ready
logger = logging.getLogger("inventree")
logger = logging.getLogger('inventree')
def hashFile(filename):
@@ -36,23 +36,37 @@ class LabelConfig(AppConfig):
def ready(self):
"""This function is called whenever the label app is loaded."""
# skip loading if plugin registry is not loaded or we run in a background thread
if not InvenTree.ready.isPluginRegistryLoaded() or not InvenTree.ready.isInMainThread():
if (
not InvenTree.ready.isPluginRegistryLoaded()
or not InvenTree.ready.isInMainThread()
):
return
if InvenTree.ready.isRunningMigrations():
return
if InvenTree.ready.canAppAccessDatabase(allow_test=False) and not InvenTree.ready.isImportingData():
if (
InvenTree.ready.canAppAccessDatabase(allow_test=False)
and not InvenTree.ready.isImportingData()
):
try:
self.create_labels() # pragma: no cover
except (AppRegistryNotReady, IntegrityError, OperationalError, ProgrammingError):
except (
AppRegistryNotReady,
IntegrityError,
OperationalError,
ProgrammingError,
):
# Database might not yet be ready
warnings.warn('Database was not ready for creating labels', stacklevel=2)
warnings.warn(
'Database was not ready for creating labels', stacklevel=2
)
def create_labels(self):
"""Create all default templates."""
# Test if models are ready
import label.models
assert bool(label.models.StockLocationLabel is not None)
# Create the categories
@@ -66,7 +80,7 @@ class LabelConfig(AppConfig):
'description': 'Simple QR code label',
'width': 24,
'height': 24,
},
}
],
)
@@ -87,8 +101,8 @@ class LabelConfig(AppConfig):
'description': 'Label with QR code and name of location',
'width': 50,
'height': 24,
}
]
},
],
)
self.create_labels_category(
@@ -109,7 +123,7 @@ class LabelConfig(AppConfig):
'width': 70,
'height': 24,
},
]
],
)
self.create_labels_category(
@@ -122,24 +136,16 @@ class LabelConfig(AppConfig):
'description': 'Example build line label',
'width': 125,
'height': 48,
},
]
}
],
)
def create_labels_category(self, model, ref_name, labels):
"""Create folder and database entries for the default templates, if they do not already exist."""
# Create root dir for templates
src_dir = Path(__file__).parent.joinpath(
'templates',
'label',
ref_name,
)
src_dir = Path(__file__).parent.joinpath('templates', 'label', ref_name)
dst_dir = settings.MEDIA_ROOT.joinpath(
'label',
'inventree',
ref_name,
)
dst_dir = settings.MEDIA_ROOT.joinpath('label', 'inventree', ref_name)
if not dst_dir.exists():
logger.info("Creating required directory: '%s'", dst_dir)
@@ -151,12 +157,7 @@ class LabelConfig(AppConfig):
def create_template_label(self, model, src_dir, ref_name, label):
"""Ensure a label template is in place."""
filename = os.path.join(
'label',
'inventree',
ref_name,
label['file']
)
filename = os.path.join('label', 'inventree', ref_name, label['file'])
src_file = src_dir.joinpath(label['file'])
dst_file = settings.MEDIA_ROOT.joinpath(filename)
@@ -187,7 +188,10 @@ class LabelConfig(AppConfig):
if model.objects.filter(label=filename).exists():
return # pragma: no cover
except Exception:
logger.exception("Failed to query label for '%s' - you should run 'invoke update' first!", filename)
logger.exception(
"Failed to query label for '%s' - you should run 'invoke update' first!",
filename,
)
logger.info("Creating entry for %s '%s'", model, label['name'])

View File

@@ -25,12 +25,12 @@ from plugin.registry import registry
try:
from django_weasyprint import WeasyTemplateResponseMixin
except OSError as err: # pragma: no cover
print(f"OSError: {err}")
print("You may require some further system packages to be installed.")
print(f'OSError: {err}')
print('You may require some further system packages to be installed.')
sys.exit(1)
logger = logging.getLogger("inventree")
logger = logging.getLogger('inventree')
def rename_label(instance, filename):
@@ -97,7 +97,7 @@ class LabelTemplate(MetadataMixin, models.Model):
abstract = True
# Each class of label files will be stored in a separate subdirectory
SUBDIR = "label"
SUBDIR = 'label'
# Object we will be printing against (will be filled out later)
object_to_print = None
@@ -109,17 +109,16 @@ class LabelTemplate(MetadataMixin, models.Model):
def __str__(self):
"""Format a string representation of a label instance"""
return f"{self.name} - {self.description}"
return f'{self.name} - {self.description}'
name = models.CharField(
blank=False, max_length=100,
verbose_name=_('Name'),
help_text=_('Label name'),
blank=False, max_length=100, verbose_name=_('Name'), help_text=_('Label name')
)
description = models.CharField(
max_length=250,
blank=True, null=True,
blank=True,
null=True,
verbose_name=_('Description'),
help_text=_('Label description'),
)
@@ -127,7 +126,8 @@ class LabelTemplate(MetadataMixin, models.Model):
label = models.FileField(
upload_to=rename_label,
unique=True,
blank=False, null=False,
blank=False,
null=False,
verbose_name=_('Label'),
help_text=_('Label template file'),
validators=[FileExtensionValidator(allowed_extensions=['html'])],
@@ -143,18 +143,18 @@ class LabelTemplate(MetadataMixin, models.Model):
default=50,
verbose_name=_('Width [mm]'),
help_text=_('Label width, specified in mm'),
validators=[MinValueValidator(2)]
validators=[MinValueValidator(2)],
)
height = models.FloatField(
default=20,
verbose_name=_('Height [mm]'),
help_text=_('Label height, specified in mm'),
validators=[MinValueValidator(2)]
validators=[MinValueValidator(2)],
)
filename_pattern = models.CharField(
default="label.pdf",
default='label.pdf',
verbose_name=_('Filename Pattern'),
help_text=_('Pattern for generating label filenames'),
max_length=100,
@@ -249,11 +249,7 @@ class LabelTemplate(MetadataMixin, models.Model):
context = self.context(request, **kwargs)
return render_to_string(
self.template_name,
context,
request
)
return render_to_string(self.template_name, context, request)
def render(self, request, target_object=None, **kwargs):
"""Render the label template to a PDF file.
@@ -269,16 +265,13 @@ class LabelTemplate(MetadataMixin, models.Model):
wp = WeasyprintLabelMixin(
request,
self.template_name,
base_url=request.build_absolute_uri("/"),
base_url=request.build_absolute_uri('/'),
presentational_hints=True,
filename=self.generate_filename(request),
**kwargs
**kwargs,
)
return wp.render_to_response(
context,
**kwargs
)
return wp.render_to_response(context, **kwargs)
class LabelOutput(models.Model):
@@ -293,22 +286,14 @@ class LabelOutput(models.Model):
# File will be stored in a subdirectory
label = models.FileField(
upload_to=rename_label_output,
unique=True, blank=False, null=False,
upload_to=rename_label_output, unique=True, blank=False, null=False
)
# Creation date of label output
created = models.DateField(
auto_now_add=True,
editable=False,
)
created = models.DateField(auto_now_add=True, editable=False)
# User who generated the label
user = models.ForeignKey(
User,
on_delete=models.SET_NULL,
blank=True, null=True,
)
user = models.ForeignKey(User, on_delete=models.SET_NULL, blank=True, null=True)
class StockItemLabel(LabelTemplate):
@@ -319,15 +304,14 @@ class StockItemLabel(LabelTemplate):
"""Return the API URL associated with the StockItemLabel model"""
return reverse('api-stockitem-label-list') # pragma: no cover
SUBDIR = "stockitem"
SUBDIR = 'stockitem'
filters = models.CharField(
blank=True, max_length=250,
blank=True,
max_length=250,
help_text=_('Query filters (comma-separated list of key=value pairs)'),
verbose_name=_('Filters'),
validators=[
validate_stock_item_filters
]
validators=[validate_stock_item_filters],
)
def get_context_data(self, request):
@@ -348,7 +332,6 @@ class StockItemLabel(LabelTemplate):
'qr_url': request.build_absolute_uri(stock_item.get_absolute_url()),
'tests': stock_item.testResultMap(),
'parameters': stock_item.part.parameters_map(),
}
@@ -360,24 +343,21 @@ class StockLocationLabel(LabelTemplate):
"""Return the API URL associated with the StockLocationLabel model"""
return reverse('api-stocklocation-label-list') # pragma: no cover
SUBDIR = "stocklocation"
SUBDIR = 'stocklocation'
filters = models.CharField(
blank=True, max_length=250,
blank=True,
max_length=250,
help_text=_('Query filters (comma-separated list of key=value pairs)'),
verbose_name=_('Filters'),
validators=[
validate_stock_location_filters]
validators=[validate_stock_location_filters],
)
def get_context_data(self, request):
"""Generate context data for each provided StockLocation."""
location = self.object_to_print
return {
'location': location,
'qr_data': location.format_barcode(brief=True),
}
return {'location': location, 'qr_data': location.format_barcode(brief=True)}
class PartLabel(LabelTemplate):
@@ -391,12 +371,11 @@ class PartLabel(LabelTemplate):
SUBDIR = 'part'
filters = models.CharField(
blank=True, max_length=250,
blank=True,
max_length=250,
help_text=_('Query filters (comma-separated list of key=value pairs)'),
verbose_name=_('Filters'),
validators=[
validate_part_filters
]
validators=[validate_part_filters],
)
def get_context_data(self, request):
@@ -427,12 +406,11 @@ class BuildLineLabel(LabelTemplate):
SUBDIR = 'buildline'
filters = models.CharField(
blank=True, max_length=250,
blank=True,
max_length=250,
help_text=_('Query filters (comma-separated list of key=value pairs)'),
verbose_name=_('Filters'),
validators=[
validate_build_line_filters
]
validators=[validate_build_line_filters],
)
def get_context_data(self, request):

View File

@@ -1,8 +1,10 @@
"""API serializers for the label app"""
import label.models
from InvenTree.serializers import (InvenTreeAttachmentSerializerField,
InvenTreeModelSerializer)
from InvenTree.serializers import (
InvenTreeAttachmentSerializerField,
InvenTreeModelSerializer,
)
class LabelSerializerBase(InvenTreeModelSerializer):
@@ -13,14 +15,7 @@ class LabelSerializerBase(InvenTreeModelSerializer):
@staticmethod
def label_fields():
"""Generic serializer fields for a label template"""
return [
'pk',
'name',
'description',
'label',
'filters',
'enabled',
]
return ['pk', 'name', 'description', 'label', 'filters', 'enabled']
class StockItemLabelSerializer(LabelSerializerBase):

View File

@@ -8,17 +8,9 @@ from InvenTree.unit_test import InvenTreeAPITestCase
class TestReportTests(InvenTreeAPITestCase):
"""Tests for the StockItem TestReport templates."""
fixtures = [
'category',
'part',
'location',
'stock',
]
fixtures = ['category', 'part', 'location', 'stock']
roles = [
'stock.view',
'stock_location.view',
]
roles = ['stock.view', 'stock_location.view']
list_url = reverse('api-stockitem-testreport-list')
@@ -42,22 +34,10 @@ class TestReportTests(InvenTreeAPITestCase):
self.assertEqual(len(response), 0)
# TODO - Add some tests to this response
response = self.do_list(
{
'item': 10,
}
)
response = self.do_list({'item': 10})
# TODO - Add some tests to this response
response = self.do_list(
{
'item': 100000,
}
)
response = self.do_list({'item': 100000})
# TODO - Add some tests to this response
response = self.do_list(
{
'items': [10, 11, 12],
}
)
response = self.do_list({'items': [10, 11, 12]})

View File

@@ -24,12 +24,7 @@ from .models import PartLabel, StockItemLabel, StockLocationLabel
class LabelTest(InvenTreeAPITestCase):
"""Unit test class for label models"""
fixtures = [
'category',
'part',
'location',
'stock'
]
fixtures = ['category', 'part', 'location', 'stock']
@classmethod
def setUpTestData(cls):
@@ -49,12 +44,9 @@ class LabelTest(InvenTreeAPITestCase):
def test_default_files(self):
"""Test that label files exist in the MEDIA directory."""
def test_subdir(ref_name):
item_dir = settings.MEDIA_ROOT.joinpath(
'label',
'inventree',
ref_name,
)
item_dir = settings.MEDIA_ROOT.joinpath('label', 'inventree', ref_name)
self.assertTrue(len([item_dir.iterdir()]) > 0)
test_subdir('stockitem')
@@ -63,13 +55,13 @@ class LabelTest(InvenTreeAPITestCase):
def test_filters(self):
"""Test the label filters."""
filter_string = "part__pk=10"
filter_string = 'part__pk=10'
filters = validateFilterString(filter_string, model=StockItem)
self.assertEqual(type(filters), dict)
bad_filter_string = "part_pk=10"
bad_filter_string = 'part_pk=10'
with self.assertRaises(ValidationError):
validateFilterString(bad_filter_string, model=StockItem)
@@ -115,14 +107,11 @@ class LabelTest(InvenTreeAPITestCase):
buffer = io.StringIO()
buffer.write(label_data)
template = ContentFile(buffer.getvalue(), "label.html")
template = ContentFile(buffer.getvalue(), 'label.html')
# Construct a label template
label = PartLabel.objects.create(
name='test',
description='Test label',
enabled=True,
label=template,
name='test', description='Test label', enabled=True, label=template
)
# Ensure we are in "debug" mode (so the report is generated as HTML)
@@ -151,7 +140,7 @@ class LabelTest(InvenTreeAPITestCase):
content = f.read()
# Test that each element has been rendered correctly
self.assertIn(f"part: {part_pk} - {part_name}", content)
self.assertIn(f'part: {part_pk} - {part_name}', content)
self.assertIn(f'data: {{"part": {part_pk}}}', content)
self.assertIn(f'http://testserver/part/{part_pk}/', content)