diff --git a/InvenTree/build/api.py b/InvenTree/build/api.py
index c9e756839f..fedfe53008 100644
--- a/InvenTree/build/api.py
+++ b/InvenTree/build/api.py
@@ -17,8 +17,6 @@ from .serializers import BuildSerializer
class BuildList(generics.ListCreateAPIView):
""" API endpoint for accessing a list of Build objects.
-
- Provides two methods:
- GET: Return list of objects (with filters)
- POST: Create a new Build object
diff --git a/InvenTree/build/serializers.py b/InvenTree/build/serializers.py
index 92534ef816..955a8d59f5 100644
--- a/InvenTree/build/serializers.py
+++ b/InvenTree/build/serializers.py
@@ -1,5 +1,5 @@
"""
-Provides JSON serializers for Build API
+JSON serializers for Build API
"""
# -*- coding: utf-8 -*-
diff --git a/InvenTree/build/views.py b/InvenTree/build/views.py
index 756566e694..c8d40e80e6 100644
--- a/InvenTree/build/views.py
+++ b/InvenTree/build/views.py
@@ -1,5 +1,5 @@
"""
-Provides Django views for interacting with Build objects
+Django views for interacting with Build objects
"""
# -*- coding: utf-8 -*-
diff --git a/InvenTree/company/serializers.py b/InvenTree/company/serializers.py
index a87560a873..e9a24e0d2a 100644
--- a/InvenTree/company/serializers.py
+++ b/InvenTree/company/serializers.py
@@ -1,9 +1,14 @@
+"""
+JSON serializers for Company app
+"""
+
from rest_framework import serializers
from .models import Company
class CompanyBriefSerializer(serializers.ModelSerializer):
+ """ Serializer for Company object (limited detail) """
url = serializers.CharField(source='get_absolute_url', read_only=True)
@@ -17,6 +22,7 @@ class CompanyBriefSerializer(serializers.ModelSerializer):
class CompanySerializer(serializers.ModelSerializer):
+ """ Serializer for Company object (full detail) """
url = serializers.CharField(source='get_absolute_url', read_only=True)
diff --git a/InvenTree/company/views.py b/InvenTree/company/views.py
index 5a80e5ea15..b171d83f86 100644
--- a/InvenTree/company/views.py
+++ b/InvenTree/company/views.py
@@ -1,3 +1,8 @@
+"""
+Django views for interacting with Company app
+"""
+
+
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
@@ -12,12 +17,20 @@ from .forms import CompanyImageForm
class CompanyIndex(ListView):
+ """ View for displaying list of companies
+ """
+
model = Company
template_name = 'company/index.html'
context_object_name = 'companies'
paginate_by = 50
def get_queryset(self):
+ """ Retrieve the Company queryset based on HTTP request parameters.
+
+ - supplier: Filter by supplier
+ - customer: Filter by customer
+ """
queryset = Company.objects.all().order_by('name')
if self.request.GET.get('supplier', None):
@@ -30,6 +43,7 @@ class CompanyIndex(ListView):
class CompanyDetail(DetailView):
+ """ Detail view for Company object """
context_obect_name = 'company'
template_name = 'company/detail.html'
queryset = Company.objects.all()
@@ -37,6 +51,7 @@ class CompanyDetail(DetailView):
class CompanyImage(AjaxUpdateView):
+ """ View for uploading an image for the Company """
model = Company
ajax_template_name = 'modal_form.html'
ajax_form_title = 'Update Company Image'
@@ -49,6 +64,7 @@ class CompanyImage(AjaxUpdateView):
class CompanyEdit(AjaxUpdateView):
+ """ View for editing a Company object """
model = Company
form_class = EditCompanyForm
context_object_name = 'company'
@@ -62,6 +78,7 @@ class CompanyEdit(AjaxUpdateView):
class CompanyCreate(AjaxCreateView):
+ """ View for creating a new Company object """
model = Company
context_object_name = 'company'
form_class = EditCompanyForm
@@ -75,6 +92,7 @@ class CompanyCreate(AjaxCreateView):
class CompanyDelete(AjaxDeleteView):
+ """ View for deleting a Company object """
model = Company
success_url = '/company/'
ajax_template_name = 'company/delete.html'
diff --git a/InvenTree/part/api.py b/InvenTree/part/api.py
index 9b5aef436a..d9ee5e5566 100644
--- a/InvenTree/part/api.py
+++ b/InvenTree/part/api.py
@@ -1,3 +1,7 @@
+"""
+Provides a JSON API for the Part app
+"""
+
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
@@ -26,6 +30,12 @@ class PartCategoryTree(TreeSerializer):
class CategoryList(generics.ListCreateAPIView):
+ """ API endpoint for accessing a list of PartCategory objects.
+
+ - GET: Return a list of PartCategory objects
+ - POST: Create a new PartCategory object
+ """
+
queryset = PartCategory.objects.all()
serializer_class = CategorySerializer
@@ -56,11 +66,13 @@ class CategoryList(generics.ListCreateAPIView):
class CategoryDetail(generics.RetrieveUpdateDestroyAPIView):
+ """ API endpoint for detail view of a single PartCategory object """
serializer_class = CategorySerializer
queryset = PartCategory.objects.all()
class PartDetail(generics.RetrieveUpdateDestroyAPIView):
+ """ API endpoint for detail view of a single Part object """
queryset = Part.objects.all()
serializer_class = PartSerializer
@@ -70,6 +82,11 @@ class PartDetail(generics.RetrieveUpdateDestroyAPIView):
class PartList(generics.ListCreateAPIView):
+ """ API endpoint for accessing a list of Part objects
+
+ - GET: Return list of objects
+ - POST: Create a new Part object
+ """
serializer_class = PartSerializer
@@ -130,6 +147,11 @@ class PartList(generics.ListCreateAPIView):
class BomList(generics.ListCreateAPIView):
+ """ API endpoing for accessing a list of BomItem objects
+
+ - GET: Return list of BomItem objects
+ - POST: Create a new BomItem object
+ """
queryset = BomItem.objects.all()
serializer_class = BomItemSerializer
@@ -151,6 +173,7 @@ class BomList(generics.ListCreateAPIView):
class BomDetail(generics.RetrieveUpdateDestroyAPIView):
+ """ API endpoint for detail view of a single BomItem object """
queryset = BomItem.objects.all()
serializer_class = BomItemSerializer
@@ -161,6 +184,11 @@ class BomDetail(generics.RetrieveUpdateDestroyAPIView):
class SupplierPartList(generics.ListCreateAPIView):
+ """ API endpoint for list view of SupplierPart object
+
+ - GET: Return list of SupplierPart objects
+ - POST: Create a new SupplierPart object
+ """
queryset = SupplierPart.objects.all()
serializer_class = SupplierPartSerializer
@@ -182,6 +210,12 @@ class SupplierPartList(generics.ListCreateAPIView):
class SupplierPartDetail(generics.RetrieveUpdateDestroyAPIView):
+ """ API endpoint for detail view of SupplierPart object
+
+ - GET: Retrieve detail view
+ - PATCH: Update object
+ - DELETE: Delete objec
+ """
queryset = SupplierPart.objects.all()
serializer_class = SupplierPartSerializer
@@ -192,6 +226,11 @@ class SupplierPartDetail(generics.RetrieveUpdateDestroyAPIView):
class SupplierPriceBreakList(generics.ListCreateAPIView):
+ """ API endpoint for list view of SupplierPriceBreak object
+
+ - GET: Retrieve list of SupplierPriceBreak objects
+ - POST: Create a new SupplierPriceBreak object
+ """
queryset = SupplierPriceBreak.objects.all()
serializer_class = SupplierPriceBreakSerializer
diff --git a/InvenTree/part/forms.py b/InvenTree/part/forms.py
index 4d7d05bba1..040444093d 100644
--- a/InvenTree/part/forms.py
+++ b/InvenTree/part/forms.py
@@ -1,3 +1,7 @@
+"""
+Django Forms for interacting with Part objects
+"""
+
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
@@ -10,6 +14,7 @@ from .models import SupplierPart
class PartImageForm(HelperForm):
+ """ Form for uploading a Part image """
class Meta:
model = Part
@@ -40,6 +45,7 @@ class BomExportForm(HelperForm):
class EditPartForm(HelperForm):
+ """ Form for editing a Part object """
class Meta:
model = Part
@@ -64,6 +70,7 @@ class EditPartForm(HelperForm):
class EditCategoryForm(HelperForm):
+ """ Form for editing a PartCategory object """
class Meta:
model = PartCategory
@@ -75,6 +82,7 @@ class EditCategoryForm(HelperForm):
class EditBomItemForm(HelperForm):
+ """ Form for editing a BomItem object """
class Meta:
model = BomItem
@@ -88,6 +96,7 @@ class EditBomItemForm(HelperForm):
class EditSupplierPartForm(HelperForm):
+ """ Form for editing a SupplierPart object """
class Meta:
model = SupplierPart
diff --git a/InvenTree/part/models.py b/InvenTree/part/models.py
index dd30178f39..4022b7ec71 100644
--- a/InvenTree/part/models.py
+++ b/InvenTree/part/models.py
@@ -1,3 +1,7 @@
+"""
+Part database model definitions
+"""
+
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
@@ -47,11 +51,19 @@ class PartCategory(InvenTreeTree):
@property
def has_parts(self):
+ """ True if there are any parts in this category """
return self.parts.count() > 0
@receiver(pre_delete, sender=PartCategory, dispatch_uid='partcategory_delete_log')
def before_delete_part_category(sender, instance, using, **kwargs):
+ """ Receives before_delete signal for PartCategory object
+
+ Before deleting, update child Part and PartCategory objects:
+
+ - For each child category, set the parent to the parent of *this* category
+ - For each part, set the 'category' to the parent of *this* category
+ """
# Update each part in this category to point to the parent category
for part in instance.parts.all():
@@ -67,6 +79,16 @@ def before_delete_part_category(sender, instance, using, **kwargs):
# Function to automatically rename a part image on upload
# Format: part_pk.
def rename_part_image(instance, filename):
+ """ Function for renaming a part image file
+
+ Args:
+ instance: Instance of a Part object
+ filename: Name of original uploaded file
+
+ Returns:
+ Cleaned filename in format part__img
+ """
+
base = 'part_images'
if filename.count('.') > 0:
@@ -248,7 +270,8 @@ class Part(models.Model):
@property
def allocation_count(self):
- """ Return true if any of this part is allocated
+ """ Return true if any of this part is allocated:
+
- To another build
- To a customer order
"""
@@ -311,6 +334,15 @@ class Part(models.Model):
def attach_file(instance, filename):
+ """ Function for storing a file for a PartAttachment
+
+ Args:
+ instance: Instance of a PartAttachment object
+ filename: name of uploaded file
+
+ Returns:
+ path to store file, format: 'part_file__filename'
+ """
# Construct a path to store a file attachment
return os.path.join('part_files', str(instance.part.id), filename)
@@ -356,6 +388,13 @@ class BomItem(models.Model):
note = models.CharField(max_length=100, blank=True, help_text='Item notes')
def clean(self):
+ """ Check validity of the BomItem model.
+
+ Performs model checks beyond simple field validation.
+
+ - A part cannot refer to itself in its BOM
+ - A part cannot refer to a part which refers to it
+ """
# A part cannot refer to itself in its BOM
if self.part == self.sub_part:
@@ -382,8 +421,9 @@ class BomItem(models.Model):
class SupplierPart(models.Model):
""" Represents a unique part as provided by a Supplier
Each SupplierPart is identified by a MPN (Manufacturer Part Number)
- Each SupplierPart is also linked to a Part object
- - A Part may be available from multiple suppliers
+ Each SupplierPart is also linked to a Part object.
+
+ A Part may be available from multiple suppliers
"""
def get_absolute_url(self):
@@ -453,6 +493,7 @@ class SupplierPart(models.Model):
def get_price(self, quantity, moq=True, multiples=True):
""" Calculate the supplier price based on quantity price breaks.
+
- If no price breaks available, use the single_price field
- Don't forget to add in flat-fee cost (base_cost field)
- If MOQ (minimum order quantity) is required, bump quantity
diff --git a/InvenTree/part/serializers.py b/InvenTree/part/serializers.py
index 68066645bd..0344e46fe7 100644
--- a/InvenTree/part/serializers.py
+++ b/InvenTree/part/serializers.py
@@ -1,3 +1,7 @@
+"""
+JSON serializers for Part app
+"""
+
from rest_framework import serializers
from .models import Part, PartCategory, BomItem
@@ -7,6 +11,7 @@ from InvenTree.serializers import InvenTreeModelSerializer
class CategorySerializer(serializers.ModelSerializer):
+ """ Serializer for PartCategory """
url = serializers.CharField(source='get_absolute_url', read_only=True)
@@ -23,6 +28,7 @@ class CategorySerializer(serializers.ModelSerializer):
class PartBriefSerializer(serializers.ModelSerializer):
+ """ Serializer for Part (brief detail) """
url = serializers.CharField(source='get_absolute_url', read_only=True)
@@ -68,6 +74,7 @@ class PartSerializer(serializers.ModelSerializer):
class BomItemSerializer(InvenTreeModelSerializer):
+ """ Serializer for BomItem object """
# url = serializers.CharField(source='get_absolute_url', read_only=True)
@@ -89,6 +96,7 @@ class BomItemSerializer(InvenTreeModelSerializer):
class SupplierPartSerializer(serializers.ModelSerializer):
+ """ Serializer for SupplierPart object """
url = serializers.CharField(source='get_absolute_url', read_only=True)
@@ -112,6 +120,7 @@ class SupplierPartSerializer(serializers.ModelSerializer):
class SupplierPriceBreakSerializer(serializers.ModelSerializer):
+ """ Serializer for SupplierPriceBreak object """
class Meta:
model = SupplierPriceBreak
diff --git a/InvenTree/part/urls.py b/InvenTree/part/urls.py
index 14a15625f9..96c830f556 100644
--- a/InvenTree/part/urls.py
+++ b/InvenTree/part/urls.py
@@ -1,3 +1,7 @@
+"""
+URL lookup for Part app
+"""
+
from django.conf.urls import url, include
from . import views
diff --git a/InvenTree/part/views.py b/InvenTree/part/views.py
index 4f7175301a..db9f66994c 100644
--- a/InvenTree/part/views.py
+++ b/InvenTree/part/views.py
@@ -1,3 +1,7 @@
+"""
+Django views for interacting with Part app
+"""
+
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
@@ -21,10 +25,12 @@ from .forms import EditSupplierPartForm
from InvenTree.views import AjaxView, AjaxCreateView, AjaxUpdateView, AjaxDeleteView
-from InvenTree.helpers import DownloadFile
+from InvenTree.helpers import DownloadFile, str2bool
class PartIndex(ListView):
+ """ View for displaying list of Part objects
+ """
model = Part
template_name = 'part/category.html'
context_object_name = 'parts'
@@ -45,8 +51,12 @@ class PartIndex(ListView):
class PartCreate(AjaxCreateView):
- """ Create a new part
- - Optionally provide a category object as initial data
+ """ View for creating a new Part object.
+
+ Options for providing initial conditions:
+
+ - Provide a category object as initial data
+ - Copy an existing Part
"""
model = Part
form_class = EditPartForm
@@ -64,6 +74,10 @@ class PartCreate(AjaxCreateView):
# If a category is provided in the URL, pass that to the page context
def get_context_data(self, **kwargs):
+ """ Provide extra context information for the form to display:
+
+ - Add category information (if provided)
+ """
context = super(PartCreate, self).get_context_data(**kwargs)
# Add category information to the page
@@ -76,6 +90,11 @@ class PartCreate(AjaxCreateView):
# Pre-fill the category field if a valid category is provided
def get_initial(self):
+ """ Get initial data for the new Part object:
+
+ - If a category is provided, pre-fill the Category field
+ - If 'copy' parameter is provided, copy from referenced Part
+ """
# Is the client attempting to copy an existing part?
part_to_copy = self.request.GET.get('copy', None)
@@ -98,15 +117,22 @@ class PartCreate(AjaxCreateView):
class PartDetail(DetailView):
+ """ Detail view for Part object
+ """
+
context_object_name = 'part'
queryset = Part.objects.all()
template_name = 'part/detail.html'
# Add in some extra context information based on query params
def get_context_data(self, **kwargs):
+ """ Provide extra context data to template
+
+ - If '?editing=True', set 'editing_enabled' context variable
+ """
context = super(PartDetail, self).get_context_data(**kwargs)
- if self.request.GET.get('edit', '').lower() in ['true', 'yes', '1']:
+ if str2bool(self.request.GET.get('edit', '')):
context['editing_enabled'] = 1
else:
context['editing_enabled'] = 0
@@ -115,6 +141,7 @@ class PartDetail(DetailView):
class PartImage(AjaxUpdateView):
+ """ View for uploading Part image """
model = Part
ajax_template_name = 'modal_form.html'
@@ -128,6 +155,8 @@ class PartImage(AjaxUpdateView):
class PartEdit(AjaxUpdateView):
+ """ View for editing Part object """
+
model = Part
template_name = 'part/edit.html'
form_class = EditPartForm
@@ -214,6 +243,8 @@ class BomDownload(AjaxView):
class PartDelete(AjaxDeleteView):
+ """ View to delete a Part object """
+
model = Part
template_name = 'part/delete.html'
ajax_template_name = 'part/partial_delete.html'
@@ -229,6 +260,7 @@ class PartDelete(AjaxDeleteView):
class CategoryDetail(DetailView):
+ """ Detail view for PartCategory """
model = PartCategory
context_object_name = 'category'
queryset = PartCategory.objects.all()
@@ -236,6 +268,7 @@ class CategoryDetail(DetailView):
class CategoryEdit(AjaxUpdateView):
+ """ Update view to edit a PartCategory """
model = PartCategory
template_name = 'part/category_edit.html'
form_class = EditCategoryForm
@@ -251,6 +284,7 @@ class CategoryEdit(AjaxUpdateView):
class CategoryDelete(AjaxDeleteView):
+ """ Delete view to delete a PartCategory """
model = PartCategory
template_name = 'part/category_delete.html'
context_object_name = 'category'
@@ -263,6 +297,7 @@ class CategoryDelete(AjaxDeleteView):
class CategoryCreate(AjaxCreateView):
+ """ Create view to make a new PartCategory """
model = PartCategory
ajax_form_action = reverse_lazy('category-create')
ajax_form_title = 'Create new part category'
@@ -271,6 +306,10 @@ class CategoryCreate(AjaxCreateView):
form_class = EditCategoryForm
def get_context_data(self, **kwargs):
+ """ Add extra context data to template.
+
+ - If parent category provided, pass the category details to the template
+ """
context = super(CategoryCreate, self).get_context_data(**kwargs).copy()
parent_id = self.request.GET.get('category', None)
@@ -281,6 +320,10 @@ class CategoryCreate(AjaxCreateView):
return context
def get_initial(self):
+ """ Get initial data for new PartCategory
+
+ - If parent provided, pre-fill the parent category
+ """
initials = super(CategoryCreate, self).get_initial().copy()
parent_id = self.request.GET.get('category', None)
@@ -292,12 +335,14 @@ class CategoryCreate(AjaxCreateView):
class BomItemDetail(DetailView):
+ """ Detail view for BomItem """
context_object_name = 'item'
queryset = BomItem.objects.all()
template_name = 'part/bom-detail.html'
class BomItemCreate(AjaxCreateView):
+ """ Create view for making a new BomItem object """
model = BomItem
form_class = EditBomItemForm
template_name = 'part/bom-create.html'
@@ -305,6 +350,11 @@ class BomItemCreate(AjaxCreateView):
ajax_form_title = 'Create BOM item'
def get_initial(self):
+ """ Provide initial data for the BomItem:
+
+ - If 'parent' provided, set the parent part field
+ """
+
# Look for initial values
initials = super(BomItemCreate, self).get_initial().copy()
@@ -318,6 +368,8 @@ class BomItemCreate(AjaxCreateView):
class BomItemEdit(AjaxUpdateView):
+ """ Update view for editing BomItem """
+
model = BomItem
form_class = EditBomItemForm
template_name = 'part/bom-edit.html'
@@ -326,6 +378,7 @@ class BomItemEdit(AjaxUpdateView):
class BomItemDelete(AjaxDeleteView):
+ """ Delete view for removing BomItem """
model = BomItem
template_name = 'part/bom-delete.html'
context_object_name = 'item'
@@ -333,6 +386,7 @@ class BomItemDelete(AjaxDeleteView):
class SupplierPartDetail(DetailView):
+ """ Detail view for SupplierPart """
model = SupplierPart
template_name = 'company/partdetail.html'
context_object_name = 'part'
@@ -340,6 +394,8 @@ class SupplierPartDetail(DetailView):
class SupplierPartEdit(AjaxUpdateView):
+ """ Update view for editing SupplierPart """
+
model = SupplierPart
template_name = 'company/partedit.html'
context_object_name = 'part'
@@ -349,6 +405,8 @@ class SupplierPartEdit(AjaxUpdateView):
class SupplierPartCreate(AjaxCreateView):
+ """ Create view for making new SupplierPart """
+
model = SupplierPart
form_class = EditSupplierPartForm
ajax_template_name = 'modal_form.html'
@@ -356,6 +414,11 @@ class SupplierPartCreate(AjaxCreateView):
context_object_name = 'part'
def get_initial(self):
+ """ Provide initial data for new SupplierPart:
+
+ - If 'supplier_id' provided, pre-fill supplier field
+ - If 'part_id' provided, pre-fill part field
+ """
initials = super(SupplierPartCreate, self).get_initial().copy()
supplier_id = self.request.GET.get('supplier', None)
@@ -374,6 +437,7 @@ class SupplierPartCreate(AjaxCreateView):
class SupplierPartDelete(AjaxDeleteView):
+ """ Delete view for removing a SupplierPart """
model = SupplierPart
success_url = '/supplier/'
template_name = 'company/partdelete.html'