Merge remote-tracking branch 'inventree/master'

This commit is contained in:
Oliver Walters committed 2022-12-11 23:07:49 +11:00
commit 994124e816
36 files changed
+10995 -11033

No files matched your search

+2 -2
View File
@@ -48,7 +48,7 @@ jobs:
- name: Build Docker Image - name: Build Docker Image
# Build the development docker image (using docker-compose.yml) # Build the development docker image (using docker-compose.yml)
run: | run: |
docker-compose build docker-compose build --no-cache
- name: Update Docker Image - name: Update Docker Image
run: | run: |
docker-compose run inventree-dev-server invoke update docker-compose run inventree-dev-server invoke update
@@ -69,7 +69,7 @@ jobs:
test -f data/secret_key.txt test -f data/secret_key.txt
- name: Run Unit Tests - name: Run Unit Tests
run: | run: |
docker-compose run inventree-dev-server invoke test docker-compose run inventree-dev-server invoke test --disable-pty
docker-compose down docker-compose down
- name: Set up QEMU - name: Set up QEMU
if: github.event_name != 'pull_request' if: github.event_name != 'pull_request'
+1 -74
View File
@@ -18,8 +18,7 @@ from django.urls import reverse_lazy
from django.utils.timezone import now from django.utils.timezone import now
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from django.views import View from django.views import View
from django.views.generic import (CreateView, DeleteView, DetailView, ListView, from django.views.generic import DeleteView, DetailView, ListView, UpdateView
UpdateView)
from django.views.generic.base import RedirectView, TemplateView from django.views.generic.base import RedirectView, TemplateView
from allauth.account.forms import AddEmailForm from allauth.account.forms import AddEmailForm
@@ -185,7 +184,6 @@ class InvenTreeRoleMixin(PermissionRequiredMixin):
UpdateView: 'change', UpdateView: 'change',
DeleteView: 'delete', DeleteView: 'delete',
AjaxUpdateView: 'change', AjaxUpdateView: 'change',
AjaxCreateView: 'add',
} }
for view_class in permission_map.keys(): for view_class in permission_map.keys():
@@ -360,77 +358,6 @@ class QRCodeView(AjaxView):
return context return context
class AjaxCreateView(AjaxMixin, CreateView):
"""An 'AJAXified' CreateView for creating a new object in the db.
- Returns a form in JSON format (for delivery to a modal window)
- Handles form validation via AJAX POST requests
"""
def get(self, request, *args, **kwargs):
"""Creates form with initial data, and renders JSON response."""
super(CreateView, self).get(request, *args, **kwargs)
self.request = request
form = self.get_form()
return self.renderJsonResponse(request, form)
def save(self, form):
"""Method for actually saving the form to the database.
Default implementation is very simple, but can be overridden if required.
"""
self.object = form.save()
return self.object
def post(self, request, *args, **kwargs):
"""Responds to form POST. Validates POST data and returns status info.
- Validate POST form data
- If valid, save form
- Return status info (success / failure)
"""
self.request = request
self.form = self.get_form()
# Perform initial form validation
self.form.is_valid()
# Perform custom validation (no object can be provided yet)
self.validate(None, self.form)
valid = self.form.is_valid()
# Extra JSON data sent alongside form
data = {
'form_valid': valid,
'form_errors': self.form.errors.as_json(),
'non_field_errors': self.form.non_field_errors().as_json(),
}
# Add in any extra class data
for value, key in enumerate(self.get_data()):
data[key] = value
if valid:
# Save the object to the database
self.object = self.save(self.form)
if self.object:
# Return the PK of the newly-created object
data['pk'] = self.object.pk
data['text'] = str(self.object)
try:
data['url'] = self.object.get_absolute_url()
except AttributeError:
pass
return self.renderJsonResponse(request, self.form, data)
class AjaxUpdateView(AjaxMixin, UpdateView): class AjaxUpdateView(AjaxMixin, UpdateView):
"""An 'AJAXified' UpdateView for updating an object in the db. """An 'AJAXified' UpdateView for updating an object in the db.
+5
View File
@@ -5,6 +5,7 @@ from datetime import datetime, timedelta
from django.conf import settings from django.conf import settings
from django.core.exceptions import AppRegistryNotReady from django.core.exceptions import AppRegistryNotReady
from django.db.utils import IntegrityError, OperationalError
import feedparser import feedparser
@@ -57,6 +58,7 @@ def update_news_feed():
continue continue
# Create entry # Create entry
try:
NewsFeedEntry.objects.create( NewsFeedEntry.objects.create(
feed_id=entry.id, feed_id=entry.id,
title=entry.title, title=entry.title,
@@ -65,5 +67,8 @@ def update_news_feed():
author=entry.author, author=entry.author,
summary=entry.summary, summary=entry.summary,
) )
except (IntegrityError, OperationalError):
# Sometimes errors-out on database start-up
pass
logger.info('update_news_feed: Sync done') logger.info('update_news_feed: Sync done')
+8 -5
View File
@@ -10,6 +10,7 @@ from pathlib import Path
from django.apps import AppConfig from django.apps import AppConfig
from django.conf import settings from django.conf import settings
from django.core.exceptions import AppRegistryNotReady from django.core.exceptions import AppRegistryNotReady
from django.db.utils import OperationalError
from InvenTree.ready import canAppAccessDatabase from InvenTree.ready import canAppAccessDatabase
@@ -35,18 +36,18 @@ class LabelConfig(AppConfig):
def ready(self): def ready(self):
"""This function is called whenever the label app is loaded.""" """This function is called whenever the label app is loaded."""
if canAppAccessDatabase(): if canAppAccessDatabase():
try:
self.create_labels() # pragma: no cover self.create_labels() # pragma: no cover
except (AppRegistryNotReady, OperationalError):
# Database might not yet be ready
warnings.warn('Database was not ready for creating labels')
def create_labels(self): def create_labels(self):
"""Create all default templates.""" """Create all default templates."""
# Test if models are ready # Test if models are ready
try:
from .models import PartLabel, StockItemLabel, StockLocationLabel from .models import PartLabel, StockItemLabel, StockLocationLabel
assert bool(StockLocationLabel is not None) assert bool(StockLocationLabel is not None)
except AppRegistryNotReady: # pragma: no cover
# Database might not yet be ready
warnings.warn('Database was not ready for creating labels')
return
# Create the categories # Create the categories
self.create_labels_category( self.create_labels_category(
@@ -62,6 +63,7 @@ class LabelConfig(AppConfig):
}, },
], ],
) )
self.create_labels_category( self.create_labels_category(
StockLocationLabel, StockLocationLabel,
'stocklocation', 'stocklocation',
@@ -82,6 +84,7 @@ class LabelConfig(AppConfig):
} }
] ]
) )
self.create_labels_category( self.create_labels_category(
PartLabel, PartLabel,
'part', 'part',
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+1 -3
View File
@@ -1,6 +1,5 @@
"""JSON API for the plugin app.""" """API for the plugin app."""
from django.conf import settings
from django.urls import include, re_path from django.urls import include, re_path
from django_filters.rest_framework import DjangoFilterBackend from django_filters.rest_framework import DjangoFilterBackend
@@ -238,7 +237,6 @@ general_plugin_api_urls = [
re_path(r'^.*$', PluginList.as_view(), name='api-plugin-list'), re_path(r'^.*$', PluginList.as_view(), name='api-plugin-list'),
] ]
if settings.PLUGINS_ENABLED:
plugin_api_urls.append( plugin_api_urls.append(
re_path(r'^plugin/', include(general_plugin_api_urls)) re_path(r'^plugin/', include(general_plugin_api_urls))
) )
+14 -7
View File
@@ -45,6 +45,18 @@ function createNewModal(options={}) {
var submitClass = options.submitClass || 'primary'; var submitClass = options.submitClass || 'primary';
var buttons = '';
// Add in a "close" button
if (!options.hideCloseButton) {
buttons += `<button type='button' class='btn btn-secondary' id='modal-form-close' data-bs-dismiss='modal'>{% trans "Cancel" %}</button>`;
}
// Add in a "submit" button
if (!options.hideSubmitButton) {
buttons += `<button type='button' class='btn btn-${submitClass}' id='modal-form-submit'>{% trans "Submit" %}</button>`;
}
var html = ` var html = `
<div class='modal fade modal-fixed-footer modal-primary inventree-modal' role='dialog' id='modal-form-${id}' tabindex='-1'> <div class='modal fade modal-fixed-footer modal-primary inventree-modal' role='dialog' id='modal-form-${id}' tabindex='-1'>
<div class='modal-dialog'> <div class='modal-dialog'>
@@ -79,8 +91,7 @@ function createNewModal(options={}) {
<div id='modal-footer-secondary-buttons'> <div id='modal-footer-secondary-buttons'>
<!-- Extra secondary buttons can be inserted here --> <!-- Extra secondary buttons can be inserted here -->
</div> </div>
<button type='button' class='btn btn-secondary' id='modal-form-close' data-bs-dismiss='modal'>{% trans "Cancel" %}</button> ${buttons}
<button type='button' class='btn btn-${submitClass}' id='modal-form-submit'>{% trans "Submit" %}</button>
</div> </div>
</div> </div>
</div> </div>
@@ -102,11 +113,7 @@ function createNewModal(options={}) {
// Steal keyboard focus // Steal keyboard focus
$(modal_name).focus(); $(modal_name).focus();
if (options.hideCloseButton) { if (options.preventSubmit) {
$(modal_name).find('#modal-form-close').hide();
}
if (options.preventSubmit || options.hideSubmitButton) {
$(modal_name).find('#modal-form-submit').hide(); $(modal_name).find('#modal-form-submit').hide();
} }
+1 -1
View File
@@ -1213,7 +1213,7 @@ function orderParts(parts_list, options={}) {
constructFormBody({}, { constructFormBody({}, {
preFormContent: html, preFormContent: html,
title: '{% trans "Order Parts" %}', title: '{% trans "Order Parts" %}',
preventSubmit: true, hideSubmitButton: true,
closeText: '{% trans "Close" %}', closeText: '{% trans "Close" %}',
afterRender: function(fields, opts) { afterRender: function(fields, opts) {
parts.forEach(function(part) { parts.forEach(function(part) {
+11
View File
@@ -4,7 +4,18 @@ import logging
import multiprocessing import multiprocessing
import os import os
# Logger configuration
logger = logging.getLogger('inventree') logger = logging.getLogger('inventree')
accesslog = '-'
errorlog = '-'
loglevel = os.environ.get('INVENTREE_LOG_LEVEL', 'warning').lower()
capture_output = True
# Worker configuration
# TODO: Implement support for gevent
# worker_class = 'gevent' # Allow multi-threading support
worker_tmp_dir = '/dev/shm' # Write temp file to RAM (faster)
threads = 4
workers = os.environ.get('INVENTREE_GUNICORN_WORKERS', None) workers = os.environ.get('INVENTREE_GUNICORN_WORKERS', None)
+1 -1
View File
@@ -20,7 +20,7 @@ fi
# Check if "config.yaml" has been copied into the correct location # Check if "config.yaml" has been copied into the correct location
if test -f "$INVENTREE_CONFIG_FILE"; then if test -f "$INVENTREE_CONFIG_FILE"; then
echo "$INVENTREE_CONFIG_FILE exists - skipping" echo "Loading config file : $INVENTREE_CONFIG_FILE"
else else
echo "Copying config file to $INVENTREE_CONFIG_FILE" echo "Copying config file to $INVENTREE_CONFIG_FILE"
cp $INVENTREE_HOME/InvenTree/config_template.yaml $INVENTREE_CONFIG_FILE cp $INVENTREE_HOME/InvenTree/config_template.yaml $INVENTREE_CONFIG_FILE
+5 -3
View File
@@ -534,13 +534,15 @@ def test_translations(c):
@task @task
def test(c, database=None): def test(c, disable_pty=False):
"""Run unit-tests for InvenTree codebase.""" """Run unit-tests for InvenTree codebase."""
# Run sanity check on the django install # Run sanity check on the django install
manage(c, 'check') manage(c, 'check')
pty = not disable_pty
# Run coverage tests # Run coverage tests
manage(c, 'test', pty=True) manage(c, 'test', pty=pty)
@task(help={'dev': 'Set up development environment at the end'}) @task(help={'dev': 'Set up development environment at the end'})
@@ -577,7 +579,7 @@ def setup_test(c, ignore_update=False, dev=False, path="inventree-demo-dataset")
shutil.copytree(src, dst, dirs_exist_ok=True) shutil.copytree(src, dst, dirs_exist_ok=True)
print("Done setting up test enviroment...") print("Done setting up test environment...")
print("========================================") print("========================================")
# Set up development setup if flag is set # Set up development setup if flag is set