mirror of
				https://github.com/inventree/InvenTree.git
				synced 2025-10-30 20:55:42 +00:00 
			
		
		
		
	[CI] Enable python autoformat (#6169)
* Squashed commit of the following: commitf5cf7b2e78Author: Matthias Mair <code@mjmair.com> Date: Sun Jan 7 20:36:57 2024 +0100 fixed reqs commit9d845bee98Author: Matthias Mair <code@mjmair.com> Date: Sun Jan 7 20:32:35 2024 +0100 disable autofix/format commitaff5f27148Author: Matthias Mair <code@mjmair.com> Date: Sun Jan 7 20:28:50 2024 +0100 adjust checks commit47271cf1efAuthor: Matthias Mair <code@mjmair.com> Date: Sun Jan 7 20:28:22 2024 +0100 reorder order of operations commite1bf178b40Author: Matthias Mair <code@mjmair.com> Date: Sun Jan 7 20:01:09 2024 +0100 adapted ruff settings to better fit code base commitad7d88a6f4Author: Matthias Mair <code@mjmair.com> Date: Sun Jan 7 19:59:45 2024 +0100 auto fixed docstring commita2e54a760eAuthor: Matthias Mair <code@mjmair.com> Date: Sun Jan 7 19:46:35 2024 +0100 fix getattr useage commitcb80c73bc6Author: Matthias Mair <code@mjmair.com> Date: Sun Jan 7 19:25:09 2024 +0100 fix requirements file commitb7780bbd21Author: Matthias Mair <code@mjmair.com> Date: Sun Jan 7 18:42:28 2024 +0100 fix removed sections commit71f1681f55Author: Matthias Mair <code@mjmair.com> Date: Sun Jan 7 18:41:21 2024 +0100 fix djlint syntax commita0bcf1bcceAuthor: Matthias Mair <code@mjmair.com> Date: Sun Jan 7 18:35:28 2024 +0100 remove flake8 from code base commit22475b31ccAuthor: Matthias Mair <code@mjmair.com> Date: Sun Jan 7 18:34:56 2024 +0100 remove flake8 from code base commit0413350f14Author: Matthias Mair <code@mjmair.com> Date: Sun Jan 7 18:24:39 2024 +0100 moved ruff section commitd90c48a0bfAuthor: Matthias Mair <code@mjmair.com> Date: Sun Jan 7 18:24:24 2024 +0100 move djlint config to pyproject commitc5ce55d511Author: Matthias Mair <code@mjmair.com> Date: Sun Jan 7 18:20:39 2024 +0100 added isort again commit42a41d23afAuthor: Matthias Mair <code@mjmair.com> Date: Sun Jan 7 18:19:02 2024 +0100 move config section commit8569233181Author: Matthias Mair <code@mjmair.com> Date: Sun Jan 7 18:17:52 2024 +0100 fix codespell error commit2897c6704dAuthor: 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: commitd3b795824bAuthor: Matthias Mair <code@mjmair.com> Date: Sun Jan 7 22:56:17 2024 +0100 fixed source path commit0bac0c19b8Author: Matthias Mair <code@mjmair.com> Date: Sun Jan 7 22:47:53 2024 +0100 fixed req commit9f61f01d9cAuthor: Matthias Mair <code@mjmair.com> Date: Sun Jan 7 22:45:18 2024 +0100 added missing toml req commit91b71ed24aAuthor: Matthias Mair <code@mjmair.com> Date: Sun Jan 7 20:49:50 2024 +0100 moved isort config commit12460b0419Author: Matthias Mair <code@mjmair.com> Date: Sun Jan 7 20:43:22 2024 +0100 remove flake8 section from setup.cfg commitf5cf7b2e78Author: Matthias Mair <code@mjmair.com> Date: Sun Jan 7 20:36:57 2024 +0100 fixed reqs commit9d845bee98Author: Matthias Mair <code@mjmair.com> Date: Sun Jan 7 20:32:35 2024 +0100 disable autofix/format commitaff5f27148Author: Matthias Mair <code@mjmair.com> Date: Sun Jan 7 20:28:50 2024 +0100 adjust checks commit47271cf1efAuthor: Matthias Mair <code@mjmair.com> Date: Sun Jan 7 20:28:22 2024 +0100 reorder order of operations commite1bf178b40Author: Matthias Mair <code@mjmair.com> Date: Sun Jan 7 20:01:09 2024 +0100 adapted ruff settings to better fit code base commitad7d88a6f4Author: Matthias Mair <code@mjmair.com> Date: Sun Jan 7 19:59:45 2024 +0100 auto fixed docstring commita2e54a760eAuthor: Matthias Mair <code@mjmair.com> Date: Sun Jan 7 19:46:35 2024 +0100 fix getattr useage commitcb80c73bc6Author: Matthias Mair <code@mjmair.com> Date: Sun Jan 7 19:25:09 2024 +0100 fix requirements file commitb7780bbd21Author: Matthias Mair <code@mjmair.com> Date: Sun Jan 7 18:42:28 2024 +0100 fix removed sections commit71f1681f55Author: Matthias Mair <code@mjmair.com> Date: Sun Jan 7 18:41:21 2024 +0100 fix djlint syntax commita0bcf1bcceAuthor: Matthias Mair <code@mjmair.com> Date: Sun Jan 7 18:35:28 2024 +0100 remove flake8 from code base commit22475b31ccAuthor: Matthias Mair <code@mjmair.com> Date: Sun Jan 7 18:34:56 2024 +0100 remove flake8 from code base commit0413350f14Author: Matthias Mair <code@mjmair.com> Date: Sun Jan 7 18:24:39 2024 +0100 moved ruff section commitd90c48a0bfAuthor: Matthias Mair <code@mjmair.com> Date: Sun Jan 7 18:24:24 2024 +0100 move djlint config to pyproject commitc5ce55d511Author: Matthias Mair <code@mjmair.com> Date: Sun Jan 7 18:20:39 2024 +0100 added isort again commit42a41d23afAuthor: Matthias Mair <code@mjmair.com> Date: Sun Jan 7 18:19:02 2024 +0100 move config section commit8569233181Author: Matthias Mair <code@mjmair.com> Date: Sun Jan 7 18:17:52 2024 +0100 fix codespell error commit2897c6704dAuthor: 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:
		| @@ -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') | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -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' | ||||
|             ), | ||||
|         ]), | ||||
|     ), | ||||
| ] | ||||
|   | ||||
| @@ -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']) | ||||
|  | ||||
|   | ||||
| @@ -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): | ||||
|   | ||||
| @@ -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): | ||||
|   | ||||
| @@ -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]}) | ||||
|   | ||||
| @@ -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) | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user