2
0
mirror of https://github.com/inventree/InvenTree.git synced 2025-06-14 11:05:41 +00:00

fix docstrings 4

This commit is contained in:
Matthias
2022-05-28 02:43:33 +02:00
parent cb6fd63343
commit 61287dba2b
29 changed files with 134 additions and 361 deletions

View File

@ -4,8 +4,7 @@ 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,8 @@ class LabelPrintingMixin:
"""
class MixinMeta:
"""
Meta options for this mixin
"""
"""Meta options for this mixin"""
MIXIN_NAME = 'Label printing'
def __init__(self): # pragma: no cover
@ -23,8 +21,7 @@ class LabelPrintingMixin:
self.add_mixin('labels', True, __class__)
def print_label(self, label, **kwargs):
"""
Callback to print a single label
"""Callback to print a single label
Arguments:
label: A black-and-white pillow Image object
@ -32,8 +29,6 @@ class LabelPrintingMixin:
kwargs:
length: The length of the label (in mm)
width: The width of the label (in mm)
"""
# Unimplemented (to be implemented by the particular plugin class)
raise MixinNotImplementedError('This Plugin must implement a `print_label` method')

View File

@ -28,7 +28,6 @@ class LabelMixinTests(InvenTreeAPITestCase):
def do_activate_plugin(self):
"""Activate the 'samplelabel' plugin"""
config = registry.get_plugin('samplelabel').plugin_config()
config.active = True
config.save()
@ -62,7 +61,6 @@ class LabelMixinTests(InvenTreeAPITestCase):
def test_wrong_implementation(self):
"""Test that a wrong implementation raises an error"""
class WrongPlugin(LabelPrintingMixin, InvenTreePlugin):
pass
@ -72,7 +70,6 @@ class LabelMixinTests(InvenTreeAPITestCase):
def test_installed(self):
"""Test that the sample printing plugin is installed"""
# Get all label plugins
plugins = registry.with_mixin('labels')
self.assertEqual(len(plugins), 1)
@ -83,7 +80,6 @@ class LabelMixinTests(InvenTreeAPITestCase):
def test_api(self):
"""Test that we can filter the API endpoint by mixin"""
url = reverse('api-plugin-list')
# Try POST (disallowed)
@ -128,7 +124,6 @@ class LabelMixinTests(InvenTreeAPITestCase):
def test_printing_process(self):
"""Test that a label can be printed"""
# Ensure the labels were created
apps.get_app_config('label').create_labels()

View File

@ -11,9 +11,7 @@ from stock.models import StockItem, StockLocation
class LocatePluginView(APIView):
"""
Endpoint for using a custom plugin to identify or 'locate' a stock item or location
"""
"""Endpoint for using a custom plugin to identify or 'locate' a stock item or location"""
permission_classes = [
permissions.IsAuthenticated,

View File

@ -8,8 +8,7 @@ logger = logging.getLogger('inventree')
class LocateMixin:
"""
Mixin class which provides support for 'locating' inventory items,
"""Mixin class which provides support for 'locating' inventory items,
for example identifying the location of a particular StockLocation.
Plugins could implement audible or visual cues to direct attention to the location,
@ -23,7 +22,6 @@ class LocateMixin:
- locate_stock_location : Used to locate / identify a particular stock location
Refer to the default method implementations below for more information!
"""
class MixinMeta:
@ -34,8 +32,7 @@ class LocateMixin:
self.add_mixin('locate', True, __class__)
def locate_stock_item(self, item_pk):
"""
Attempt to locate a particular StockItem
"""Attempt to locate a particular StockItem
Arguments:
item_pk: The PK (primary key) of the StockItem to be located
@ -63,8 +60,7 @@ class LocateMixin:
pass
def locate_stock_location(self, location_pk):
"""
Attempt to location a particular StockLocation
"""Attempt to location a particular StockLocation
Arguments:
location_pk: The PK (primary key) of the StockLocation to be located

View File

@ -1,6 +1,4 @@
"""
Unit tests for the 'locate' plugin mixin class
"""
"""Unit tests for the 'locate' plugin mixin class"""
from django.urls import reverse
@ -21,7 +19,6 @@ class LocatePluginTests(InvenTreeAPITestCase):
def test_installed(self):
"""Test that a locate plugin is actually installed"""
plugins = registry.with_mixin('locate')
self.assertTrue(len(plugins) > 0)
@ -30,7 +27,6 @@ class LocatePluginTests(InvenTreeAPITestCase):
def test_locate_fail(self):
"""Test various API failure modes"""
url = reverse('api-locate-plugin')
# Post without a plugin
@ -90,13 +86,11 @@ class LocatePluginTests(InvenTreeAPITestCase):
self.assertIn(f"StockLocation matching PK '{pk}' not found", str(response.data))
def test_locate_item(self):
"""
Test that the plugin correctly 'locates' a StockItem
"""Test that the plugin correctly 'locates' a StockItem
As the background worker is not running during unit testing,
the sample 'locate' function will be called 'inline'
"""
url = reverse('api-locate-plugin')
item = StockItem.objects.get(pk=1)
@ -121,10 +115,7 @@ class LocatePluginTests(InvenTreeAPITestCase):
self.assertTrue(item.metadata['located'])
def test_locate_location(self):
"""
Test that the plugin correctly 'locates' a StockLocation
"""
"""Test that the plugin correctly 'locates' a StockLocation"""
url = reverse('api-locate-plugin')
for location in StockLocation.objects.all():

View File

@ -1,14 +1,11 @@
# -*- coding: utf-8 -*-
"""sample implementation for ActionMixin"""
"""Sample implementation for ActionMixin"""
from plugin import InvenTreePlugin
from plugin.mixins import ActionMixin
class SimpleActionPlugin(ActionMixin, InvenTreePlugin):
"""
An EXTREMELY simple action plugin which demonstrates
the capability of the ActionMixin class
"""
"""An EXTREMELY simple action plugin which demonstrates the capability of the ActionMixin class"""
NAME = "SimpleActionPlugin"
ACTION_NAME = "simple"

View File

@ -1,11 +1,11 @@
""" Unit tests for action plugins """
"""Unit tests for action plugins"""
from InvenTree.helpers import InvenTreeTestCase
from plugin.builtin.action.simpleactionplugin import SimpleActionPlugin
class SimpleActionPluginTests(InvenTreeTestCase):
""" Tests for SampleIntegrationPlugin """
"""Tests for SampleIntegrationPlugin"""
def setUp(self):
super().setUp()
@ -13,12 +13,12 @@ class SimpleActionPluginTests(InvenTreeTestCase):
self.plugin = SimpleActionPlugin()
def test_name(self):
"""check plugn names """
"""Check plugn names"""
self.assertEqual(self.plugin.plugin_name(), "SimpleActionPlugin")
self.assertEqual(self.plugin.action_name(), "simple")
def test_function(self):
"""check if functions work """
"""Check if functions work"""
# test functions
response = self.client.post('/api/action/', data={'action': "simple", 'data': {'foo': "bar", }})
self.assertEqual(response.status_code, 200)

View File

@ -1,4 +1,3 @@
# -*- coding: utf-8 -*-
"""Unit tests for InvenTreeBarcodePlugin"""
from django.urls import reverse
@ -18,9 +17,7 @@ class TestInvenTreeBarcode(InvenTreeAPITestCase):
]
def test_errors(self):
"""
Test all possible error cases for assigment action
"""
"""Test all possible error cases for assigment action"""
def test_assert_error(barcode_data):
response = self.client.post(
@ -46,10 +43,7 @@ class TestInvenTreeBarcode(InvenTreeAPITestCase):
test_assert_error('{"blbla": 10004}')
def test_scan(self):
"""
Test that a barcode can be scanned
"""
"""Test that a barcode can be scanned"""
response = self.client.post(reverse('api-barcode-scan'), format='json', data={'barcode': 'blbla=10004'})
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertIn('success', response.data)

View File

@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
"""Core set of Notifications as a Plugin"""
from django.template.loader import render_to_string
from django.utils.translation import ugettext_lazy as _
@ -16,9 +16,7 @@ class PlgMixin:
class CoreNotificationsPlugin(SettingsMixin, InvenTreePlugin):
"""
Core notification methods for InvenTree
"""
"""Core notification methods for InvenTree"""
NAME = "CoreNotificationsPlugin"
AUTHOR = _('InvenTree contributors')
@ -50,11 +48,7 @@ class CoreNotificationsPlugin(SettingsMixin, InvenTreePlugin):
}
def get_targets(self):
"""
Return a list of target email addresses,
only for users which allow email notifications
"""
"""Return a list of target email addresses, only for users which allow email notifications"""
allowed_users = []
for user in self.targets:

View File

@ -8,10 +8,7 @@ from plugin.models import NotificationUserSetting
class CoreNotificationTestTests(BaseNotificationIntegrationTest):
def test_email(self):
"""
Ensure that the email notifications run
"""
"""Ensure that the email notifications run"""
# enable plugin and set mail setting to true
plugin = registry.plugins.get('corenotificationsplugin')
plugin.set_setting('ENABLE_NOTIFICATION_EMAILS', True)

View File

@ -1,6 +1,4 @@
"""
Utility class to enable simpler imports
"""
"""Utility class to enable simpler imports"""
from common.notifications import (BulkNotificationMethod,
SingleNotificationMethod)

View File

@ -1,6 +1,4 @@
"""
Sample plugin which responds to events
"""
"""Sample plugin which responds to events"""
import warnings
@ -11,16 +9,14 @@ from plugin.mixins import EventMixin
class EventPluginSample(EventMixin, InvenTreePlugin):
"""
A sample plugin which provides supports for triggered events
"""
"""A sample plugin which provides supports for triggered events"""
NAME = "EventPlugin"
SLUG = "sampleevent"
TITLE = "Triggered Events"
def process_event(self, event, *args, **kwargs):
""" Custom event processing """
"""Custom event processing"""
print(f"Processing triggered event: '{event}'")
print("args:", str(args))

View File

@ -31,7 +31,6 @@ class EventPluginSampleTests(TestCase):
def test_mixin(self):
"""Test that MixinNotImplementedError is raised"""
with self.assertRaises(MixinNotImplementedError):
class Wrong(EventMixin, InvenTreePlugin):
pass

View File

@ -1,19 +1,15 @@
"""sample implementation for IntegrationPlugin"""
"""Sample implementation for IntegrationPlugin"""
from plugin import InvenTreePlugin
from plugin.mixins import UrlsMixin
class NoIntegrationPlugin(InvenTreePlugin):
"""
An basic plugin
"""
"""A basic plugin"""
NAME = "NoIntegrationPlugin"
class WrongIntegrationPlugin(UrlsMixin, InvenTreePlugin):
"""
An basic wron plugin with urls
"""
"""A basic wron plugin with urls"""
NAME = "WrongIntegrationPlugin"

View File

@ -1,14 +1,11 @@
"""
Sample plugin for calling an external API
"""
"""Sample plugin for calling an external API"""
from plugin import InvenTreePlugin
from plugin.mixins import APICallMixin, SettingsMixin
class SampleApiCallerPlugin(APICallMixin, SettingsMixin, InvenTreePlugin):
"""
A small api call sample
"""
"""A small api call sample"""
NAME = "Sample API Caller"
SETTINGS = {
@ -26,7 +23,5 @@ class SampleApiCallerPlugin(APICallMixin, SettingsMixin, InvenTreePlugin):
API_TOKEN_SETTING = 'API_TOKEN'
def get_external_url(self):
"""
returns data from the sample endpoint
"""
"""Returns data from the sample endpoint"""
return self.api_call('api/users/2')

View File

@ -1,11 +1,10 @@
"""sample of a broken plugin"""
"""Sample of a broken plugin"""
from plugin import InvenTreePlugin
class BrokenIntegrationPlugin(InvenTreePlugin):
"""
An very broken plugin
"""
"""A very broken plugin"""
NAME = 'Test'
TITLE = 'Broken Plugin'
SLUG = 'broken'

View File

@ -1,6 +1,4 @@
"""
Sample plugin which renders custom panels on certain pages
"""
"""Sample plugin which renders custom panels on certain pages"""
from part.views import PartDetail
from plugin import InvenTreePlugin
@ -9,9 +7,7 @@ from stock.views import StockLocationDetail
class CustomPanelSample(PanelMixin, SettingsMixin, InvenTreePlugin):
"""
A sample plugin which renders some custom panels.
"""
"""A sample plugin which renders some custom panels."""
NAME = "CustomPanelExample"
SLUG = "samplepanel"
@ -45,9 +41,7 @@ class CustomPanelSample(PanelMixin, SettingsMixin, InvenTreePlugin):
return ctx
def get_custom_panels(self, view, request):
"""
You can decide at run-time which custom panels you want to display!
"""You can decide at run-time which custom panels you want to display!
- Display on every page
- Only on a single page or set of pages

View File

@ -4,9 +4,7 @@ from plugin.mixins import LabelPrintingMixin
class SampleLabelPrinter(LabelPrintingMixin, InvenTreePlugin):
"""
Sample plugin which provides a 'fake' label printer endpoint
"""
"""Sample plugin which provides a 'fake' label printer endpoint"""
NAME = "Label Printer"
SLUG = "samplelabel"

View File

@ -1,6 +1,4 @@
"""
Sample implementations for IntegrationPlugin
"""
"""Sample implementations for IntegrationPlugin"""
from django.http import HttpResponse
from django.urls import include, re_path
@ -11,9 +9,7 @@ from plugin.mixins import AppMixin, NavigationMixin, SettingsMixin, UrlsMixin
class SampleIntegrationPlugin(AppMixin, SettingsMixin, UrlsMixin, NavigationMixin, InvenTreePlugin):
"""
A full plugin example
"""
"""A full plugin example"""
NAME = "SampleIntegrationPlugin"
SLUG = "sample"
@ -23,7 +19,7 @@ class SampleIntegrationPlugin(AppMixin, SettingsMixin, UrlsMixin, NavigationMixi
NAVIGATION_TAB_ICON = 'fas fa-plus'
def view_test(self, request):
"""very basic view"""
"""Very basic view"""
return HttpResponse(f'Hi there {request.user.username} this works')
def setup_urls(self):

View File

@ -1,6 +1,4 @@
"""
Sample plugin which supports task scheduling
"""
"""Sample plugin which supports task scheduling"""
from plugin import InvenTreePlugin
from plugin.mixins import ScheduleMixin, SettingsMixin
@ -16,9 +14,7 @@ def print_world():
class ScheduledTaskPlugin(ScheduleMixin, SettingsMixin, InvenTreePlugin):
"""
A sample plugin which provides support for scheduled tasks
"""
"""A sample plugin which provides support for scheduled tasks"""
NAME = "ScheduledTasksPlugin"
SLUG = "schedule"
@ -51,10 +47,7 @@ class ScheduledTaskPlugin(ScheduleMixin, SettingsMixin, InvenTreePlugin):
}
def member_func(self, *args, **kwargs):
"""
A simple member function to demonstrate functionality
"""
"""A simple member function to demonstrate functionality"""
t_or_f = self.get_setting('T_OR_F')
print(f"Called member_func - value is {t_or_f}")

View File

@ -1,4 +1,4 @@
""" Unit tests for action caller sample"""
"""Unit tests for action caller sample"""
from django.test import TestCase
@ -6,10 +6,10 @@ from plugin import registry
class SampleApiCallerPluginTests(TestCase):
""" Tests for SampleApiCallerPluginTests """
"""Tests for SampleApiCallerPluginTests """
def test_return(self):
"""check if the external api call works"""
"""Check if the external api call works"""
# The plugin should be defined
self.assertIn('sample-api-caller', registry.plugins)
plg = registry.plugins['sample-api-caller']

View File

@ -1,13 +1,13 @@
""" Unit tests for action plugins """
"""Unit tests for action plugins"""
from InvenTree.helpers import InvenTreeTestCase
class SampleIntegrationPluginTests(InvenTreeTestCase):
""" Tests for SampleIntegrationPlugin """
"""Tests for SampleIntegrationPlugin"""
def test_view(self):
"""check the function of the custom sample plugin """
"""Check the function of the custom sample plugin"""
response = self.client.get('/plugin/sample/ho/he/')
self.assertEqual(response.status_code, 200)
self.assertEqual(response.content, b'Hi there testuser this works')

View File

@ -1,4 +1,4 @@
""" Unit tests for scheduled tasks"""
"""Unit tests for scheduled tasks"""
from django.test import TestCase
@ -9,10 +9,10 @@ from plugin.registry import call_function
class ExampleScheduledTaskPluginTests(TestCase):
""" Tests for provided ScheduledTaskPlugin """
"""Tests for provided ScheduledTaskPlugin"""
def test_function(self):
"""check if the scheduling works"""
"""Check if the scheduling works"""
# The plugin should be defined
self.assertIn('schedule', registry.plugins)
plg = registry.plugins['schedule']
@ -44,7 +44,7 @@ class ExampleScheduledTaskPluginTests(TestCase):
self.assertEqual(len(scheduled_plugin_tasks), 0)
def test_calling(self):
"""check if a function can be called without errors"""
"""Check if a function can be called without errors"""
# Check with right parameters
self.assertEqual(call_function('schedule', 'member_func'), False)
@ -53,7 +53,7 @@ class ExampleScheduledTaskPluginTests(TestCase):
class ScheduledTaskPluginTests(TestCase):
""" Tests for ScheduledTaskPluginTests mixin base """
"""Tests for ScheduledTaskPluginTests mixin base"""
def test_init(self):
"""Check that all MixinImplementationErrors raise"""
@ -68,8 +68,7 @@ class ScheduledTaskPluginTests(TestCase):
NoSchedules()
class WrongFuncSchedules(Base):
"""
Plugin with broken functions
"""Plugin with broken functions
This plugin is missing a func
"""
@ -88,8 +87,7 @@ class ScheduledTaskPluginTests(TestCase):
WrongFuncSchedules()
class WrongFuncSchedules1(WrongFuncSchedules):
"""
Plugin with broken functions
"""Plugin with broken functions
This plugin is missing a schedule
"""
@ -105,8 +103,7 @@ class ScheduledTaskPluginTests(TestCase):
WrongFuncSchedules1()
class WrongFuncSchedules2(WrongFuncSchedules):
"""
Plugin with broken functions
"""Plugin with broken functions
This plugin is missing a schedule
"""
@ -122,8 +119,7 @@ class ScheduledTaskPluginTests(TestCase):
WrongFuncSchedules2()
class WrongFuncSchedules3(WrongFuncSchedules):
"""
Plugin with broken functions
"""Plugin with broken functions
This plugin has a broken schedule
"""
@ -140,8 +136,7 @@ class ScheduledTaskPluginTests(TestCase):
WrongFuncSchedules3()
class WrongFuncSchedules4(WrongFuncSchedules):
"""
Plugin with broken functions
"""Plugin with broken functions
This plugin is missing a minute marker for its schedule
"""

View File

@ -13,8 +13,8 @@ logger = logging.getLogger('inventree')
class SampleLocatePlugin(LocateMixin, InvenTreePlugin):
"""
A very simple example of the 'locate' plugin.
"""A very simple example of the 'locate' plugin.
This plugin class simply prints location information to the logger.
"""

View File

@ -51,7 +51,6 @@ class SampleLocatePlugintests(InvenTreeAPITestCase):
def test_mixin(self):
"""Test that MixinNotImplementedError is raised"""
with self.assertRaises(MixinNotImplementedError):
class Wrong(LocateMixin, InvenTreePlugin):
pass

View File

@ -1,7 +1,5 @@
# -*- coding: utf-8 -*-
"""This module provides template tags for handeling plugins"""
""" This module provides template tags for handeling plugins
"""
from django import template
from django.conf import settings as djangosettings
from django.urls import reverse
@ -15,49 +13,37 @@ register = template.Library()
@register.simple_tag()
def plugin_list(*args, **kwargs):
"""
List of all installed plugins
"""
"""List of all installed plugins"""
return registry.plugins
@register.simple_tag()
def inactive_plugin_list(*args, **kwargs):
"""
List of all inactive plugins
"""
"""List of all inactive plugins"""
return registry.plugins_inactive
@register.simple_tag()
def plugin_settings(plugin, *args, **kwargs):
"""
List of all settings for the plugin
"""
"""List of all settings for the plugin"""
return registry.mixins_settings.get(plugin)
@register.simple_tag()
def mixin_enabled(plugin, key, *args, **kwargs):
"""
Is the mixin registerd and configured in the plugin?
"""
"""Is the mixin registerd and configured in the plugin?"""
return plugin.mixin_enabled(key)
@register.simple_tag()
def mixin_available(mixin, *args, **kwargs):
"""
Returns True if there is at least one active plugin which supports the provided mixin
"""
"""Returns True if there is at least one active plugin which supports the provided mixin"""
return len(registry.with_mixin(mixin)) > 0
@register.simple_tag()
def navigation_enabled(*args, **kwargs):
"""
Is plugin navigation enabled?
"""
"""Is plugin navigation enabled?"""
if djangosettings.PLUGIN_TESTING:
return True
return InvenTreeSetting.get_setting('ENABLE_PLUGINS_NAVIGATION') # pragma: no cover
@ -65,8 +51,8 @@ def navigation_enabled(*args, **kwargs):
@register.simple_tag()
def safe_url(view_name, *args, **kwargs):
"""
Safe lookup fnc for URLs
"""Safe lookup fnc for URLs
Returns None if not found
"""
try:
@ -77,15 +63,11 @@ def safe_url(view_name, *args, **kwargs):
@register.simple_tag()
def plugin_errors(*args, **kwargs):
"""
All plugin errors in the current session
"""
"""All plugin errors in the current session"""
return registry.errors
@register.simple_tag(takes_context=True)
def notification_settings_list(context, *args, **kwargs):
"""
List of all user notification settings
"""
"""List of all user notification settings"""
return storage.get_usersettings(user=context.get('user', None))

View File

@ -24,9 +24,7 @@ from .serializers import (BOMReportSerializer, BuildReportSerializer,
class ReportListView(generics.ListAPIView):
"""
Generic API class for report templates
"""
"""Generic API class for report templates"""
filter_backends = [
DjangoFilterBackend,
@ -44,15 +42,10 @@ class ReportListView(generics.ListAPIView):
class StockItemReportMixin:
"""
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
@ -77,15 +70,10 @@ class StockItemReportMixin:
class BuildReportMixin:
"""
Mixin for extracting Build items from query params
"""
"""Mixin for extracting Build items from query params"""
def get_builds(self):
"""
Return a list of requested Build objects
"""
"""Return a list of requested Build objects"""
builds = []
params = self.request.query_params
@ -109,17 +97,13 @@ class BuildReportMixin:
class OrderReportMixin:
"""
Mixin for extracting order items from query params
"""Mixin for extracting order items from query params
requires the OrderModel class attribute to be set!
"""
def get_orders(self):
"""
Return a list of order objects
"""
"""Return a list of order objects"""
orders = []
params = self.request.query_params
@ -143,15 +127,10 @@ class OrderReportMixin:
class PartReportMixin:
"""
Mixin for extracting part items from query params
"""
"""Mixin for extracting part items from query params"""
def get_parts(self):
"""
Return a list of requested part objects
"""
"""Return a list of requested part objects"""
parts = []
params = self.request.query_params
@ -176,15 +155,10 @@ class PartReportMixin:
class ReportPrintMixin:
"""
Mixin for printing reports
"""
"""Mixin for printing reports"""
def print(self, request, items_to_print):
"""
Print this report template against a number of pre-validated items.
"""
"""Print this report template against a number of pre-validated items."""
if len(items_to_print) == 0:
# No valid items provided, return an error message
data = {
@ -283,8 +257,7 @@ class ReportPrintMixin:
class StockItemTestReportList(ReportListView, StockItemReportMixin):
"""
API endpoint for viewing list of TestReport objects.
"""API endpoint for viewing list of TestReport objects.
Filterable by:
@ -347,35 +320,27 @@ class StockItemTestReportList(ReportListView, StockItemReportMixin):
class StockItemTestReportDetail(generics.RetrieveUpdateDestroyAPIView):
"""
API endpoint for a single TestReport object
"""
"""API endpoint for a single TestReport object"""
queryset = TestReport.objects.all()
serializer_class = TestReportSerializer
class StockItemTestReportPrint(generics.RetrieveAPIView, StockItemReportMixin, ReportPrintMixin):
"""
API endpoint for printing a TestReport object
"""
"""API endpoint for printing a TestReport object"""
queryset = TestReport.objects.all()
serializer_class = TestReportSerializer
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 BOMReportList(ReportListView, PartReportMixin):
"""
API endpoint for viewing a list of BillOfMaterialReport objects.
"""API endpoint for viewing a list of BillOfMaterialReport objects.
Filterably by:
@ -436,35 +401,27 @@ class BOMReportList(ReportListView, PartReportMixin):
class BOMReportDetail(generics.RetrieveUpdateDestroyAPIView):
"""
API endpoint for a single BillOfMaterialReport object
"""
"""API endpoint for a single BillOfMaterialReport object"""
queryset = BillOfMaterialsReport.objects.all()
serializer_class = BOMReportSerializer
class BOMReportPrint(generics.RetrieveAPIView, PartReportMixin, ReportPrintMixin):
"""
API endpoint for printing a BillOfMaterialReport object
"""
"""API endpoint for printing a BillOfMaterialReport object"""
queryset = BillOfMaterialsReport.objects.all()
serializer_class = BOMReportSerializer
def get(self, request, *args, **kwargs):
"""
Check if valid part item(s) have been provided
"""
"""Check if valid part item(s) have been provided"""
parts = self.get_parts()
return self.print(request, parts)
class BuildReportList(ReportListView, BuildReportMixin):
"""
API endpoint for viewing a list of BuildReport objects.
"""API endpoint for viewing a list of BuildReport objects.
Can be filtered by:
@ -526,18 +483,14 @@ class BuildReportList(ReportListView, BuildReportMixin):
class BuildReportDetail(generics.RetrieveUpdateDestroyAPIView):
"""
API endpoint for a single BuildReport object
"""
"""API endpoint for a single BuildReport object"""
queryset = BuildReport.objects.all()
serializer_class = BuildReportSerializer
class BuildReportPrint(generics.RetrieveAPIView, BuildReportMixin, ReportPrintMixin):
"""
API endpoint for printing a BuildReport
"""
"""API endpoint for printing a BuildReport"""
queryset = BuildReport.objects.all()
serializer_class = BuildReportSerializer
@ -607,18 +560,14 @@ class PurchaseOrderReportList(ReportListView, OrderReportMixin):
class PurchaseOrderReportDetail(generics.RetrieveUpdateDestroyAPIView):
"""
API endpoint for a single PurchaseOrderReport object
"""
"""API endpoint for a single PurchaseOrderReport object"""
queryset = PurchaseOrderReport.objects.all()
serializer_class = PurchaseOrderReportSerializer
class PurchaseOrderReportPrint(generics.RetrieveAPIView, OrderReportMixin, ReportPrintMixin):
"""
API endpoint for printing a PurchaseOrderReport object
"""
"""API endpoint for printing a PurchaseOrderReport object"""
OrderModel = order.models.PurchaseOrder
@ -690,18 +639,14 @@ class SalesOrderReportList(ReportListView, OrderReportMixin):
class SalesOrderReportDetail(generics.RetrieveUpdateDestroyAPIView):
"""
API endpoint for a single SalesOrderReport object
"""
"""API endpoint for a single SalesOrderReport object"""
queryset = SalesOrderReport.objects.all()
serializer_class = SalesOrderReportSerializer
class SalesOrderReportPrint(generics.RetrieveAPIView, OrderReportMixin, ReportPrintMixin):
"""
API endpoint for printing a PurchaseOrderReport object
"""
"""API endpoint for printing a PurchaseOrderReport object"""
OrderModel = order.models.SalesOrder

View File

@ -14,19 +14,13 @@ class ReportConfig(AppConfig):
name = 'report'
def ready(self):
"""
This function is called whenever the report app is loaded
"""
"""This function is called whenever the report app is loaded"""
if canAppAccessDatabase(allow_test=True):
self.create_default_test_reports()
self.create_default_build_reports()
def create_default_reports(self, model, reports):
"""
Copy defualt report files across to the media directory.
"""
"""Copy defualt report files across to the media directory."""
# Source directory for report templates
src_dir = os.path.join(
os.path.dirname(os.path.realpath(__file__)),
@ -82,11 +76,7 @@ class ReportConfig(AppConfig):
pass
def create_default_test_reports(self):
"""
Create database entries for the default TestReport templates,
if they do not already exist
"""
"""Create database entries for the default TestReport templates, if they do not already exist"""
try:
from .models import TestReport
except: # pragma: no cover
@ -105,11 +95,7 @@ class ReportConfig(AppConfig):
self.create_default_reports(TestReport, reports)
def create_default_build_reports(self):
"""
Create database entries for the default BuildReport templates
(if they do not already exist)
"""
"""Create database entries for the default BuildReport templates (if they do not already exist)"""
try:
from .models import BuildReport
except: # pragma: no cover

View File

@ -1,6 +1,4 @@
"""
Report template model definitions
"""
"""Report template model definitions"""
import datetime
import logging
@ -36,59 +34,41 @@ logger = logging.getLogger("inventree")
def rename_template(instance, filename):
"""
Helper function for 'renaming' uploaded report files.
"""Helper function for 'renaming' uploaded report files.
Pass responsibility back to the calling class,
to ensure that files are uploaded to the correct directory.
"""
return instance.rename_file(filename)
def validate_stock_item_report_filters(filters):
"""
Validate filter string against StockItem model
"""
"""Validate filter string against StockItem model"""
return validateFilterString(filters, model=stock.models.StockItem)
def validate_part_report_filters(filters):
"""
Validate filter string against Part model
"""
"""Validate filter string against Part model"""
return validateFilterString(filters, model=part.models.Part)
def validate_build_report_filters(filters):
"""
Validate filter string against Build model
"""
"""Validate filter string against Build model"""
return validateFilterString(filters, model=build.models.Build)
def validate_purchase_order_filters(filters):
"""
Validate filter string against PurchaseOrder model
"""
"""Validate filter string against PurchaseOrder model"""
return validateFilterString(filters, model=order.models.PurchaseOrder)
def validate_sales_order_filters(filters):
"""
Validate filter string against SalesOrder model
"""
"""Validate filter string against SalesOrder model"""
return validateFilterString(filters, model=order.models.SalesOrder)
class WeasyprintReportMixin(WeasyTemplateResponseMixin):
"""
Class for rendering a HTML template to a PDF.
"""
"""Class for rendering a HTML template to a PDF."""
pdf_filename = 'report.pdf'
pdf_attachment = True
@ -101,9 +81,7 @@ class WeasyprintReportMixin(WeasyTemplateResponseMixin):
class ReportBase(models.Model):
"""
Base class for uploading html templates
"""
"""Base class for uploading html templates"""
class Meta:
abstract = True
@ -151,11 +129,10 @@ class ReportBase(models.Model):
@property
def template_name(self):
"""
Returns the file system path to the template file.
"""Returns the file system path to the template file.
Required for passing the file to an external process
"""
template = self.template.name
template = template.replace('/', os.path.sep)
template = template.replace('\\', os.path.sep)
@ -192,28 +169,20 @@ class ReportBase(models.Model):
class ReportTemplateBase(ReportBase):
"""
Reporting template model.
"""Reporting template model.
Able to be passed context data
"""
# Pass a single top-level object to the report template
object_to_print = None
def get_context_data(self, request):
"""
Supply context data to the template for rendering
"""
"""Supply context data to the template for rendering"""
return {}
def context(self, request):
"""
All context to be passed to the renderer.
"""
"""All context to be passed to the renderer."""
# Generate custom context data based on the particular report subclass
context = self.get_context_data(request)
@ -230,10 +199,7 @@ class ReportTemplateBase(ReportBase):
return context
def generate_filename(self, request, **kwargs):
"""
Generate a filename for this report
"""
"""Generate a filename for this report"""
template_string = Template(self.filename_pattern)
ctx = self.context(request)
@ -243,21 +209,17 @@ class ReportTemplateBase(ReportBase):
return template_string.render(context)
def render_as_string(self, request, **kwargs):
"""
Render the report to a HTML string.
"""Render the report to a HTML string.
Useful for debug mode (viewing generated code)
"""
return render_to_string(self.template_name, self.context(request), request)
def render(self, request, **kwargs):
"""
Render the template to a PDF file.
"""Render the template to a PDF file.
Uses django-weasyprint plugin to render HTML template against Weasyprint
"""
# TODO: Support custom filename generation!
# filename = kwargs.get('filename', 'report.pdf')
@ -292,9 +254,7 @@ class ReportTemplateBase(ReportBase):
class TestReport(ReportTemplateBase):
"""
Render a TestReport against a StockItem object.
"""
"""Render a TestReport against a StockItem object."""
@staticmethod
def get_api_url():
@ -321,10 +281,7 @@ class TestReport(ReportTemplateBase):
)
def matches_stock_item(self, item):
"""
Test if this report template matches a given StockItem objects
"""
"""Test if this report template matches a given StockItem objects"""
try:
filters = validateFilterString(self.filters)
items = stock.models.StockItem.objects.filter(**filters)
@ -352,9 +309,7 @@ class TestReport(ReportTemplateBase):
class BuildReport(ReportTemplateBase):
"""
Build order / work order report
"""
"""Build order / work order report"""
@staticmethod
def get_api_url():
@ -375,10 +330,7 @@ class BuildReport(ReportTemplateBase):
)
def get_context_data(self, request):
"""
Custom context data for the build report
"""
"""Custom context data for the build report"""
my_build = self.object_to_print
if type(my_build) != build.models.Build:
@ -395,9 +347,7 @@ class BuildReport(ReportTemplateBase):
class BillOfMaterialsReport(ReportTemplateBase):
"""
Render a Bill of Materials against a Part object
"""
"""Render a Bill of Materials against a Part object"""
@staticmethod
def get_api_url():
@ -429,9 +379,7 @@ class BillOfMaterialsReport(ReportTemplateBase):
class PurchaseOrderReport(ReportTemplateBase):
"""
Render a report against a PurchaseOrder object
"""
"""Render a report against a PurchaseOrder object"""
@staticmethod
def get_api_url():
@ -468,9 +416,7 @@ class PurchaseOrderReport(ReportTemplateBase):
class SalesOrderReport(ReportTemplateBase):
"""
Render a report against a SalesOrder object
"""
"""Render a report against a SalesOrder object"""
@staticmethod
def get_api_url():
@ -530,9 +476,7 @@ def rename_snippet(instance, filename):
class ReportSnippet(models.Model):
"""
Report template 'snippet' which can be used to make templates
that can then be included in other reports.
"""Report template 'snippet' which can be used to make templates that can then be included in other reports.
Useful for 'common' template actions, sub-templates, etc
"""
@ -568,6 +512,7 @@ def rename_asset(instance, filename):
class ReportAsset(models.Model):
"""Asset file for use in report templates.
For example, an image to use in a header file.
Uploaded asset files appear in MEDIA_ROOT/report/assets,
and can be loaded in a template using the {% report_asset <filename> %} tag.