2
0
mirror of https://github.com/inventree/InvenTree.git synced 2025-07-31 17:11:34 +00:00

Docstring checks in QC checks ()

* Add pre-commit to the stack

* exclude static

* Add locales to excludes

* fix style errors

* rename pipeline steps

* also wait on precommit

* make template matching simpler

* Use the same code for python setup everywhere

* use step and cache for python setup

* move regular settings up into general envs

* just use full update

* Use invoke instead of static references

* make setup actions more similar

* use python3

* refactor names to be similar

* fix runner version

* fix references

* remove incidential change

* use matrix for os

* Github can't do this right now

* ignore docstyle errors

* Add seperate docstring test

* update flake call

* do not fail on docstring

* refactor setup into workflow

* update reference

* switch to action

* resturcture

* add bash statements

* remove os from cache

* update input checks

* make code cleaner

* fix boolean

* no relative paths

* install wheel by python

* switch to install

* revert back to simple wheel

* refactor import export tests

* move setup keys back to not disturbe tests

* remove docstyle till that is fixed

* update references

* continue on error

* add docstring test

* use relativ action references

* Change step / job docstrings

* update to merge

* reformat comments 1

* fix docstrings 2

* fix docstrings 3

* fix docstrings 4

* fix docstrings 5

* fix docstrings 6

* fix docstrings 7

* fix docstrings 8

* fix docstirns 9

* fix docstrings 10

* docstring adjustments

* update the remaining docstrings

* small docstring changes

* fix function name

* update support files for docstrings

* Add missing args to docstrings

* Remove outdated function

* Add docstrings for the 'build' app

* Make API code cleaner

* add more docstrings for plugin app

* Remove dead code for plugin settings
No idea what that was even intended for

* ignore __init__ files for docstrings

* More docstrings

* Update docstrings for the 'part' directory

* Fixes for related_part functionality

* Fix removed stuff from merge 99676ee

* make more consistent

* Show statistics for docstrings

* add more docstrings

* move specific register statements to make them clearer to understant

* More docstrings for common

* and more docstrings

* and more

* simpler call

* docstrings for notifications

* docstrings for common/tests

* Add docs for common/models

* Revert "move specific register statements to make them clearer to understant"

This reverts commit ca96654622.

* use typing here

* Revert "Make API code cleaner"

This reverts commit 24fb68bd3e.

* docstring updates for the 'users' app

* Add generic Meta info to simple Meta classes

* remove unneeded unique_together statements

* More simple metas

* Remove unnecessary format specifier

* Remove extra json format specifiers

* Add docstrings for the 'plugin' app

* Docstrings for the 'label' app

* Add missing docstrings for the 'report' app

* Fix build test regression

* Fix top-level files

* docstrings for InvenTree/InvenTree

* reduce unneeded code

* add docstrings

* and more docstrings

* more docstrings

* more docstrings for stock

* more docstrings

* docstrings for order/views

* Docstrings for various files in the 'order' app

* Docstrings for order/test_api.py

* Docstrings for order/serializers.py

* Docstrings for order/admin.py

* More docstrings for the order app

* Add docstrings for the 'company' app

* Add unit tests for rebuilding the reference fields

* Prune out some more dead code

* remove more dead code

Co-authored-by: Oliver Walters <oliver.henry.walters@gmail.com>
This commit is contained in:
Matthias Mair
2022-06-01 17:37:39 +02:00
committed by GitHub
parent 66a6915213
commit 0c97a50e47
223 changed files with 4416 additions and 6980 deletions
.github/workflows
InvenTree
InvenTree
build
common
company
gunicorn.conf.py
label
manage.py
order
part
plugin
plugins
report
script
stock
users
ci
setup.cfgtasks.py

@@ -1,10 +1,12 @@
"""Admin functionality for the 'label' app"""
from django.contrib import admin
from .models import PartLabel, StockItemLabel, StockLocationLabel
class LabelAdmin(admin.ModelAdmin):
"""Admin class for the various label models"""
list_display = ('name', 'description', 'label', 'filters', 'enabled')

@@ -1,3 +1,5 @@
"""API functionality for the 'label' app"""
from django.conf import settings
from django.core.exceptions import FieldError, ValidationError
from django.http import HttpResponse, JsonResponse
@@ -21,9 +23,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 +41,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 +54,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 +77,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 +113,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 +147,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 +180,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 +208,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 +276,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 +324,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 +337,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,39 +392,30 @@ 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
def get(self, request, *args, **kwargs):
"""Print labels based on the request parameters"""
locations = self.get_locations()
return self.print(request, locations)
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,15 +438,13 @@ 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
def filter_queryset(self, queryset):
"""Custom queryset filtering for the PartLabel list"""
queryset = super().filter_queryset(queryset)
parts = self.get_parts()
@@ -539,27 +484,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,3 +1,5 @@
"""label app specification"""
import hashlib
import logging
import os
@@ -14,10 +16,7 @@ logger = logging.getLogger("inventree")
def hashFile(filename):
"""
Calculate the MD5 hash of a file
"""
"""Calculate the MD5 hash of a file."""
md5 = hashlib.md5()
with open(filename, 'rb') as f:
@@ -28,20 +27,17 @@ def hashFile(filename):
class LabelConfig(AppConfig):
"""App configuration class for the 'label' app"""
name = 'label'
def ready(self):
"""
This function is called whenever the label app is loaded
"""
"""This function is called whenever the label app is loaded."""
if canAppAccessDatabase():
self.create_labels() # pragma: no cover
def create_labels(self):
"""
Create all default templates
"""
"""Create all default templates."""
# Test if models are ready
try:
from .models import StockLocationLabel
@@ -56,11 +52,7 @@ class LabelConfig(AppConfig):
self.create_part_labels()
def create_stock_item_labels(self):
"""
Create database entries for the default StockItemLabel templates,
if they do not already exist
"""
"""Create database entries for the default StockItemLabel templates, if they do not already exist."""
from .models import StockItemLabel
src_dir = os.path.join(
@@ -139,11 +131,7 @@ class LabelConfig(AppConfig):
)
def create_stock_location_labels(self):
"""
Create database entries for the default StockItemLocation templates,
if they do not already exist
"""
"""Create database entries for the default StockItemLocation templates, if they do not already exist."""
from .models import StockLocationLabel
src_dir = os.path.join(
@@ -229,11 +217,7 @@ class LabelConfig(AppConfig):
)
def create_part_labels(self):
"""
Create database entries for the default PartLabel templates,
if they do not already exist.
"""
"""Create database entries for the default PartLabel templates, if they do not already exist."""
from .models import PartLabel
src_dir = os.path.join(

@@ -1,6 +1,4 @@
"""
Label printing models
"""
"""Label printing models."""
import datetime
import logging
@@ -32,55 +30,52 @@ logger = logging.getLogger("inventree")
def rename_label(instance, filename):
""" Place the label file into the correct subdirectory """
"""Place the label file into the correct subdirectory."""
filename = os.path.basename(filename)
return os.path.join('label', 'template', instance.SUBDIR, filename)
def validate_stock_item_filters(filters):
"""Validate query filters for the StockItemLabel model"""
filters = validateFilterString(filters, model=stock.models.StockItem)
return filters
def validate_stock_location_filters(filters):
"""Validate query filters for the StockLocationLabel model"""
filters = validateFilterString(filters, model=stock.models.StockLocation)
return filters
def validate_part_filters(filters):
"""Validate query filters for the PartLabel model"""
filters = validateFilterString(filters, model=part.models.Part)
return filters
class WeasyprintLabelMixin(WeasyTemplateResponseMixin):
"""
Class for rendering a label to a PDF
"""
"""Class for rendering a label to a PDF."""
pdf_filename = 'label.pdf'
pdf_attachment = True
def __init__(self, request, template, **kwargs):
"""Initialize a label mixin with certain properties"""
self.request = request
self.template_name = template
self.pdf_filename = kwargs.get('filename', 'label.pdf')
class LabelTemplate(models.Model):
"""
Base class for generic, filterable labels.
"""
"""Base class for generic, filterable labels."""
class Meta:
"""Metaclass options. Abstract ensures no database table is created."""
abstract = True
# Each class of label files will be stored in a separate subdirectory
@@ -91,9 +86,11 @@ class LabelTemplate(models.Model):
@property
def template(self):
"""Return the file path of the template associated with this label instance"""
return self.label.path
def __str__(self):
"""Format a string representation of a label instance"""
return "{n} - {d}".format(
n=self.name,
d=self.description
@@ -150,11 +147,10 @@ class LabelTemplate(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.label.name
template = template.replace('/', os.path.sep)
template = template.replace('\\', os.path.sep)
@@ -164,19 +160,14 @@ class LabelTemplate(models.Model):
return template
def get_context_data(self, request):
"""
Supply custom context data to the template for rendering.
"""Supply custom context data to the template for rendering.
Note: Override this in any subclass
"""
return {} # pragma: no cover
def generate_filename(self, request, **kwargs):
"""
Generate a filename for this label
"""
"""Generate a filename for this label."""
template_string = Template(self.filename_pattern)
ctx = self.context(request)
@@ -186,10 +177,7 @@ class LabelTemplate(models.Model):
return template_string.render(context)
def context(self, request):
"""
Provides context data to the template.
"""
"""Provides context data to the template."""
context = self.get_context_data(request)
# Add "basic" context data which gets passed to every label
@@ -204,21 +192,17 @@ class LabelTemplate(models.Model):
return context
def render_as_string(self, request, **kwargs):
"""
Render the label to a HTML string
"""Render the label 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 label template to a PDF file
"""Render the label template to a PDF file.
Uses django-weasyprint plugin to render HTML template
"""
wp = WeasyprintLabelMixin(
request,
self.template_name,
@@ -235,12 +219,11 @@ class LabelTemplate(models.Model):
class StockItemLabel(LabelTemplate):
"""
Template for printing StockItem labels
"""
"""Template for printing StockItem labels."""
@staticmethod
def get_api_url():
"""Return the API URL associated with the StockItemLabel model"""
return reverse('api-stockitem-label-list') # pragma: no cover
SUBDIR = "stockitem"
@@ -255,10 +238,7 @@ class StockItemLabel(LabelTemplate):
)
def get_context_data(self, request):
"""
Generate context data for each provided StockItem
"""
"""Generate context data for each provided StockItem."""
stock_item = self.object_to_print
return {
@@ -279,12 +259,11 @@ class StockItemLabel(LabelTemplate):
class StockLocationLabel(LabelTemplate):
"""
Template for printing StockLocation labels
"""
"""Template for printing StockLocation labels."""
@staticmethod
def get_api_url():
"""Return the API URL associated with the StockLocationLabel model"""
return reverse('api-stocklocation-label-list') # pragma: no cover
SUBDIR = "stocklocation"
@@ -298,10 +277,7 @@ class StockLocationLabel(LabelTemplate):
)
def get_context_data(self, request):
"""
Generate context data for each provided StockLocation
"""
"""Generate context data for each provided StockLocation."""
location = self.object_to_print
return {
@@ -311,12 +287,11 @@ class StockLocationLabel(LabelTemplate):
class PartLabel(LabelTemplate):
"""
Template for printing Part labels
"""
"""Template for printing Part labels."""
@staticmethod
def get_api_url():
"""Return the API url associated with the PartLabel model"""
return reverse('api-part-label-list') # pragma: no cover
SUBDIR = 'part'
@@ -331,10 +306,7 @@ class PartLabel(LabelTemplate):
)
def get_context_data(self, request):
"""
Generate context data for each provided Part object
"""
"""Generate context data for each provided Part object."""
part = self.object_to_print
return {

@@ -1,3 +1,5 @@
"""API serializers for the label app"""
from InvenTree.serializers import (InvenTreeAttachmentSerializerField,
InvenTreeModelSerializer)
@@ -5,13 +7,13 @@ from .models import PartLabel, StockItemLabel, StockLocationLabel
class StockItemLabelSerializer(InvenTreeModelSerializer):
"""
Serializes a StockItemLabel object.
"""
"""Serializes a StockItemLabel object."""
label = InvenTreeAttachmentSerializerField(required=True)
class Meta:
"""Metaclass options."""
model = StockItemLabel
fields = [
'pk',
@@ -24,13 +26,13 @@ class StockItemLabelSerializer(InvenTreeModelSerializer):
class StockLocationLabelSerializer(InvenTreeModelSerializer):
"""
Serializes a StockLocationLabel object
"""
"""Serializes a StockLocationLabel object."""
label = InvenTreeAttachmentSerializerField(required=True)
class Meta:
"""Metaclass options."""
model = StockLocationLabel
fields = [
'pk',
@@ -43,13 +45,13 @@ class StockLocationLabelSerializer(InvenTreeModelSerializer):
class PartLabelSerializer(InvenTreeModelSerializer):
"""
Serializes a PartLabel object
"""
"""Serializes a PartLabel object."""
label = InvenTreeAttachmentSerializerField(required=True)
class Meta:
"""Metaclass options."""
model = PartLabel
fields = [
'pk',

@@ -1,4 +1,4 @@
# Tests for labels
"""Unit tests for label API"""
from django.urls import reverse
@@ -6,9 +6,7 @@ from InvenTree.api_tester import InvenTreeAPITestCase
class TestReportTests(InvenTreeAPITestCase):
"""
Tests for the StockItem TestReport templates
"""
"""Tests for the StockItem TestReport templates."""
fixtures = [
'category',
@@ -24,12 +22,8 @@ class TestReportTests(InvenTreeAPITestCase):
list_url = reverse('api-stockitem-testreport-list')
def setUp(self):
super().setUp()
def do_list(self, filters={}):
"""Helper function to request list of labels with provided filters"""
response = self.client.get(self.list_url, filters, format='json')
self.assertEqual(response.status_code, 200)
@@ -37,7 +31,7 @@ class TestReportTests(InvenTreeAPITestCase):
return response.data
def test_list(self):
"""Test the API list endpoint"""
response = self.do_list()
# TODO - Add some report templates to the fixtures

@@ -1,4 +1,4 @@
# Tests for labels
"""Tests for labels"""
import os
@@ -16,6 +16,7 @@ from .models import PartLabel, StockItemLabel, StockLocationLabel
class LabelTest(InvenTreeAPITestCase):
"""Unit test class for label models"""
fixtures = [
'category',
@@ -25,15 +26,12 @@ class LabelTest(InvenTreeAPITestCase):
]
def setUp(self) -> None:
"""Ensure that some label instances exist as part of init routine"""
super().setUp()
# ensure the labels were created
apps.get_app_config('label').create_labels()
def test_default_labels(self):
"""
Test that the default label templates are copied across
"""
"""Test that the default label templates are copied across."""
labels = StockItemLabel.objects.all()
self.assertTrue(labels.count() > 0)
@@ -43,10 +41,7 @@ class LabelTest(InvenTreeAPITestCase):
self.assertTrue(labels.count() > 0)
def test_default_files(self):
"""
Test that label files exist in the MEDIA directory
"""
"""Test that label files exist in the MEDIA directory."""
item_dir = os.path.join(
settings.MEDIA_ROOT,
'label',
@@ -70,10 +65,7 @@ class LabelTest(InvenTreeAPITestCase):
self.assertTrue(len(files) > 0)
def test_filters(self):
"""
Test the label filters
"""
"""Test the label filters."""
filter_string = "part__pk=10"
filters = validateFilterString(filter_string, model=StockItem)
@@ -86,8 +78,7 @@ class LabelTest(InvenTreeAPITestCase):
validateFilterString(bad_filter_string, model=StockItem)
def test_label_rendering(self):
"""Test label rendering"""
"""Test label rendering."""
labels = PartLabel.objects.all()
part = Part.objects.first()

@@ -1 +0,0 @@
# Create your views here.