mirror of
https://github.com/inventree/InvenTree
synced 2024-08-30 18:33:04 +00:00
Fix removed stuff from merge 99676ee
This commit is contained in:
parent
0db69252d7
commit
6f498d6292
16
.github/workflows/qc_checks.yaml
vendored
16
.github/workflows/qc_checks.yaml
vendored
@ -133,6 +133,22 @@ jobs:
|
||||
invoke check-server
|
||||
coverage run -m unittest discover -s test/
|
||||
|
||||
docstyle:
|
||||
name: Style [Python Docstrings]
|
||||
runs-on: ubuntu-20.04
|
||||
|
||||
needs: pre-commit
|
||||
continue-on-error: true
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
- name: Enviroment Setup
|
||||
uses: ./.github/actions/setup
|
||||
with:
|
||||
install: true
|
||||
- name: Run flake8
|
||||
run: flake8 InvenTree
|
||||
|
||||
coverage:
|
||||
name: Tests - DB [SQLite] + Coverage
|
||||
runs-on: ubuntu-20.04
|
||||
|
@ -1,5 +1,5 @@
|
||||
"""
|
||||
Version information for InvenTree.
|
||||
"""Version information for InvenTree.
|
||||
|
||||
Provides information on the current InvenTree version
|
||||
"""
|
||||
|
||||
@ -17,12 +17,12 @@ INVENTREE_SW_VERSION = "0.8.0 dev"
|
||||
|
||||
|
||||
def inventreeInstanceName():
|
||||
""" Returns the InstanceName settings for the current database """
|
||||
"""Returns the InstanceName settings for the current database."""
|
||||
return common.models.InvenTreeSetting.get_setting("INVENTREE_INSTANCE", "")
|
||||
|
||||
|
||||
def inventreeInstanceTitle():
|
||||
""" Returns the InstanceTitle for the current database """
|
||||
"""Returns the InstanceTitle for the current database."""
|
||||
if common.models.InvenTreeSetting.get_setting("INVENTREE_INSTANCE_TITLE", False):
|
||||
return common.models.InvenTreeSetting.get_setting("INVENTREE_INSTANCE", "")
|
||||
else:
|
||||
@ -30,13 +30,12 @@ def inventreeInstanceTitle():
|
||||
|
||||
|
||||
def inventreeVersion():
|
||||
""" Returns the InvenTree version string """
|
||||
"""Returns the InvenTree version string."""
|
||||
return INVENTREE_SW_VERSION.lower().strip()
|
||||
|
||||
|
||||
def inventreeVersionTuple(version=None):
|
||||
""" Return the InvenTree version string as (maj, min, sub) tuple """
|
||||
|
||||
"""Return the InvenTree version string as (maj, min, sub) tuple."""
|
||||
if version is None:
|
||||
version = INVENTREE_SW_VERSION
|
||||
|
||||
@ -46,21 +45,16 @@ def inventreeVersionTuple(version=None):
|
||||
|
||||
|
||||
def isInvenTreeDevelopmentVersion():
|
||||
"""
|
||||
Return True if current InvenTree version is a "development" version
|
||||
"""
|
||||
"""Return True if current InvenTree version is a "development" version."""
|
||||
return inventreeVersion().endswith('dev')
|
||||
|
||||
|
||||
def inventreeDocsVersion():
|
||||
"""
|
||||
Return the version string matching the latest documentation.
|
||||
"""Return the version string matching the latest documentation.
|
||||
|
||||
Development -> "latest"
|
||||
Release -> "major.minor.sub" e.g. "0.5.2"
|
||||
|
||||
"""
|
||||
|
||||
if isInvenTreeDevelopmentVersion():
|
||||
return "latest"
|
||||
else:
|
||||
@ -68,13 +62,10 @@ def inventreeDocsVersion():
|
||||
|
||||
|
||||
def isInvenTreeUpToDate():
|
||||
"""
|
||||
Test if the InvenTree instance is "up to date" with the latest version.
|
||||
"""Test if the InvenTree instance is "up to date" with the latest version.
|
||||
|
||||
A background task periodically queries GitHub for latest version,
|
||||
and stores it to the database as INVENTREE_LATEST_VERSION
|
||||
A background task periodically queries GitHub for latest version, and stores it to the database as INVENTREE_LATEST_VERSION
|
||||
"""
|
||||
|
||||
latest = common.models.InvenTreeSetting.get_setting('INVENTREE_LATEST_VERSION', backup_value=None, create=False)
|
||||
|
||||
# No record for "latest" version - we must assume we are up to date!
|
||||
@ -93,13 +84,12 @@ def inventreeApiVersion():
|
||||
|
||||
|
||||
def inventreeDjangoVersion():
|
||||
""" Return the version of Django library """
|
||||
"""Return the version of Django library."""
|
||||
return django.get_version()
|
||||
|
||||
|
||||
def inventreeCommitHash():
|
||||
""" Returns the git commit hash for the running codebase """
|
||||
|
||||
""" Returns the git commit hash for the running codebase."""
|
||||
# First look in the environment variables, i.e. if running in docker
|
||||
commit_hash = os.environ.get('INVENTREE_COMMIT_HASH', '')
|
||||
|
||||
@ -113,8 +103,7 @@ def inventreeCommitHash():
|
||||
|
||||
|
||||
def inventreeCommitDate():
|
||||
""" Returns the git commit date for the running codebase """
|
||||
|
||||
"""Returns the git commit date for the running codebase."""
|
||||
# First look in the environment variables, e.g. if running in docker
|
||||
commit_date = os.environ.get('INVENTREE_COMMIT_DATE', '')
|
||||
|
||||
|
@ -12,9 +12,7 @@ logger = logging.getLogger('inventree')
|
||||
|
||||
# region methods
|
||||
class NotificationMethod:
|
||||
"""
|
||||
Base class for notification methods
|
||||
"""
|
||||
"""Base class for notification methods."""
|
||||
|
||||
METHOD_NAME = ''
|
||||
METHOD_ICON = None
|
||||
@ -92,11 +90,11 @@ class NotificationMethod:
|
||||
|
||||
# region plugins
|
||||
def get_plugin(self):
|
||||
"""Returns plugin class"""
|
||||
"""Returns plugin class."""
|
||||
return False
|
||||
|
||||
def global_setting_disable(self):
|
||||
"""Check if the method is defined in a plugin and has a global setting"""
|
||||
"""Check if the method is defined in a plugin and has a global setting."""
|
||||
# Check if plugin has a setting
|
||||
if not self.GLOBAL_SETTING:
|
||||
return False
|
||||
@ -115,9 +113,7 @@ class NotificationMethod:
|
||||
return False
|
||||
|
||||
def usersetting(self, target):
|
||||
"""
|
||||
Returns setting for this method for a given user
|
||||
"""
|
||||
"""Returns setting for this method for a given user."""
|
||||
return NotificationUserSetting.get_setting(f'NOTIFICATION_METHOD_{self.METHOD_NAME.upper()}', user=target, method=self.METHOD_NAME)
|
||||
# endregion
|
||||
|
||||
@ -204,10 +200,7 @@ class UIMessageNotification(SingleNotificationMethod):
|
||||
|
||||
|
||||
def trigger_notification(obj, category=None, obj_ref='pk', **kwargs):
|
||||
"""
|
||||
Send out a notification
|
||||
"""
|
||||
|
||||
"""Send out a notification."""
|
||||
targets = kwargs.get('targets', None)
|
||||
target_fnc = kwargs.get('target_fnc', None)
|
||||
target_args = kwargs.get('target_args', [])
|
||||
|
@ -21,9 +21,7 @@ from .serializers import (PartLabelSerializer, StockItemLabelSerializer,
|
||||
|
||||
|
||||
class LabelListView(generics.ListAPIView):
|
||||
"""
|
||||
Generic API class for label templates
|
||||
"""
|
||||
"""Generic API class for label templates."""
|
||||
|
||||
filter_backends = [
|
||||
DjangoFilterBackend,
|
||||
@ -41,13 +39,11 @@ class LabelListView(generics.ListAPIView):
|
||||
|
||||
|
||||
class LabelPrintMixin:
|
||||
"""
|
||||
Mixin for printing labels
|
||||
"""
|
||||
"""Mixin for printing labels."""
|
||||
|
||||
def get_plugin(self, request):
|
||||
"""
|
||||
Return the label printing plugin associated with this request.
|
||||
"""Return the label printing plugin associated with this request.
|
||||
|
||||
This is provided in the url, e.g. ?plugin=myprinter
|
||||
|
||||
Requires:
|
||||
@ -56,7 +52,6 @@ class LabelPrintMixin:
|
||||
- matching plugin implements the 'labels' mixin
|
||||
- matching plugin is enabled
|
||||
"""
|
||||
|
||||
if not settings.PLUGINS_ENABLED:
|
||||
return None # pragma: no cover
|
||||
|
||||
@ -80,10 +75,7 @@ class LabelPrintMixin:
|
||||
raise NotFound(f"Plugin '{plugin_key}' not found")
|
||||
|
||||
def print(self, request, items_to_print):
|
||||
"""
|
||||
Print this label template against a number of pre-validated items
|
||||
"""
|
||||
|
||||
"""Print this label template against a number of pre-validated items."""
|
||||
# Check the request to determine if the user has selected a label printing plugin
|
||||
plugin = self.get_plugin(request)
|
||||
|
||||
@ -119,26 +111,20 @@ class LabelPrintMixin:
|
||||
label_name += ".pdf"
|
||||
|
||||
if plugin is not None:
|
||||
"""
|
||||
Label printing is to be handled by a plugin,
|
||||
rather than being exported to PDF.
|
||||
"""Label printing is to be handled by a plugin, rather than being exported to PDF.
|
||||
|
||||
In this case, we do the following:
|
||||
|
||||
- Individually generate each label, exporting as an image file
|
||||
- Pass all the images through to the label printing plugin
|
||||
- Return a JSON response indicating that the printing has been offloaded
|
||||
|
||||
"""
|
||||
|
||||
# Label instance
|
||||
label_instance = self.get_object()
|
||||
|
||||
for idx, output in enumerate(outputs):
|
||||
"""
|
||||
For each output, we generate a temporary image file,
|
||||
which will then get sent to the printer
|
||||
"""
|
||||
"""For each output, we generate a temporary image file, which will then get sent to the printer."""
|
||||
|
||||
# Generate PDF data for the label
|
||||
pdf = output.get_document().write_pdf()
|
||||
@ -159,20 +145,14 @@ class LabelPrintMixin:
|
||||
})
|
||||
|
||||
elif debug_mode:
|
||||
"""
|
||||
Contatenate all rendered templates into a single HTML string,
|
||||
and return the string as a HTML response.
|
||||
"""
|
||||
"""Contatenate all rendered templates into a single HTML string, and return the string as a HTML response."""
|
||||
|
||||
html = "\n".join(outputs)
|
||||
|
||||
return HttpResponse(html)
|
||||
|
||||
else:
|
||||
"""
|
||||
Concatenate all rendered pages into a single PDF object,
|
||||
and return the resulting document!
|
||||
"""
|
||||
"""Concatenate all rendered pages into a single PDF object, and return the resulting document!"""
|
||||
|
||||
pages = []
|
||||
|
||||
@ -198,15 +178,10 @@ class LabelPrintMixin:
|
||||
|
||||
|
||||
class StockItemLabelMixin:
|
||||
"""
|
||||
Mixin for extracting stock items from query params
|
||||
"""
|
||||
"""Mixin for extracting stock items from query params."""
|
||||
|
||||
def get_items(self):
|
||||
"""
|
||||
Return a list of requested stock items
|
||||
"""
|
||||
|
||||
"""Return a list of requested stock items."""
|
||||
items = []
|
||||
|
||||
params = self.request.query_params
|
||||
@ -231,25 +206,20 @@ class StockItemLabelMixin:
|
||||
|
||||
|
||||
class StockItemLabelList(LabelListView, StockItemLabelMixin):
|
||||
"""
|
||||
API endpoint for viewing list of StockItemLabel objects.
|
||||
"""API endpoint for viewing list of StockItemLabel objects.
|
||||
|
||||
Filterable by:
|
||||
|
||||
- enabled: Filter by enabled / disabled status
|
||||
- item: Filter by single stock item
|
||||
- items: Filter by list of stock items
|
||||
|
||||
"""
|
||||
|
||||
queryset = StockItemLabel.objects.all()
|
||||
serializer_class = StockItemLabelSerializer
|
||||
|
||||
def filter_queryset(self, queryset):
|
||||
"""
|
||||
Filter the StockItem label queryset.
|
||||
"""
|
||||
|
||||
"""Filter the StockItem label queryset."""
|
||||
queryset = super().filter_queryset(queryset)
|
||||
|
||||
# List of StockItem objects to match against
|
||||
@ -304,42 +274,30 @@ class StockItemLabelList(LabelListView, StockItemLabelMixin):
|
||||
|
||||
|
||||
class StockItemLabelDetail(generics.RetrieveUpdateDestroyAPIView):
|
||||
"""
|
||||
API endpoint for a single StockItemLabel object
|
||||
"""
|
||||
"""API endpoint for a single StockItemLabel object."""
|
||||
|
||||
queryset = StockItemLabel.objects.all()
|
||||
serializer_class = StockItemLabelSerializer
|
||||
|
||||
|
||||
class StockItemLabelPrint(generics.RetrieveAPIView, StockItemLabelMixin, LabelPrintMixin):
|
||||
"""
|
||||
API endpoint for printing a StockItemLabel object
|
||||
"""
|
||||
"""API endpoint for printing a StockItemLabel object."""
|
||||
|
||||
queryset = StockItemLabel.objects.all()
|
||||
serializer_class = StockItemLabelSerializer
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
"""
|
||||
Check if valid stock item(s) have been provided.
|
||||
"""
|
||||
|
||||
"""Check if valid stock item(s) have been provided."""
|
||||
items = self.get_items()
|
||||
|
||||
return self.print(request, items)
|
||||
|
||||
|
||||
class StockLocationLabelMixin:
|
||||
"""
|
||||
Mixin for extracting stock locations from query params
|
||||
"""
|
||||
"""Mixin for extracting stock locations from query params."""
|
||||
|
||||
def get_locations(self):
|
||||
"""
|
||||
Return a list of requested stock locations
|
||||
"""
|
||||
|
||||
"""Return a list of requested stock locations."""
|
||||
locations = []
|
||||
|
||||
params = self.request.query_params
|
||||
@ -364,8 +322,7 @@ class StockLocationLabelMixin:
|
||||
|
||||
|
||||
class StockLocationLabelList(LabelListView, StockLocationLabelMixin):
|
||||
"""
|
||||
API endpoint for viewiing list of StockLocationLabel objects.
|
||||
"""API endpoint for viewiing list of StockLocationLabel objects.
|
||||
|
||||
Filterable by:
|
||||
|
||||
@ -378,10 +335,7 @@ class StockLocationLabelList(LabelListView, StockLocationLabelMixin):
|
||||
serializer_class = StockLocationLabelSerializer
|
||||
|
||||
def filter_queryset(self, queryset):
|
||||
"""
|
||||
Filter the StockLocationLabel queryset
|
||||
"""
|
||||
|
||||
"""Filter the StockLocationLabel queryset."""
|
||||
queryset = super().filter_queryset(queryset)
|
||||
|
||||
# List of StockLocation objects to match against
|
||||
@ -436,18 +390,14 @@ class StockLocationLabelList(LabelListView, StockLocationLabelMixin):
|
||||
|
||||
|
||||
class StockLocationLabelDetail(generics.RetrieveUpdateDestroyAPIView):
|
||||
"""
|
||||
API endpoint for a single StockLocationLabel object
|
||||
"""
|
||||
"""API endpoint for a single StockLocationLabel object."""
|
||||
|
||||
queryset = StockLocationLabel.objects.all()
|
||||
serializer_class = StockLocationLabelSerializer
|
||||
|
||||
|
||||
class StockLocationLabelPrint(generics.RetrieveAPIView, StockLocationLabelMixin, LabelPrintMixin):
|
||||
"""
|
||||
API endpoint for printing a StockLocationLabel object
|
||||
"""
|
||||
"""API endpoint for printing a StockLocationLabel object."""
|
||||
|
||||
queryset = StockLocationLabel.objects.all()
|
||||
seiralizer_class = StockLocationLabelSerializer
|
||||
@ -460,15 +410,10 @@ class StockLocationLabelPrint(generics.RetrieveAPIView, StockLocationLabelMixin,
|
||||
|
||||
|
||||
class PartLabelMixin:
|
||||
"""
|
||||
Mixin for extracting Part objects from query parameters
|
||||
"""
|
||||
"""Mixin for extracting Part objects from query parameters."""
|
||||
|
||||
def get_parts(self):
|
||||
"""
|
||||
Return a list of requested Part objects
|
||||
"""
|
||||
|
||||
"""Return a list of requested Part objects."""
|
||||
parts = []
|
||||
|
||||
params = self.request.query_params
|
||||
@ -491,9 +436,7 @@ class PartLabelMixin:
|
||||
|
||||
|
||||
class PartLabelList(LabelListView, PartLabelMixin):
|
||||
"""
|
||||
API endpoint for viewing list of PartLabel objects
|
||||
"""
|
||||
"""API endpoint for viewing list of PartLabel objects."""
|
||||
|
||||
queryset = PartLabel.objects.all()
|
||||
serializer_class = PartLabelSerializer
|
||||
@ -539,27 +482,20 @@ class PartLabelList(LabelListView, PartLabelMixin):
|
||||
|
||||
|
||||
class PartLabelDetail(generics.RetrieveUpdateDestroyAPIView):
|
||||
"""
|
||||
API endpoint for a single PartLabel object
|
||||
"""
|
||||
"""API endpoint for a single PartLabel object."""
|
||||
|
||||
queryset = PartLabel.objects.all()
|
||||
serializer_class = PartLabelSerializer
|
||||
|
||||
|
||||
class PartLabelPrint(generics.RetrieveAPIView, PartLabelMixin, LabelPrintMixin):
|
||||
"""
|
||||
API endpoint for printing a PartLabel object
|
||||
"""
|
||||
"""API endpoint for printing a PartLabel object."""
|
||||
|
||||
queryset = PartLabel.objects.all()
|
||||
serializer_class = PartLabelSerializer
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
"""
|
||||
Check if valid part(s) have been provided
|
||||
"""
|
||||
|
||||
"""Check if valid part(s) have been provided."""
|
||||
parts = self.get_parts()
|
||||
|
||||
return self.print(request, parts)
|
||||
|
@ -1,4 +1,5 @@
|
||||
"""Functions to print a label to a mixin printer"""
|
||||
"""Functions to print a label to a mixin printer."""
|
||||
|
||||
import logging
|
||||
import sys
|
||||
import traceback
|
||||
@ -16,20 +17,19 @@ from plugin.registry import registry
|
||||
logger = logging.getLogger('inventree')
|
||||
|
||||
|
||||
def print_label(plugin_slug, pdf_data, filename=None, label_instance=None, user=None):
|
||||
"""
|
||||
Print label with the provided plugin.
|
||||
def print_label(plugin_slug: str, pdf_data, filename=None, label_instance=None, user=None):
|
||||
"""Print label with the provided plugin.
|
||||
|
||||
This task is nominally handled by the background worker.
|
||||
|
||||
If the printing fails (throws an exception) then the user is notified.
|
||||
|
||||
Arguments:
|
||||
plugin_slug: The unique slug (key) of the plugin
|
||||
pdf_data: Binary PDF data
|
||||
filename: The intended name of the printed label
|
||||
Args:
|
||||
plugin_slug (str): The unique slug (key) of the plugin.
|
||||
pdf_data: Binary PDF data.
|
||||
filename: The intended name of the printed label. Defaults to None.
|
||||
label_instance (Union[LabelTemplate, None], optional): The template instance that should be printed. Defaults to None.
|
||||
user (Union[User, None], optional): User that should be informed of errors. Defaults to None.
|
||||
"""
|
||||
|
||||
logger.info(f"Plugin '{plugin_slug}' is printing a label '{filename}'")
|
||||
|
||||
plugin = registry.plugins.get(plugin_slug, None)
|
||||
|
@ -1,11 +1,10 @@
|
||||
"""Plugin mixin classes for label plugins"""
|
||||
"""Plugin mixin classes for label plugins."""
|
||||
|
||||
from plugin.helpers import MixinNotImplementedError
|
||||
|
||||
|
||||
class LabelPrintingMixin:
|
||||
"""
|
||||
Mixin which enables direct printing of stock labels.
|
||||
"""Mixin which enables direct printing of stock labels.
|
||||
|
||||
Each plugin must provide a NAME attribute, which is used to uniquely identify the printer.
|
||||
|
||||
@ -13,9 +12,7 @@ class LabelPrintingMixin:
|
||||
"""
|
||||
|
||||
class MixinMeta:
|
||||
"""
|
||||
Meta options for this mixin
|
||||
"""
|
||||
"""Meta options for this mixin."""
|
||||
MIXIN_NAME = 'Label printing'
|
||||
|
||||
def __init__(self): # pragma: no cover
|
||||
@ -24,8 +21,7 @@ class LabelPrintingMixin:
|
||||
self.add_mixin('labels', True, __class__)
|
||||
|
||||
def print_label(self, **kwargs):
|
||||
"""
|
||||
Callback to print a single label
|
||||
"""Callback to print a single label.
|
||||
|
||||
kwargs:
|
||||
pdf_data: Raw PDF data of the rendered label
|
||||
@ -36,6 +32,5 @@ class LabelPrintingMixin:
|
||||
filename: The filename of this PDF label
|
||||
user: The user who printed this label
|
||||
"""
|
||||
|
||||
# Unimplemented (to be implemented by the particular plugin class)
|
||||
raise MixinNotImplementedError('This Plugin must implement a `print_label` method')
|
||||
|
@ -1,4 +1,5 @@
|
||||
"""Unit tests for the label printing mixin"""
|
||||
"""Unit tests for the label printing mixin."""
|
||||
|
||||
import os
|
||||
|
||||
from django.apps import apps
|
||||
@ -18,7 +19,7 @@ from stock.models import StockItem, StockLocation
|
||||
|
||||
|
||||
class LabelMixinTests(InvenTreeAPITestCase):
|
||||
"""Test that the Label mixin operates correctly"""
|
||||
"""Test that the Label mixin operates correctly."""
|
||||
|
||||
fixtures = [
|
||||
'category',
|
||||
@ -30,14 +31,13 @@ class LabelMixinTests(InvenTreeAPITestCase):
|
||||
roles = 'all'
|
||||
|
||||
def do_activate_plugin(self):
|
||||
"""Activate the 'samplelabel' plugin"""
|
||||
|
||||
"""Activate the 'samplelabel' plugin."""
|
||||
config = registry.get_plugin('samplelabel').plugin_config()
|
||||
config.active = True
|
||||
config.save()
|
||||
|
||||
def do_url(self, parts, plugin_ref, label, url_name: str = 'api-part-label-print', url_single: str = 'part', invalid: bool = False):
|
||||
"""Generate an URL to print a label"""
|
||||
"""Generate an URL to print a label."""
|
||||
# Construct URL
|
||||
kwargs = {}
|
||||
if label:
|
||||
@ -64,7 +64,7 @@ class LabelMixinTests(InvenTreeAPITestCase):
|
||||
return url
|
||||
|
||||
def test_wrong_implementation(self):
|
||||
"""Test that a wrong implementation raises an error"""
|
||||
"""Test that a wrong implementation raises an error."""
|
||||
|
||||
class WrongPlugin(LabelPrintingMixin, InvenTreePlugin):
|
||||
pass
|
||||
@ -74,8 +74,7 @@ class LabelMixinTests(InvenTreeAPITestCase):
|
||||
plugin.print_label(filename='test')
|
||||
|
||||
def test_installed(self):
|
||||
"""Test that the sample printing plugin is installed"""
|
||||
|
||||
"""Test that the sample printing plugin is installed."""
|
||||
# Get all label plugins
|
||||
plugins = registry.with_mixin('labels')
|
||||
self.assertEqual(len(plugins), 1)
|
||||
@ -85,8 +84,7 @@ class LabelMixinTests(InvenTreeAPITestCase):
|
||||
self.assertEqual(len(plugins), 0)
|
||||
|
||||
def test_api(self):
|
||||
"""Test that we can filter the API endpoint by mixin"""
|
||||
|
||||
"""Test that we can filter the API endpoint by mixin."""
|
||||
url = reverse('api-plugin-list')
|
||||
|
||||
# Try POST (disallowed)
|
||||
@ -130,8 +128,7 @@ class LabelMixinTests(InvenTreeAPITestCase):
|
||||
self.assertEqual(data['key'], 'samplelabel')
|
||||
|
||||
def test_printing_process(self):
|
||||
"""Test that a label can be printed"""
|
||||
|
||||
"""Test that a label can be printed."""
|
||||
# Ensure the labels were created
|
||||
apps.get_app_config('label').create_labels()
|
||||
|
||||
@ -194,13 +191,13 @@ class LabelMixinTests(InvenTreeAPITestCase):
|
||||
self.do_activate_plugin()
|
||||
|
||||
def run_print_test(label, qs, url_name, url_single):
|
||||
"""Run tests on single and multiple page printing
|
||||
"""Run tests on single and multiple page printing.
|
||||
|
||||
Args:
|
||||
label (_type_): class of the label
|
||||
qs (_type_): class of the base queryset
|
||||
url_name (_type_): url for endpoints
|
||||
url_single (_type_): item lookup reference
|
||||
label: class of the label
|
||||
qs: class of the base queryset
|
||||
url_name: url for endpoints
|
||||
url_single: item lookup reference
|
||||
"""
|
||||
label = label.objects.first()
|
||||
qs = qs.objects.all()
|
||||
|
@ -1,5 +1,4 @@
|
||||
"""
|
||||
Registry for loading and managing multiple plugins at run-time
|
||||
"""Registry for loading and managing multiple plugins at run-time.
|
||||
|
||||
- Holds the class and the object that contains all code to maintain plugin states
|
||||
- Manages setup and teardown of plugin class instances
|
||||
@ -31,9 +30,7 @@ logger = logging.getLogger('inventree')
|
||||
|
||||
|
||||
class PluginsRegistry:
|
||||
"""
|
||||
The PluginsRegistry class
|
||||
"""
|
||||
"""The PluginsRegistry class."""
|
||||
|
||||
def __init__(self) -> None:
|
||||
"""Initialize registry.
|
||||
@ -59,10 +56,7 @@ class PluginsRegistry:
|
||||
self.mixins_settings = {}
|
||||
|
||||
def get_plugin(self, slug):
|
||||
"""
|
||||
Lookup plugin by slug (unique key).
|
||||
"""
|
||||
|
||||
"""Lookup plugin by slug (unique key)."""
|
||||
if slug not in self.plugins:
|
||||
logger.warning(f"Plugin registry has no record of plugin '{slug}'")
|
||||
return None
|
||||
@ -70,15 +64,13 @@ class PluginsRegistry:
|
||||
return self.plugins[slug]
|
||||
|
||||
def call_plugin_function(self, slug, func, *args, **kwargs):
|
||||
"""
|
||||
Call a member function (named by 'func') of the plugin named by 'slug'.
|
||||
"""Call a member function (named by 'func') of the plugin named by 'slug'.
|
||||
|
||||
As this is intended to be run by the background worker,
|
||||
we do not perform any try/except here.
|
||||
|
||||
Instead, any error messages are returned to the worker.
|
||||
"""
|
||||
|
||||
plugin = self.get_plugin(slug)
|
||||
|
||||
if not plugin:
|
||||
@ -91,7 +83,7 @@ class PluginsRegistry:
|
||||
# region public functions
|
||||
# region loading / unloading
|
||||
def load_plugins(self, full_reload: bool = False):
|
||||
"""Load and activate all IntegrationPlugins
|
||||
"""Load and activate all IntegrationPlugins.
|
||||
|
||||
Args:
|
||||
full_reload (bool, optional): Reload everything - including plugin mechanism. Defaults to False.
|
||||
@ -154,10 +146,7 @@ class PluginsRegistry:
|
||||
logger.info('Finished loading plugins')
|
||||
|
||||
def unload_plugins(self):
|
||||
"""
|
||||
Unload and deactivate all IntegrationPlugins
|
||||
"""
|
||||
|
||||
"""Unload and deactivate all IntegrationPlugins."""
|
||||
if not settings.PLUGINS_ENABLED:
|
||||
# Plugins not enabled, do nothing
|
||||
return # pragma: no cover
|
||||
@ -181,12 +170,11 @@ class PluginsRegistry:
|
||||
logger.info('Finished unloading plugins')
|
||||
|
||||
def reload_plugins(self, full_reload: bool = False):
|
||||
"""Safely reload IntegrationPlugins
|
||||
"""Safely reload.
|
||||
|
||||
Args:
|
||||
full_reload (bool, optional): Reload everything - including plugin mechanism. Defaults to False.
|
||||
"""
|
||||
|
||||
# Do not reload whe currently loading
|
||||
if self.is_loading:
|
||||
return # pragma: no cover
|
||||
@ -200,8 +188,7 @@ class PluginsRegistry:
|
||||
logger.info('Finished reloading plugins')
|
||||
|
||||
def collect_plugins(self):
|
||||
"""Collect plugins from all possible ways of loading"""
|
||||
|
||||
"""Collect plugins from all possible ways of loading."""
|
||||
if not settings.PLUGINS_ENABLED:
|
||||
# Plugins not enabled, do nothing
|
||||
return # pragma: no cover
|
||||
@ -230,10 +217,7 @@ class PluginsRegistry:
|
||||
logger.info(", ".join([a.__module__ for a in self.plugin_modules]))
|
||||
|
||||
def install_plugin_file(self):
|
||||
"""
|
||||
Make sure all plugins are installed in the current enviroment
|
||||
"""
|
||||
|
||||
"""Make sure all plugins are installed in the current enviroment."""
|
||||
if settings.PLUGIN_FILE_CHECKED:
|
||||
logger.info('Plugin file was already checked')
|
||||
return True
|
||||
@ -254,9 +238,7 @@ class PluginsRegistry:
|
||||
|
||||
# region registry functions
|
||||
def with_mixin(self, mixin: str, active=None):
|
||||
"""
|
||||
Returns reference to all plugins that have a specified mixin enabled
|
||||
"""
|
||||
"""Returns reference to all plugins that have a specified mixin enabled."""
|
||||
result = []
|
||||
|
||||
for plugin in self.plugins.values():
|
||||
@ -277,14 +259,12 @@ class PluginsRegistry:
|
||||
|
||||
# region general internal loading /activating / deactivating / deloading
|
||||
def _init_plugins(self, disabled=None):
|
||||
"""
|
||||
Initialise all found plugins
|
||||
"""Initialise all found plugins.
|
||||
|
||||
:param disabled: loading path of disabled app, defaults to None
|
||||
:type disabled: str, optional
|
||||
:raises error: IntegrationPluginError
|
||||
"""
|
||||
|
||||
from plugin.models import PluginConfig
|
||||
|
||||
logger.info('Starting plugin initialisation')
|
||||
@ -348,7 +328,7 @@ class PluginsRegistry:
|
||||
self.plugins_inactive[plug_key] = plugin_db_setting # pragma: no cover
|
||||
|
||||
def _activate_plugins(self, force_reload=False, full_reload: bool = False):
|
||||
"""Run activation functions for all plugins
|
||||
"""Run activation functions for all plugins.
|
||||
|
||||
Args:
|
||||
force_reload (bool, optional): Also reload base apps. Defaults to False.
|
||||
@ -363,8 +343,7 @@ class PluginsRegistry:
|
||||
self.activate_plugin_app(plugins, force_reload=force_reload, full_reload=full_reload)
|
||||
|
||||
def _deactivate_plugins(self):
|
||||
"""Run deactivation functions for all plugins"""
|
||||
|
||||
"""Run deactivation functions for all plugins."""
|
||||
self.deactivate_plugin_app()
|
||||
self.deactivate_plugin_schedule()
|
||||
self.deactivate_plugin_settings()
|
||||
@ -437,21 +416,20 @@ class PluginsRegistry:
|
||||
logger.warning("activate_integration_schedule failed, database not ready")
|
||||
|
||||
def deactivate_plugin_schedule(self):
|
||||
"""
|
||||
Deactivate ScheduleMixin
|
||||
currently nothing is done
|
||||
"""Deactivate ScheduleMixin.
|
||||
|
||||
Currently nothing is done here.
|
||||
"""
|
||||
pass
|
||||
|
||||
def activate_plugin_app(self, plugins, force_reload=False, full_reload: bool = False):
|
||||
"""Activate AppMixin plugins - add custom apps and reload
|
||||
"""Activate AppMixin plugins - add custom apps and reload.
|
||||
|
||||
Args:
|
||||
plugins (dict): List of IntegrationPlugins that should be installed
|
||||
force_reload (bool, optional): Only reload base apps. Defaults to False.
|
||||
full_reload (bool, optional): Reload everything - including plugin mechanism. Defaults to False.
|
||||
"""
|
||||
|
||||
from common.models import InvenTreeSetting
|
||||
|
||||
if settings.PLUGIN_TESTING or InvenTreeSetting.get_setting('ENABLE_PLUGINS_APP'):
|
||||
@ -516,8 +494,9 @@ class PluginsRegistry:
|
||||
reload(app_config.module.admin)
|
||||
|
||||
def _get_plugin_path(self, plugin):
|
||||
"""parse plugin path
|
||||
the input can be eiter:
|
||||
"""Parse plugin path.
|
||||
|
||||
The input can be eiter:
|
||||
- a local file / dir
|
||||
- a package
|
||||
"""
|
||||
@ -601,13 +580,12 @@ class PluginsRegistry:
|
||||
clear_url_caches()
|
||||
|
||||
def _reload_apps(self, force_reload: bool = False, full_reload: bool = False):
|
||||
"""Internal: reload apps using django internal functions
|
||||
"""Internal: reload apps using django internal functions.
|
||||
|
||||
Args:
|
||||
force_reload (bool, optional): Also reload base apps. Defaults to False.
|
||||
full_reload (bool, optional): Reload everything - including plugin mechanism. Defaults to False.
|
||||
"""
|
||||
|
||||
# If full_reloading is set to true we do not want to set the flag
|
||||
if not full_reload:
|
||||
self.is_loading = True # set flag to disable loop reloading
|
||||
@ -622,9 +600,9 @@ class PluginsRegistry:
|
||||
self.is_loading = False
|
||||
|
||||
def _try_reload(self, cmd, *args, **kwargs):
|
||||
"""
|
||||
wrapper to try reloading the apps
|
||||
throws an custom error that gets handled by the loading function
|
||||
"""Wrapper to try reloading the apps.
|
||||
|
||||
Throws an custom error that gets handled by the loading function.
|
||||
"""
|
||||
try:
|
||||
cmd(*args, **kwargs)
|
||||
@ -638,5 +616,5 @@ registry = PluginsRegistry()
|
||||
|
||||
|
||||
def call_function(plugin_name, function_name, *args, **kwargs):
|
||||
""" Global helper function to call a specific member function of a plugin """
|
||||
"""Global helper function to call a specific member function of a plugin."""
|
||||
return registry.call_plugin_function(plugin_name, function_name, *args, **kwargs)
|
||||
|
Loading…
Reference in New Issue
Block a user