Merge remote-tracking branch 'inventree/master'

This commit is contained in:
Oliver Walters 2022-12-11 23:07:49 +11:00
commit 994124e816
36 changed files with 11009 additions and 11047 deletions

View File

@ -48,7 +48,7 @@ jobs:
- name: Build Docker Image
# Build the development docker image (using docker-compose.yml)
run: |
docker-compose build
docker-compose build --no-cache
- name: Update Docker Image
run: |
docker-compose run inventree-dev-server invoke update
@ -69,7 +69,7 @@ jobs:
test -f data/secret_key.txt
- name: Run Unit Tests
run: |
docker-compose run inventree-dev-server invoke test
docker-compose run inventree-dev-server invoke test --disable-pty
docker-compose down
- name: Set up QEMU
if: github.event_name != 'pull_request'

View File

@ -18,8 +18,7 @@ from django.urls import reverse_lazy
from django.utils.timezone import now
from django.utils.translation import gettext_lazy as _
from django.views import View
from django.views.generic import (CreateView, DeleteView, DetailView, ListView,
UpdateView)
from django.views.generic import DeleteView, DetailView, ListView, UpdateView
from django.views.generic.base import RedirectView, TemplateView
from allauth.account.forms import AddEmailForm
@ -185,7 +184,6 @@ class InvenTreeRoleMixin(PermissionRequiredMixin):
UpdateView: 'change',
DeleteView: 'delete',
AjaxUpdateView: 'change',
AjaxCreateView: 'add',
}
for view_class in permission_map.keys():
@ -360,77 +358,6 @@ class QRCodeView(AjaxView):
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):
"""An 'AJAXified' UpdateView for updating an object in the db.

View File

@ -5,6 +5,7 @@ from datetime import datetime, timedelta
from django.conf import settings
from django.core.exceptions import AppRegistryNotReady
from django.db.utils import IntegrityError, OperationalError
import feedparser
@ -57,13 +58,17 @@ def update_news_feed():
continue
# Create entry
NewsFeedEntry.objects.create(
feed_id=entry.id,
title=entry.title,
link=entry.link,
published=entry.published,
author=entry.author,
summary=entry.summary,
)
try:
NewsFeedEntry.objects.create(
feed_id=entry.id,
title=entry.title,
link=entry.link,
published=entry.published,
author=entry.author,
summary=entry.summary,
)
except (IntegrityError, OperationalError):
# Sometimes errors-out on database start-up
pass
logger.info('update_news_feed: Sync done')

View File

@ -10,6 +10,7 @@ from pathlib import Path
from django.apps import AppConfig
from django.conf import settings
from django.core.exceptions import AppRegistryNotReady
from django.db.utils import OperationalError
from InvenTree.ready import canAppAccessDatabase
@ -35,18 +36,18 @@ class LabelConfig(AppConfig):
def ready(self):
"""This function is called whenever the label app is loaded."""
if canAppAccessDatabase():
self.create_labels() # pragma: no cover
try:
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):
"""Create all default templates."""
# Test if models are ready
try:
from .models import PartLabel, StockItemLabel, StockLocationLabel
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
from .models import PartLabel, StockItemLabel, StockLocationLabel
assert bool(StockLocationLabel is not None)
# Create the categories
self.create_labels_category(
@ -62,6 +63,7 @@ class LabelConfig(AppConfig):
},
],
)
self.create_labels_category(
StockLocationLabel,
'stocklocation',
@ -82,6 +84,7 @@ class LabelConfig(AppConfig):
}
]
)
self.create_labels_category(
PartLabel,
'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

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_filters.rest_framework import DjangoFilterBackend
@ -238,7 +237,6 @@ general_plugin_api_urls = [
re_path(r'^.*$', PluginList.as_view(), name='api-plugin-list'),
]
if settings.PLUGINS_ENABLED:
plugin_api_urls.append(
re_path(r'^plugin/', include(general_plugin_api_urls))
)
plugin_api_urls.append(
re_path(r'^plugin/', include(general_plugin_api_urls))
)

View File

@ -45,6 +45,18 @@ function createNewModal(options={}) {
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 = `
<div class='modal fade modal-fixed-footer modal-primary inventree-modal' role='dialog' id='modal-form-${id}' tabindex='-1'>
<div class='modal-dialog'>
@ -79,8 +91,7 @@ function createNewModal(options={}) {
<div id='modal-footer-secondary-buttons'>
<!-- Extra secondary buttons can be inserted here -->
</div>
<button type='button' class='btn btn-secondary' id='modal-form-close' data-bs-dismiss='modal'>{% trans "Cancel" %}</button>
<button type='button' class='btn btn-${submitClass}' id='modal-form-submit'>{% trans "Submit" %}</button>
${buttons}
</div>
</div>
</div>
@ -102,11 +113,7 @@ function createNewModal(options={}) {
// Steal keyboard focus
$(modal_name).focus();
if (options.hideCloseButton) {
$(modal_name).find('#modal-form-close').hide();
}
if (options.preventSubmit || options.hideSubmitButton) {
if (options.preventSubmit) {
$(modal_name).find('#modal-form-submit').hide();
}

View File

@ -1213,7 +1213,7 @@ function orderParts(parts_list, options={}) {
constructFormBody({}, {
preFormContent: html,
title: '{% trans "Order Parts" %}',
preventSubmit: true,
hideSubmitButton: true,
closeText: '{% trans "Close" %}',
afterRender: function(fields, opts) {
parts.forEach(function(part) {

View File

@ -4,7 +4,18 @@ import logging
import multiprocessing
import os
# Logger configuration
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)

View File

@ -20,7 +20,7 @@ fi
# Check if "config.yaml" has been copied into the correct location
if test -f "$INVENTREE_CONFIG_FILE"; then
echo "$INVENTREE_CONFIG_FILE exists - skipping"
echo "Loading config file : $INVENTREE_CONFIG_FILE"
else
echo "Copying config file to $INVENTREE_CONFIG_FILE"
cp $INVENTREE_HOME/InvenTree/config_template.yaml $INVENTREE_CONFIG_FILE

View File

@ -534,13 +534,15 @@ def test_translations(c):
@task
def test(c, database=None):
def test(c, disable_pty=False):
"""Run unit-tests for InvenTree codebase."""
# Run sanity check on the django install
manage(c, 'check')
pty = not disable_pty
# Run coverage tests
manage(c, 'test', pty=True)
manage(c, 'test', pty=pty)
@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)
print("Done setting up test enviroment...")
print("Done setting up test environment...")
print("========================================")
# Set up development setup if flag is set