From d9169a0dd1c16ee63d8d0fecea516f212d093f97 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Sat, 27 Apr 2019 20:35:14 +1000 Subject: [PATCH 01/13] Add docstrings for Build app --- InvenTree/build/api.py | 13 ++++++- InvenTree/build/forms.py | 6 +++ InvenTree/build/models.py | 69 +++++++++++++++++++--------------- InvenTree/build/serializers.py | 5 +++ InvenTree/build/urls.py | 4 ++ InvenTree/build/views.py | 22 +++++++++++ InvenTree/keygen.py | 14 +++---- docs/conf.py | 11 +++++- 8 files changed, 103 insertions(+), 41 deletions(-) diff --git a/InvenTree/build/api.py b/InvenTree/build/api.py index 41d46bab49..c9e756839f 100644 --- a/InvenTree/build/api.py +++ b/InvenTree/build/api.py @@ -1,3 +1,7 @@ +""" +Provides a JSON API for the Build app +""" + # -*- coding: utf-8 -*- from __future__ import unicode_literals @@ -11,7 +15,14 @@ from .models import Build from .serializers import BuildSerializer -class BuildList(generics.ListAPIView): +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 + """ queryset = Build.objects.all() serializer_class = BuildSerializer diff --git a/InvenTree/build/forms.py b/InvenTree/build/forms.py index 1775b5c3b5..6254f0745c 100644 --- a/InvenTree/build/forms.py +++ b/InvenTree/build/forms.py @@ -1,3 +1,7 @@ +""" +Django Forms for interacting with Build objects +""" + # -*- coding: utf-8 -*- from __future__ import unicode_literals @@ -7,6 +11,8 @@ from .models import Build class EditBuildForm(HelperForm): + """ Form for editing a Build object. + """ class Meta: model = Build diff --git a/InvenTree/build/models.py b/InvenTree/build/models.py index c72aa5209b..3d4feef93a 100644 --- a/InvenTree/build/models.py +++ b/InvenTree/build/models.py @@ -1,3 +1,9 @@ +""" +Build models + +Defines the database models for part Builds +""" + # -*- coding: utf-8 -*- from __future__ import unicode_literals @@ -9,60 +15,61 @@ from django.core.validators import MinValueValidator class Build(models.Model): - """ A Build object organises the creation of new parts from the component parts - It uses the part BOM to generate new parts. - Parts are then taken from stock + """ A Build object organises the creation of new parts from the component parts. """ def get_absolute_url(self): return reverse('build-detail', kwargs={'pk': self.id}) + part = models.ForeignKey('part.Part', on_delete=models.CASCADE, + related_name='builds', + limit_choices_to={'buildable': True}, + ) + """ A reference to the part being built - only parts marked as 'buildable' may be selected """ + + #: Brief title describing the build + title = models.CharField(max_length=100, help_text='Brief description of the build') + + #: Number of output parts to build + quantity = models.PositiveIntegerField(default=1, + validators=[MinValueValidator(1)], + help_text='Number of parts to build') + # Build status codes PENDING = 10 # Build is pending / active HOLDING = 20 # Build is currently being held CANCELLED = 30 # Build was cancelled COMPLETE = 40 # Build is complete + #: Build status codes BUILD_STATUS_CODES = {PENDING: _("Pending"), HOLDING: _("Holding"), CANCELLED: _("Cancelled"), COMPLETE: _("Complete"), } - batch = models.CharField(max_length=100, blank=True, null=True, - help_text='Batch code for this build output') - - # Status of the build + #: Status of the build (ref BUILD_STATUS_CODES) status = models.PositiveIntegerField(default=PENDING, choices=BUILD_STATUS_CODES.items(), validators=[MinValueValidator(0)]) - # Date the build model was 'created' + #: Batch number for the build (optional) + batch = models.CharField(max_length=100, blank=True, null=True, + help_text='Batch code for this build output') + + + #: Date the build model was 'created' creation_date = models.DateField(auto_now=True, editable=False) - # Date the build was 'completed' + #: Date the build was 'completed' (and parts removed from stock) completion_date = models.DateField(null=True, blank=True) - # Brief build title - title = models.CharField(max_length=100, help_text='Brief description of the build') - - # A reference to the part being built - # Only 'buildable' parts can be selected - part = models.ForeignKey('part.Part', on_delete=models.CASCADE, - related_name='builds', - limit_choices_to={'buildable': True}, - ) - - # How many parts to build? - quantity = models.PositiveIntegerField(default=1, - validators=[MinValueValidator(1)], - help_text='Number of parts to build') - - # Notes can be attached to each build output + #: Notes attached to each build output notes = models.TextField(blank=True) @property def required_parts(self): + """ Returns a dict of parts required to build this part (BOM) """ parts = [] for item in self.part.bom_items.all(): @@ -77,8 +84,7 @@ class Build(models.Model): @property def can_build(self): - """ Return true if there are enough parts to supply build - """ + """ Return true if there are enough parts to supply build """ for item in self.required_parts: if item['part'].total_stock < item['quantity']: @@ -88,10 +94,10 @@ class Build(models.Model): @property def is_active(self): - """ Is this build active? - An active build is either: - - Pending - - Holding + """ Is this build active? An active build is either: + + - PENDING + - HOLDING """ return self.status in [ @@ -101,4 +107,5 @@ class Build(models.Model): @property def is_complete(self): + """ Returns True if the build status is COMPLETE """ return self.status == self.COMPLETE diff --git a/InvenTree/build/serializers.py b/InvenTree/build/serializers.py index ac36fd5b41..92534ef816 100644 --- a/InvenTree/build/serializers.py +++ b/InvenTree/build/serializers.py @@ -1,3 +1,7 @@ +""" +Provides JSON serializers for Build API +""" + # -*- coding: utf-8 -*- from __future__ import unicode_literals @@ -7,6 +11,7 @@ from .models import Build class BuildSerializer(serializers.ModelSerializer): + """ Serializes a Build object """ url = serializers.CharField(source='get_absolute_url', read_only=True) status_text = serializers.CharField(source='get_status_display', read_only=True) diff --git a/InvenTree/build/urls.py b/InvenTree/build/urls.py index a8e4435e24..fea3b6596c 100644 --- a/InvenTree/build/urls.py +++ b/InvenTree/build/urls.py @@ -1,3 +1,7 @@ +""" +URL lookup for Build app +""" + from django.conf.urls import url, include from . import views diff --git a/InvenTree/build/views.py b/InvenTree/build/views.py index f3f7bd79f3..756566e694 100644 --- a/InvenTree/build/views.py +++ b/InvenTree/build/views.py @@ -1,3 +1,7 @@ +""" +Provides Django views for interacting with Build objects +""" + # -*- coding: utf-8 -*- from __future__ import unicode_literals @@ -13,11 +17,14 @@ from InvenTree.views import AjaxView, AjaxUpdateView, AjaxCreateView class BuildIndex(ListView): + """ View for displaying list of Builds + """ model = Build template_name = 'build/index.html' context_object_name = 'builds' def get_queryset(self): + """ Return all Build objects (order by date, newest first) """ return Build.objects.order_by('status', '-completion_date') def get_context_data(self, **kwargs): @@ -35,6 +42,9 @@ class BuildIndex(ListView): class BuildCancel(AjaxView): + """ View to cancel a Build. + Provides a cancellation information dialog + """ model = Build template_name = 'build/cancel.html' ajax_form_title = 'Cancel Build' @@ -42,6 +52,7 @@ class BuildCancel(AjaxView): fields = [] def post(self, request, *args, **kwargs): + """ Handle POST request. Mark the build status as CANCELLED """ build = get_object_or_404(Build, pk=self.kwargs['pk']) @@ -51,24 +62,28 @@ class BuildCancel(AjaxView): return self.renderJsonResponse(request, None) def get_data(self): + """ Provide JSON context data. """ return { 'info': 'Build was cancelled' } class BuildDetail(DetailView): + """ Detail view of a single Build object. """ model = Build template_name = 'build/detail.html' context_object_name = 'build' class BuildAllocate(DetailView): + """ View for allocating parts to a Build """ model = Build context_object_name = 'build' template_name = 'build/allocate.html' class BuildCreate(AjaxCreateView): + """ View to create a new Build object """ model = Build context_object_name = 'build' form_class = EditBuildForm @@ -76,6 +91,11 @@ class BuildCreate(AjaxCreateView): ajax_template_name = 'modal_form.html' def get_initial(self): + """ Get initial parameters for Build creation. + + If 'part' is specified in the GET query, initialize the Build with the specified Part + """ + initials = super(BuildCreate, self).get_initial().copy() part_id = self.request.GET.get('part', None) @@ -92,6 +112,8 @@ class BuildCreate(AjaxCreateView): class BuildUpdate(AjaxUpdateView): + """ View for editing a Build object """ + model = Build form_class = EditBuildForm context_object_name = 'build' diff --git a/InvenTree/keygen.py b/InvenTree/keygen.py index 02909b0d79..c96df22794 100644 --- a/InvenTree/keygen.py +++ b/InvenTree/keygen.py @@ -1,7 +1,5 @@ """ -Module keygen -============= -This module generates a Django SECRET_KEY file to be used by manage.py +Generates a Django SECRET_KEY file to be used by manage.py """ import random @@ -15,11 +13,13 @@ KEY_DIR = os.path.dirname(os.path.realpath(__file__)) def generate_key(length=50): - """ - Generate a random string + """ Generate a random string - :param length: Number of characters in returned string (default=50) - :returns: Randomized secret key string + Args: + length: Number of characters in returned string (default = 50) + + Returns: + Randomized secret key string """ options = string.digits + string.ascii_letters + string.punctuation diff --git a/docs/conf.py b/docs/conf.py index 78bcb74673..3b21b24458 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -28,9 +28,13 @@ copyright = '2019, InvenTree' # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ - 'autoapi.extension' + 'sphinx.ext.napoleon', + 'autoapi.extension', ] +napoleon_google_docstring = True +napoleon_numpy_docstring = False + autoapi_dirs = [ '../InvenTree', ] @@ -44,7 +48,10 @@ autoapi_type = 'python' autoapi_ignore = [ '*migrations*', '**/test*.py', - '**/manage.py' + '**/manage.py', + '**/apps.py', + '**/admin.py', + '**/templates/', ] # Add any paths that contain templates here, relative to this directory. From c3f057092679ec81031544673ffebfc1ded1e043 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Sat, 27 Apr 2019 20:43:27 +1000 Subject: [PATCH 02/13] Add docstring to Company app --- InvenTree/build/models.py | 4 +--- InvenTree/company/api.py | 12 ++++++++++++ InvenTree/company/forms.py | 6 ++++++ InvenTree/company/models.py | 24 ++++++++++++++++++++++++ 4 files changed, 43 insertions(+), 3 deletions(-) diff --git a/InvenTree/build/models.py b/InvenTree/build/models.py index 3d4feef93a..2af1077205 100644 --- a/InvenTree/build/models.py +++ b/InvenTree/build/models.py @@ -1,7 +1,5 @@ """ -Build models - -Defines the database models for part Builds +Build database model definitions """ # -*- coding: utf-8 -*- diff --git a/InvenTree/company/api.py b/InvenTree/company/api.py index d90cfdb1ee..dfa3ebfb77 100644 --- a/InvenTree/company/api.py +++ b/InvenTree/company/api.py @@ -1,3 +1,7 @@ +""" +Provides a JSON API for the Company app +""" + # -*- coding: utf-8 -*- from __future__ import unicode_literals @@ -12,6 +16,13 @@ from .serializers import CompanySerializer class CompanyList(generics.ListCreateAPIView): + """ API endpoint for accessing a list of Company objects + + Provides two methods: + + - GET: Return list of objects + - POST: Create a new Company object + """ serializer_class = CompanySerializer queryset = Company.objects.all() @@ -44,6 +55,7 @@ class CompanyList(generics.ListCreateAPIView): class CompanyDetail(generics.RetrieveUpdateDestroyAPIView): + """ API endpoint for detail of a single Company object """ queryset = Company.objects.all() serializer_class = CompanySerializer diff --git a/InvenTree/company/forms.py b/InvenTree/company/forms.py index 7fca7c7e72..7bb9e382c9 100644 --- a/InvenTree/company/forms.py +++ b/InvenTree/company/forms.py @@ -1,3 +1,7 @@ +""" +Django Forms for interacting with Company objects +""" + # -*- coding: utf-8 -*- from __future__ import unicode_literals @@ -7,6 +11,7 @@ from .models import Company class EditCompanyForm(HelperForm): + """ Form for editing a Company object """ class Meta: model = Company @@ -26,6 +31,7 @@ class EditCompanyForm(HelperForm): class CompanyImageForm(HelperForm): + """ Form for uploading a Company image """ class Meta: model = Company diff --git a/InvenTree/company/models.py b/InvenTree/company/models.py index fe97d9a90c..dcee37959d 100644 --- a/InvenTree/company/models.py +++ b/InvenTree/company/models.py @@ -1,3 +1,7 @@ +""" +Company database model definitions +""" + # -*- coding: utf-8 -*- from __future__ import unicode_literals @@ -8,6 +12,16 @@ from django.urls import reverse def rename_company_image(instance, filename): + """ Function to rename a company image after upload + + Args: + instance: Company object + filename: uploaded image filename + + Returns: + New image filename + """ + base = 'company_images' if filename.count('.') > 0: @@ -24,6 +38,9 @@ def rename_company_image(instance, filename): class Company(models.Model): + """ A Company object represents an external company. + It may be a supplier or a customer (or both). + """ name = models.CharField(max_length=100, unique=True, help_text='Company name') @@ -54,21 +71,28 @@ class Company(models.Model): is_supplier = models.BooleanField(default=True) def __str__(self): + """ Get string representation of a Company """ return "{n} - {d}".format(n=self.name, d=self.description) def get_absolute_url(self): + """ Get the web URL for the detail view for this Company """ return reverse('company-detail', kwargs={'pk': self.id}) @property def part_count(self): + """ The number of parts supplied by this company """ return self.parts.count() @property def has_parts(self): + """ Return True if this company supplies any parts """ return self.part_count > 0 class Contact(models.Model): + """ A Contact represents a person who works at a particular company. + A Company may have zero or more associated Contact objects + """ name = models.CharField(max_length=100) From 5e6d49102d85d772bd02a7083f8da9ba7302e6a9 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Sat, 27 Apr 2019 21:21:58 +1000 Subject: [PATCH 03/13] Add docstring documentation to the main InvenTree app --- InvenTree/InvenTree/forms.py | 5 +++ InvenTree/InvenTree/helpers.py | 46 +++++++++++++++++++------ InvenTree/InvenTree/models.py | 54 ++++++++++++++---------------- InvenTree/InvenTree/serializers.py | 7 ++++ InvenTree/InvenTree/urls.py | 7 ++++ InvenTree/InvenTree/views.py | 51 +++++++++++++++++++++++----- docs/conf.py | 5 +++ 7 files changed, 128 insertions(+), 47 deletions(-) diff --git a/InvenTree/InvenTree/forms.py b/InvenTree/InvenTree/forms.py index 002a83e896..8d574f7a38 100644 --- a/InvenTree/InvenTree/forms.py +++ b/InvenTree/InvenTree/forms.py @@ -1,3 +1,7 @@ +""" +Helper forms which subclass Django forms to provide additional functionality +""" + # -*- coding: utf-8 -*- from __future__ import unicode_literals @@ -6,6 +10,7 @@ from crispy_forms.helper import FormHelper class HelperForm(forms.ModelForm): + """ Provides simple integration of crispy_forms extension. """ def __init__(self, *args, **kwargs): super(forms.ModelForm, self).__init__(*args, **kwargs) diff --git a/InvenTree/InvenTree/helpers.py b/InvenTree/InvenTree/helpers.py index a492ca353d..7b8f1b57c1 100644 --- a/InvenTree/InvenTree/helpers.py +++ b/InvenTree/InvenTree/helpers.py @@ -1,3 +1,7 @@ +""" +Provides helper functions used throughout the InvenTree project +""" + import io from wsgiref.util import FileWrapper @@ -5,7 +9,14 @@ from django.http import StreamingHttpResponse def str2bool(text, test=True): - """ Test if a string 'looks' like a boolean value + """ Test if a string 'looks' like a boolean value. + + Args: + text: Input text + test (default = True): Set which boolean value to look for + + Returns: + True if the text looks like the selected boolean value """ if test: return str(text).lower() in ['1', 'y', 'yes', 't', 'true', 'ok', ] @@ -13,21 +24,36 @@ def str2bool(text, test=True): return str(text).lower() in ['0', 'n', 'no', 'none', 'f', 'false', ] -def WrapWithQuotes(text): - # TODO - Make this better - if not text.startswith('"'): - text = '"' + text +def WrapWithQuotes(text, quote='"'): + """ Wrap the supplied text with quotes - if not text.endswith('"'): - text = text + '"' + Args: + text: Input text to wrap + quote: Quote character to use for wrapping (default = "") + + Returns: + Supplied text wrapped in quote char + """ + + if not text.startswith(quote): + text = quote + text + + if not text.endswith(quote): + text = text + quote return text def DownloadFile(data, filename, content_type='application/text'): - """ - Create a dynamic file for the user to download. - @param data is the raw file data + """ Create a dynamic file for the user to download. + + Args: + data: Raw file data (string or bytes) + filename: Filename for the file download + content_type: Content type for the download + + Return: + A StreamingHttpResponse object wrapping the supplied data """ filename = WrapWithQuotes(filename) diff --git a/InvenTree/InvenTree/models.py b/InvenTree/InvenTree/models.py index 2b49f7b908..0c1e1f10ee 100644 --- a/InvenTree/InvenTree/models.py +++ b/InvenTree/InvenTree/models.py @@ -1,3 +1,7 @@ +""" +Generic models which provide extra functionality over base Django model types. +""" + from __future__ import unicode_literals from django.db import models @@ -11,6 +15,7 @@ from django.dispatch import receiver class InvenTreeTree(models.Model): """ Provides an abstracted self-referencing tree model for data categories. + - Each Category has one parent Category, which can be blank (for a top-level Category). - Each Category can have zero-or-more child Categor(y/ies) """ @@ -69,10 +74,12 @@ class InvenTreeTree(models.Model): @property def has_children(self): + """ True if there are any children under this item """ return self.children.count() > 0 @property def children(self): + """ Return the children of this item """ contents = ContentType.objects.get_for_model(type(self)) childs = contents.get_all_objects_for_this_type(parent=self.id) @@ -100,11 +107,10 @@ class InvenTreeTree(models.Model): @property def parentpath(self): - """ Return the parent path of this category + """ Get the parent path of this category - Todo: - This function is recursive and expensive. - It should be reworked such that only a single db call is required + Returns: + List of category names from the top level to the parent of this category """ if self.parent: @@ -114,10 +120,21 @@ class InvenTreeTree(models.Model): @property def path(self): + """ Get the complete part of this category. + + e.g. ["Top", "Second", "Third", "This"] + + Returns: + List of category names from the top level to this category + """ return self.parentpath + [self] @property def pathstring(self): + """ Get a string representation for the path of this item. + + e.g. "Top/Second/Third/This" + """ return '/'.join([item.name for item in self.path]) def __setattr__(self, attrname, val): @@ -157,38 +174,19 @@ class InvenTreeTree(models.Model): super(InvenTreeTree, self).__setattr__(attrname, val) def __str__(self): - """ String representation of a category is the full path to that category - - Todo: - This is recursive - Make it not so. - """ + """ String representation of a category is the full path to that category """ return self.pathstring @receiver(pre_delete, sender=InvenTreeTree, dispatch_uid='tree_pre_delete_log') def before_delete_tree_item(sender, instance, using, **kwargs): + """ Receives pre_delete signal from InvenTreeTree object. + + Before an item is deleted, update each child object to point to the parent of the object being deleted. + """ # Update each tree item below this one for child in instance.children.all(): child.parent = instance.parent child.save() - - -def FilterChildren(queryset, parent): - """ Filter a queryset, limit to only objects that are a child of the given parent - Filter is passed in the URL string, e.g. '/?parent=123' - To accommodate for items without a parent, top-level items can be specified as: - none / false / null / top / 0 - """ - - if not parent: - return queryset - elif str2bool(parent, False): - return queryset.filter(parent=None) - else: - parent_id = int(parent) - if parent_id == 0: - return queryset.filter(parent=None) - else: - return queryset.filter(parent=parent_id) diff --git a/InvenTree/InvenTree/serializers.py b/InvenTree/InvenTree/serializers.py index 4ab1fd694d..77e38ade21 100644 --- a/InvenTree/InvenTree/serializers.py +++ b/InvenTree/InvenTree/serializers.py @@ -1,3 +1,8 @@ +""" +Serializers used in various InvenTree apps +""" + + # -*- coding: utf-8 -*- from __future__ import unicode_literals @@ -7,6 +12,7 @@ from django.contrib.auth.models import User class UserSerializer(serializers.ModelSerializer): + """ Serializer for User - provides all fields """ class Meta: model = User @@ -14,6 +20,7 @@ class UserSerializer(serializers.ModelSerializer): class UserSerializerBrief(serializers.ModelSerializer): + """ Serializer for User - provides limited information """ class Meta: model = User diff --git a/InvenTree/InvenTree/urls.py b/InvenTree/InvenTree/urls.py index c33689be61..ccc828de55 100644 --- a/InvenTree/InvenTree/urls.py +++ b/InvenTree/InvenTree/urls.py @@ -1,3 +1,10 @@ +""" +Top-level URL lookup for InvenTree application. + +Passes URL lookup downstream to each app as required. +""" + + from django.conf.urls import url, include from django.contrib import admin from django.contrib.auth import views as auth_views diff --git a/InvenTree/InvenTree/views.py b/InvenTree/InvenTree/views.py index c160398caf..fee5d2481f 100644 --- a/InvenTree/InvenTree/views.py +++ b/InvenTree/InvenTree/views.py @@ -1,3 +1,10 @@ +""" +Various Views which provide extra functionality over base Django Views. + +In particular these views provide base functionality for rendering Django forms +as JSON objects and passing them to modal forms (using jQuery / bootstrap). +""" + # -*- coding: utf-8 -*- from __future__ import unicode_literals @@ -12,6 +19,8 @@ from rest_framework import views class TreeSerializer(views.APIView): + """ JSON View for serializing a Tree object. + """ def itemToJson(self, item): @@ -52,20 +61,34 @@ class TreeSerializer(views.APIView): class AjaxMixin(object): + """ AjaxMixin provides basic functionality for rendering a Django form to JSON. + Handles jsonResponse rendering, and adds extra data for the modal forms to process + on the client side. + """ ajax_form_action = '' ajax_form_title = '' def get_data(self): + """ Get extra context data (default implementation is empty dict) + + Returns: + dict object (empty) + """ return {} - def getAjaxTemplate(self): - if hasattr(self, 'ajax_template_name'): - return self.ajax_template_name - else: - return self.template_name - def renderJsonResponse(self, request, form=None, data={}, context={}): + """ Render a JSON response based on specific class context. + + Args: + request: HTTP request object (e.g. GET / POST) + form: Django form object (may be None) + data: Extra JSON data to pass to client + context: Extra context data to pass to template rendering + + Returns: + JSON response object + """ if form: context['form'] = form @@ -73,7 +96,7 @@ class AjaxMixin(object): data['title'] = self.ajax_form_title data['html_form'] = render_to_string( - self.getAjaxTemplate(), + self.ajax_template_name, context, request=request ) @@ -88,7 +111,8 @@ class AjaxMixin(object): class AjaxView(AjaxMixin, View): - """ Bare-bones AjaxView """ + """ An 'AJAXified' View for displaying an object + """ # By default, point to the modal_form template # (this can be overridden by a child class) @@ -201,7 +225,7 @@ class AjaxDeleteView(AjaxMixin, DeleteView): data = {'id': self.get_object().id, 'delete': False, 'title': self.ajax_form_title, - 'html_data': render_to_string(self.getAjaxTemplate(), + 'html_data': render_to_string(self.ajax_template_name, self.get_context_data(), request=request) } @@ -229,15 +253,24 @@ class AjaxDeleteView(AjaxMixin, DeleteView): class IndexView(TemplateView): + """ View for InvenTree index page """ template_name = 'InvenTree/index.html' class SearchView(TemplateView): + """ View for InvenTree search page. + + Displays results of search query + """ template_name = 'InvenTree/search.html' def post(self, request, *args, **kwargs): + """ Handle POST request (which contains search query). + + Pass the search query to the page template + """ context = self.get_context_data() diff --git a/docs/conf.py b/docs/conf.py index 3b21b24458..295dee688f 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -41,6 +41,8 @@ autoapi_dirs = [ autoapi_options = [ 'members', + 'private-members', + 'special-members', ] autoapi_type = 'python' @@ -51,6 +53,9 @@ autoapi_ignore = [ '**/manage.py', '**/apps.py', '**/admin.py', + '**/middleware.py', + '**/utils.py', + '**/wsgi.py', '**/templates/', ] From ed3ae30248fc8a314723d5cfccfaa590eb60e215 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Sat, 27 Apr 2019 22:18:07 +1000 Subject: [PATCH 04/13] Documentation for Part app --- InvenTree/build/api.py | 2 - InvenTree/build/serializers.py | 2 +- InvenTree/build/views.py | 2 +- InvenTree/company/serializers.py | 6 +++ InvenTree/company/views.py | 18 ++++++++ InvenTree/part/api.py | 39 +++++++++++++++++ InvenTree/part/forms.py | 9 ++++ InvenTree/part/models.py | 47 +++++++++++++++++++-- InvenTree/part/serializers.py | 9 ++++ InvenTree/part/urls.py | 4 ++ InvenTree/part/views.py | 72 ++++++++++++++++++++++++++++++-- 11 files changed, 199 insertions(+), 11 deletions(-) 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' From a499fd325e54815f890915b416741ad7a50a2b7e Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Sat, 27 Apr 2019 22:49:16 +1000 Subject: [PATCH 05/13] Docstring for Stock app --- InvenTree/build/api.py | 2 +- InvenTree/company/forms.py | 2 +- InvenTree/company/urls.py | 5 +++ InvenTree/stock/api.py | 58 ++++++++++++++++++++-------------- InvenTree/stock/forms.py | 8 +++++ InvenTree/stock/models.py | 5 +++ InvenTree/stock/serializers.py | 9 ++++-- InvenTree/stock/urls.py | 4 +++ InvenTree/stock/views.py | 7 ++-- 9 files changed, 71 insertions(+), 29 deletions(-) diff --git a/InvenTree/build/api.py b/InvenTree/build/api.py index fedfe53008..393bd1f073 100644 --- a/InvenTree/build/api.py +++ b/InvenTree/build/api.py @@ -1,5 +1,5 @@ """ -Provides a JSON API for the Build app +JSON API for the Build app """ # -*- coding: utf-8 -*- diff --git a/InvenTree/company/forms.py b/InvenTree/company/forms.py index 7bb9e382c9..bf7057a1ea 100644 --- a/InvenTree/company/forms.py +++ b/InvenTree/company/forms.py @@ -1,5 +1,5 @@ """ -Django Forms for interacting with Company objects +Django Forms for interacting with Company app """ # -*- coding: utf-8 -*- diff --git a/InvenTree/company/urls.py b/InvenTree/company/urls.py index 13c478f28a..2c5018ebec 100644 --- a/InvenTree/company/urls.py +++ b/InvenTree/company/urls.py @@ -1,3 +1,8 @@ +""" +URL lookup for Company app +""" + + from django.conf.urls import url, include from django.views.generic.base import RedirectView diff --git a/InvenTree/stock/api.py b/InvenTree/stock/api.py index 215a9e311f..c352605ad2 100644 --- a/InvenTree/stock/api.py +++ b/InvenTree/stock/api.py @@ -1,3 +1,7 @@ +""" +JSON API for the Stock app +""" + from django_filters.rest_framework import FilterSet, DjangoFilterBackend from django_filters import NumberFilter @@ -26,7 +30,7 @@ class StockCategoryTree(TreeSerializer): class StockDetail(generics.RetrieveUpdateDestroyAPIView): - """ + """ API detail endpoint for Stock object get: Return a single StockItem object @@ -44,6 +48,11 @@ class StockDetail(generics.RetrieveUpdateDestroyAPIView): class StockFilter(FilterSet): + """ FilterSet for advanced stock filtering. + + Allows greater-than / less-than filtering for stock quantity + """ + min_stock = NumberFilter(name='quantity', lookup_expr='gte') max_stock = NumberFilter(name='quantity', lookup_expr='lte') @@ -53,12 +62,11 @@ class StockFilter(FilterSet): class StockStocktake(APIView): - """ - Stocktake API endpoint provides stock update of multiple items simultaneously + """ Stocktake API endpoint provides stock update of multiple items simultaneously. The 'action' field tells the type of stock action to perform: - * 'stocktake' - Count the stock item(s) - * 'remove' - Remove the quantity provided from stock - * 'add' - Add the quantity provided from stock + - stocktake: Count the stock item(s) + - remove: Remove the quantity provided from stock + - add: Add the quantity provided from stock """ permission_classes = [ @@ -129,6 +137,7 @@ class StockStocktake(APIView): class StockMove(APIView): + """ API endpoint for performing stock movements """ permission_classes = [ permissions.IsAuthenticatedOrReadOnly, @@ -183,6 +192,11 @@ class StockMove(APIView): class StockLocationList(generics.ListCreateAPIView): + """ API endpoint for list view of StockLocation objects: + + - GET: Return list of StockLocation objects + - POST: Create a new StockLocation + """ queryset = StockLocation.objects.all() @@ -204,14 +218,10 @@ class StockLocationList(generics.ListCreateAPIView): class StockList(generics.ListCreateAPIView): - """ + """ API endpoint for list view of Stock objects - get: - Return a list of all StockItem objects - (with optional query filters) - - post: - Create a new StockItem + - GET: Return a list of all StockItem objects (with optional query filters) + - POST: Create a new StockItem """ def get_queryset(self): @@ -268,6 +278,7 @@ class StockList(generics.ListCreateAPIView): class StockStocktakeEndpoint(generics.UpdateAPIView): + """ API endpoint for performing stocktake """ queryset = StockItem.objects.all() serializer_class = StockQuantitySerializer @@ -283,6 +294,13 @@ class StockStocktakeEndpoint(generics.UpdateAPIView): class StockTrackingList(generics.ListCreateAPIView): + """ API endpoint for list view of StockItemTracking objects. + + StockItemTracking objects are read-only + (they are created by internal model functionality) + + - GET: Return list of StockItemTracking objects + """ queryset = StockItemTracking.objects.all() serializer_class = StockTrackingSerializer @@ -312,17 +330,11 @@ class StockTrackingList(generics.ListCreateAPIView): class LocationDetail(generics.RetrieveUpdateDestroyAPIView): - """ - - get: - Return a single StockLocation object - - post: - Update a StockLocation object - - delete: - Remove a StockLocation object + """ API endpoint for detail view of StockLocation object + - GET: Return a single StockLocation object + - PATCH: Update a StockLocation object + - DELETE: Remove a StockLocation object """ queryset = StockLocation.objects.all() diff --git a/InvenTree/stock/forms.py b/InvenTree/stock/forms.py index bd0254e720..a94156ad29 100644 --- a/InvenTree/stock/forms.py +++ b/InvenTree/stock/forms.py @@ -1,3 +1,7 @@ +""" +Django Forms for interacting with Stock app +""" + # -*- coding: utf-8 -*- from __future__ import unicode_literals @@ -8,6 +12,7 @@ from .models import StockLocation, StockItem class EditStockLocationForm(HelperForm): + """ Form for editing a StockLocation """ class Meta: model = StockLocation @@ -19,6 +24,7 @@ class EditStockLocationForm(HelperForm): class CreateStockItemForm(HelperForm): + """ Form for creating a new StockItem """ class Meta: model = StockItem @@ -38,6 +44,7 @@ class CreateStockItemForm(HelperForm): class MoveStockItemForm(forms.ModelForm): + """ Form for moving a StockItem to a new location """ note = forms.CharField(label='Notes', required=True, help_text='Add note (required)') @@ -60,6 +67,7 @@ class StocktakeForm(forms.ModelForm): class EditStockItemForm(HelperForm): + """ Form for editing a StockItem object """ class Meta: model = StockItem diff --git a/InvenTree/stock/models.py b/InvenTree/stock/models.py index 4b1505d2d0..636efeb293 100644 --- a/InvenTree/stock/models.py +++ b/InvenTree/stock/models.py @@ -1,3 +1,8 @@ +""" +Stock database model definitions +""" + + # -*- coding: utf-8 -*- from __future__ import unicode_literals diff --git a/InvenTree/stock/serializers.py b/InvenTree/stock/serializers.py index 64baeb5330..0a863aa7f9 100644 --- a/InvenTree/stock/serializers.py +++ b/InvenTree/stock/serializers.py @@ -1,3 +1,7 @@ +""" +JSON serializers for Stock app +""" + from rest_framework import serializers from .models import StockItem, StockLocation @@ -44,8 +48,8 @@ class StockItemSerializerBrief(serializers.ModelSerializer): class StockItemSerializer(serializers.ModelSerializer): - """ - Serializer for a StockItem + """ Serializer for a StockItem: + - Includes serialization for the linked part - Includes serialization for the item location """ @@ -112,6 +116,7 @@ class LocationSerializer(serializers.ModelSerializer): class StockTrackingSerializer(serializers.ModelSerializer): + """ Serializer for StockItemTracking model """ url = serializers.CharField(source='get_absolute_url', read_only=True) diff --git a/InvenTree/stock/urls.py b/InvenTree/stock/urls.py index 503c9e809c..6b3fc0ff2d 100644 --- a/InvenTree/stock/urls.py +++ b/InvenTree/stock/urls.py @@ -1,3 +1,7 @@ +""" +URL lookup for Stock app +""" + from django.conf.urls import url, include from . import views diff --git a/InvenTree/stock/views.py b/InvenTree/stock/views.py index fc1d2726aa..70d3f7ded3 100644 --- a/InvenTree/stock/views.py +++ b/InvenTree/stock/views.py @@ -1,3 +1,7 @@ +""" +Django views for interacting with Stock app +""" + # -*- coding: utf-8 -*- from __future__ import unicode_literals @@ -19,8 +23,7 @@ from .forms import StocktakeForm class StockIndex(ListView): - """ - StockIndex view loads all StockLocation and StockItem object + """ StockIndex view loads all StockLocation and StockItem object """ model = StockItem template_name = 'stock/location.html' From 4aef8eecea9502a45571ab7563d3727bc334df61 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Sun, 28 Apr 2019 00:03:19 +1000 Subject: [PATCH 06/13] Force TOC to appear in sidebar for each page - https://stackoverflow.com/questions/18969093/how-to-include-the-toctree-in-the-sidebar-of-each-page#19007358 --- .gitignore | 2 -- docs/conf.py | 18 +++++++++++++++++- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index b4aa808107..6229f979df 100644 --- a/.gitignore +++ b/.gitignore @@ -29,8 +29,6 @@ local_settings.py # Sphinx files docs/_build -docs/_static -docs/_templates # Local media storage (only when running in development mode) InvenTree/media diff --git a/docs/conf.py b/docs/conf.py index 295dee688f..127f6abe22 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -28,6 +28,7 @@ copyright = '2019, InvenTree' # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ + 'sphinx.ext.autodoc', 'sphinx.ext.napoleon', 'autoapi.extension', ] @@ -37,6 +38,12 @@ napoleon_numpy_docstring = False autoapi_dirs = [ '../InvenTree', + '../Inventree/build', + '../InvenTree/company', + '../InvenTree/InvenTree', + '../InvenTree/part', + '../InvenTree/stock', + '../InvenTree/users', ] autoapi_options = [ @@ -60,7 +67,8 @@ autoapi_ignore = [ ] # Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] +autoapi_template_dir = 'templates' + # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. @@ -85,3 +93,11 @@ html_theme = 'sphinx_rtd_theme' # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] + +# Table of contents in sidebar +html_sidebars = {'**': [ + 'globaltoc.html', + 'relations.html', + 'sourcelink.html', + 'searchbox.html' +]} \ No newline at end of file From 0d8f14d3e289bbedb4017bf4633bc0263cf610bc Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Sun, 28 Apr 2019 00:20:27 +1000 Subject: [PATCH 07/13] Add index and modindex to sidebar - https://stackoverflow.com/questions/25243482/how-to-add-sphinx-generated-index-to-the-sidebar-when-using-read-the-docs-theme --- docs/conf.py | 2 ++ docs/templates/layout.html | 7 +++++++ 2 files changed, 9 insertions(+) create mode 100644 docs/templates/layout.html diff --git a/docs/conf.py b/docs/conf.py index 127f6abe22..3efd301b47 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -68,7 +68,9 @@ autoapi_ignore = [ # Add any paths that contain templates here, relative to this directory. autoapi_template_dir = 'templates' +autoapi_root = 'api' +templates_path = ['templates'] # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. diff --git a/docs/templates/layout.html b/docs/templates/layout.html new file mode 100644 index 0000000000..a778e0fdb3 --- /dev/null +++ b/docs/templates/layout.html @@ -0,0 +1,7 @@ +{% extends "!layout.html" %} + + {% block menu %} + {{ super() }} + Index + Module Index + {% endblock %} \ No newline at end of file From c87166a49a82f702dfe1e11472583f9562f2e620 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Sun, 28 Apr 2019 00:37:20 +1000 Subject: [PATCH 08/13] Prevent auto-addition of toc by autoapi - Also fix links in sidebar --- docs/conf.py | 1 + docs/index.rst | 8 +++----- docs/introduction.rst | 4 ++++ docs/templates/layout.html | 4 ++-- 4 files changed, 10 insertions(+), 7 deletions(-) create mode 100644 docs/introduction.rst diff --git a/docs/conf.py b/docs/conf.py index 3efd301b47..792f96b010 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -69,6 +69,7 @@ autoapi_ignore = [ # Add any paths that contain templates here, relative to this directory. autoapi_template_dir = 'templates' autoapi_root = 'api' +autoapi_add_toctree_entry = False templates_path = ['templates'] diff --git a/docs/index.rst b/docs/index.rst index 9c43b19929..9130e05d58 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -9,12 +9,10 @@ Welcome to InvenTree's documentation! .. toctree:: :maxdepth: 2 :caption: Contents: + :hidden: + InvenTree -Indices and tables -================== - * :ref:`genindex` -* :ref:`modindex` -* :ref:`search` +* :ref:`modindex` \ No newline at end of file diff --git a/docs/introduction.rst b/docs/introduction.rst new file mode 100644 index 0000000000..a10fe0e5b5 --- /dev/null +++ b/docs/introduction.rst @@ -0,0 +1,4 @@ +Introduction +============ + +This is a test \ No newline at end of file diff --git a/docs/templates/layout.html b/docs/templates/layout.html index a778e0fdb3..620814d1b3 100644 --- a/docs/templates/layout.html +++ b/docs/templates/layout.html @@ -2,6 +2,6 @@ {% block menu %} {{ super() }} - Index - Module Index + Module Index + Index {% endblock %} \ No newline at end of file From 3a43a8ba727c0bb9b33c6e21a44d292668d1aa09 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Sun, 28 Apr 2019 00:57:29 +1000 Subject: [PATCH 09/13] Add custom links to app documentation --- docs/index.rst | 14 ++++---------- docs/introduction.rst | 20 +++++++++++++++++--- 2 files changed, 21 insertions(+), 13 deletions(-) diff --git a/docs/index.rst b/docs/index.rst index 9130e05d58..114ecaf27b 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -1,18 +1,12 @@ -.. InvenTree documentation master file, created by - sphinx-quickstart on Sat Apr 27 15:45:39 2019. - You can adapt this file completely to your liking, but it should at least - contain the root `toctree` directive. +InvenTree's Django Documentation +================================ -Welcome to InvenTree's documentation! -===================================== +This documentation is auto-generated from the `InvenTree codebase `_ .. toctree:: + :titlesonly: :maxdepth: 2 :caption: Contents: :hidden: InvenTree - - -* :ref:`genindex` -* :ref:`modindex` \ No newline at end of file diff --git a/docs/introduction.rst b/docs/introduction.rst index a10fe0e5b5..505da5f865 100644 --- a/docs/introduction.rst +++ b/docs/introduction.rst @@ -1,4 +1,18 @@ -Introduction -============ +InvenTree +========= -This is a test \ No newline at end of file +InvenTree is an open source inventory management system which provides powerful low-level part management and stock tracking functionality. + +The core of the InvenTree software is a Python/Django database backend whi + + +Django Apps +=========== + +The InvenTree Django ecosystem provides the following 'apps' for core functionality: + +* `InvenTree `_ - High level management functions +* `Build `_ - Part build projects +* `Company `_ - Company management (suppliers / customers) +* `Part `_ - Part management +* `Stock `_ - Stock management \ No newline at end of file From b0487b33dc69201022eefcf7ba3f135392fd6957 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Sun, 28 Apr 2019 00:58:59 +1000 Subject: [PATCH 10/13] Change documentation root url to 'docs' --- docs/conf.py | 2 +- docs/introduction.rst | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 792f96b010..d1b8f1c5b8 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -68,7 +68,7 @@ autoapi_ignore = [ # Add any paths that contain templates here, relative to this directory. autoapi_template_dir = 'templates' -autoapi_root = 'api' +autoapi_root = 'docs' autoapi_add_toctree_entry = False templates_path = ['templates'] diff --git a/docs/introduction.rst b/docs/introduction.rst index 505da5f865..e117010749 100644 --- a/docs/introduction.rst +++ b/docs/introduction.rst @@ -11,8 +11,8 @@ Django Apps The InvenTree Django ecosystem provides the following 'apps' for core functionality: -* `InvenTree `_ - High level management functions -* `Build `_ - Part build projects -* `Company `_ - Company management (suppliers / customers) -* `Part `_ - Part management -* `Stock `_ - Stock management \ No newline at end of file +* `InvenTree `_ - High level management functions +* `Build `_ - Part build projects +* `Company `_ - Company management (suppliers / customers) +* `Part `_ - Part management +* `Stock `_ - Stock management \ No newline at end of file From aa04941bf162aea43e13dcfb32cfc4f4f1119b5a Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Sun, 28 Apr 2019 01:05:45 +1000 Subject: [PATCH 11/13] Add module-level docstrings for each app --- InvenTree/InvenTree/__init__.py | 5 ++ InvenTree/build/__init__.py | 5 ++ InvenTree/company/__init__.py | 8 +++ InvenTree/part/__init__.py | 10 ++++ InvenTree/stock/__init__.py | 9 +++ docs/templates/python/module.rst | 97 ++++++++++++++++++++++++++++++++ 6 files changed, 134 insertions(+) create mode 100644 docs/templates/python/module.rst diff --git a/InvenTree/InvenTree/__init__.py b/InvenTree/InvenTree/__init__.py index e69de29bb2..f02ada5664 100644 --- a/InvenTree/InvenTree/__init__.py +++ b/InvenTree/InvenTree/__init__.py @@ -0,0 +1,5 @@ +""" +The InvenTree module provides high-level management and functionality. + +It provides a number of helper functions and generic classes which are used by InvenTree apps. +""" \ No newline at end of file diff --git a/InvenTree/build/__init__.py b/InvenTree/build/__init__.py index e69de29bb2..17cba16fc7 100644 --- a/InvenTree/build/__init__.py +++ b/InvenTree/build/__init__.py @@ -0,0 +1,5 @@ +""" +The Build module is responsible for managing "Build" transactions. + +A Build consumes parts from stock to create new parts +""" \ No newline at end of file diff --git a/InvenTree/company/__init__.py b/InvenTree/company/__init__.py index e69de29bb2..c662aee7f8 100644 --- a/InvenTree/company/__init__.py +++ b/InvenTree/company/__init__.py @@ -0,0 +1,8 @@ +""" +The Company module is responsible for managing Company interactions. + +A company can be either (or both): + +- Supplier +- Customer +""" \ No newline at end of file diff --git a/InvenTree/part/__init__.py b/InvenTree/part/__init__.py index e69de29bb2..8fad2c5f19 100644 --- a/InvenTree/part/__init__.py +++ b/InvenTree/part/__init__.py @@ -0,0 +1,10 @@ +""" +The Part module is responsible for Part management. + +It includes models for: + +- PartCategory +- Part +- SupplierPart +- BomItem +""" \ No newline at end of file diff --git a/InvenTree/stock/__init__.py b/InvenTree/stock/__init__.py index e69de29bb2..7b58c08fc9 100644 --- a/InvenTree/stock/__init__.py +++ b/InvenTree/stock/__init__.py @@ -0,0 +1,9 @@ +""" +The Stock module is responsible for Stock management. + +It includes models for: + +- StockLocation +- StockItem +- StockItemTracking +""" \ No newline at end of file diff --git a/docs/templates/python/module.rst b/docs/templates/python/module.rst new file mode 100644 index 0000000000..ec51763131 --- /dev/null +++ b/docs/templates/python/module.rst @@ -0,0 +1,97 @@ +{% if not obj.display %} +:orphan: + +{% endif %} +:mod:`{{ obj.name }}` +======={{ "=" * obj.name|length }} + +.. py:module:: {{ obj.name }} + +{% if obj.docstring %} +.. autoapi-nested-parse:: + + {{ obj.docstring|prepare_docstring|indent(3) }} + +{% endif %} + +{% block subpackages %} +{% set visible_subpackages = obj.subpackages|selectattr("display")|list %} +{% if visible_subpackages %} +Subpackages +----------- +.. toctree:: + :titlesonly: + :maxdepth: 3 + +{% for subpackage in visible_subpackages %} + {{ subpackage.short_name }}/index.rst +{% endfor %} + + +{% endif %} +{% endblock %} +{% block submodules %} +{% set visible_submodules = obj.submodules|selectattr("display")|list %} +{% if visible_submodules %} +Submodules +---------- + +The {{ obj.name }} module contains the following submodules + +.. toctree:: + :titlesonly: + :maxdepth: 1 + +{% for submodule in visible_submodules %} + {{ submodule.short_name }}/index.rst +{% endfor %} + + +{% endif %} +{% endblock %} +{% block content %} +{% set visible_children = obj.children|selectattr("display")|list %} +{% if visible_children %} +{{ obj.type|title }} Contents +{{ "-" * obj.type|length }}--------- + +{% set visible_classes = visible_children|selectattr("type", "equalto", "class")|list %} +{% set visible_functions = visible_children|selectattr("type", "equalto", "function")|list %} +{% if include_summaries and (visible_classes or visible_functions) %} +{% block classes %} +{% if visible_classes %} +Classes +~~~~~~~ + +.. autoapisummary:: + +{% for klass in visible_classes %} + {{ klass.id }} +{% endfor %} + + +{% endif %} +{% endblock %} + +{% block functions %} +{% if visible_functions %} +Functions +~~~~~~~~~ + +.. autoapisummary:: + +{% for function in visible_functions %} + {{ function.id }} +{% endfor %} + + +{% endif %} +{% endblock %} +{% endif %} +{% for obj_item in visible_children %} +{% if obj.all is none or obj_item.short_name in obj.all %} +{{ obj_item.rendered|indent(0) }} +{% endif %} +{% endfor %} +{% endif %} +{% endblock %} From d55ea1a98224d13b997059914ec4763af1319a44 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Sun, 28 Apr 2019 01:09:48 +1000 Subject: [PATCH 12/13] PEP fixes --- InvenTree/InvenTree/__init__.py | 2 +- InvenTree/InvenTree/forms.py | 2 +- InvenTree/InvenTree/helpers.py | 2 +- InvenTree/InvenTree/models.py | 3 +-- InvenTree/InvenTree/serializers.py | 2 +- InvenTree/InvenTree/views.py | 6 +++--- InvenTree/build/__init__.py | 2 +- InvenTree/build/models.py | 1 - InvenTree/company/__init__.py | 2 +- InvenTree/company/serializers.py | 2 +- InvenTree/company/views.py | 2 +- InvenTree/part/__init__.py | 2 +- InvenTree/part/api.py | 2 +- InvenTree/stock/__init__.py | 2 +- 14 files changed, 15 insertions(+), 17 deletions(-) diff --git a/InvenTree/InvenTree/__init__.py b/InvenTree/InvenTree/__init__.py index f02ada5664..521e0a60fc 100644 --- a/InvenTree/InvenTree/__init__.py +++ b/InvenTree/InvenTree/__init__.py @@ -2,4 +2,4 @@ The InvenTree module provides high-level management and functionality. It provides a number of helper functions and generic classes which are used by InvenTree apps. -""" \ No newline at end of file +""" diff --git a/InvenTree/InvenTree/forms.py b/InvenTree/InvenTree/forms.py index 8d574f7a38..ba203b2f1b 100644 --- a/InvenTree/InvenTree/forms.py +++ b/InvenTree/InvenTree/forms.py @@ -1,4 +1,4 @@ -""" +""" Helper forms which subclass Django forms to provide additional functionality """ diff --git a/InvenTree/InvenTree/helpers.py b/InvenTree/InvenTree/helpers.py index 7b8f1b57c1..295937af08 100644 --- a/InvenTree/InvenTree/helpers.py +++ b/InvenTree/InvenTree/helpers.py @@ -9,7 +9,7 @@ from django.http import StreamingHttpResponse def str2bool(text, test=True): - """ Test if a string 'looks' like a boolean value. + """ Test if a string 'looks' like a boolean value. Args: text: Input text diff --git a/InvenTree/InvenTree/models.py b/InvenTree/InvenTree/models.py index 0c1e1f10ee..61fb50831b 100644 --- a/InvenTree/InvenTree/models.py +++ b/InvenTree/InvenTree/models.py @@ -7,7 +7,6 @@ from __future__ import unicode_literals from django.db import models from django.contrib.contenttypes.models import ContentType from rest_framework.exceptions import ValidationError -from .helpers import str2bool from django.db.models.signals import pre_delete from django.dispatch import receiver @@ -125,7 +124,7 @@ class InvenTreeTree(models.Model): e.g. ["Top", "Second", "Third", "This"] Returns: - List of category names from the top level to this category + List of category names from the top level to this category """ return self.parentpath + [self] diff --git a/InvenTree/InvenTree/serializers.py b/InvenTree/InvenTree/serializers.py index 77e38ade21..7149668cc4 100644 --- a/InvenTree/InvenTree/serializers.py +++ b/InvenTree/InvenTree/serializers.py @@ -1,4 +1,4 @@ -""" +""" Serializers used in various InvenTree apps """ diff --git a/InvenTree/InvenTree/views.py b/InvenTree/InvenTree/views.py index fee5d2481f..551f624cb9 100644 --- a/InvenTree/InvenTree/views.py +++ b/InvenTree/InvenTree/views.py @@ -1,4 +1,4 @@ -""" +""" Various Views which provide extra functionality over base Django Views. In particular these views provide base functionality for rendering Django forms @@ -20,7 +20,7 @@ from rest_framework import views class TreeSerializer(views.APIView): """ JSON View for serializing a Tree object. - """ + """ def itemToJson(self, item): @@ -259,7 +259,7 @@ class IndexView(TemplateView): class SearchView(TemplateView): - """ View for InvenTree search page. + """ View for InvenTree search page. Displays results of search query """ diff --git a/InvenTree/build/__init__.py b/InvenTree/build/__init__.py index 17cba16fc7..747a20f837 100644 --- a/InvenTree/build/__init__.py +++ b/InvenTree/build/__init__.py @@ -2,4 +2,4 @@ The Build module is responsible for managing "Build" transactions. A Build consumes parts from stock to create new parts -""" \ No newline at end of file +""" diff --git a/InvenTree/build/models.py b/InvenTree/build/models.py index 2af1077205..880c1b08b1 100644 --- a/InvenTree/build/models.py +++ b/InvenTree/build/models.py @@ -55,7 +55,6 @@ class Build(models.Model): batch = models.CharField(max_length=100, blank=True, null=True, help_text='Batch code for this build output') - #: Date the build model was 'created' creation_date = models.DateField(auto_now=True, editable=False) diff --git a/InvenTree/company/__init__.py b/InvenTree/company/__init__.py index c662aee7f8..6c89578ac3 100644 --- a/InvenTree/company/__init__.py +++ b/InvenTree/company/__init__.py @@ -5,4 +5,4 @@ A company can be either (or both): - Supplier - Customer -""" \ No newline at end of file +""" diff --git a/InvenTree/company/serializers.py b/InvenTree/company/serializers.py index e9a24e0d2a..88792f7f64 100644 --- a/InvenTree/company/serializers.py +++ b/InvenTree/company/serializers.py @@ -1,4 +1,4 @@ -""" +""" JSON serializers for Company app """ diff --git a/InvenTree/company/views.py b/InvenTree/company/views.py index b171d83f86..f61da1fffa 100644 --- a/InvenTree/company/views.py +++ b/InvenTree/company/views.py @@ -17,7 +17,7 @@ from .forms import CompanyImageForm class CompanyIndex(ListView): - """ View for displaying list of companies + """ View for displaying list of companies """ model = Company diff --git a/InvenTree/part/__init__.py b/InvenTree/part/__init__.py index 8fad2c5f19..da73be7753 100644 --- a/InvenTree/part/__init__.py +++ b/InvenTree/part/__init__.py @@ -7,4 +7,4 @@ It includes models for: - Part - SupplierPart - BomItem -""" \ No newline at end of file +""" diff --git a/InvenTree/part/api.py b/InvenTree/part/api.py index d9ee5e5566..8f93cede7c 100644 --- a/InvenTree/part/api.py +++ b/InvenTree/part/api.py @@ -226,7 +226,7 @@ class SupplierPartDetail(generics.RetrieveUpdateDestroyAPIView): class SupplierPriceBreakList(generics.ListCreateAPIView): - """ API endpoint for list view of SupplierPriceBreak object + """ API endpoint for list view of SupplierPriceBreak object - GET: Retrieve list of SupplierPriceBreak objects - POST: Create a new SupplierPriceBreak object diff --git a/InvenTree/stock/__init__.py b/InvenTree/stock/__init__.py index 7b58c08fc9..6970329be1 100644 --- a/InvenTree/stock/__init__.py +++ b/InvenTree/stock/__init__.py @@ -6,4 +6,4 @@ It includes models for: - StockLocation - StockItem - StockItemTracking -""" \ No newline at end of file +""" From e512bc6536aed37774a1538585acb3ebcc76f2b8 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Sun, 28 Apr 2019 01:17:28 +1000 Subject: [PATCH 13/13] Fix for autoapi-dirs --- docs/conf.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index d1b8f1c5b8..b02b70a41d 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -38,12 +38,6 @@ napoleon_numpy_docstring = False autoapi_dirs = [ '../InvenTree', - '../Inventree/build', - '../InvenTree/company', - '../InvenTree/InvenTree', - '../InvenTree/part', - '../InvenTree/stock', - '../InvenTree/users', ] autoapi_options = [