mirror of
https://github.com/inventree/InvenTree.git
synced 2025-07-17 18:26:32 +00:00
Refactor label/report template copying (#6582)
* [BUG] Inventree fiddles with files directly rather than using Django Storage api Fixes #2585 * PEP fix * clean diff * move template discovery into central location * more moving file operations * fix paths * and another path fixing * more fixes * fix typing * switch config back to local * revert locale stats * add s3 support * storages * more adaptions * use s3 switch to set storage backend * fix reqs * cleanup default_storage * init in storage_backend * move to storage classes everywhere * fix call * remove more S3 references * move storage init * fix startup error * alsways use url * ignore FileExistsError * move s3 required url in * remove S3 for now * use Djangos defaults * fix old import * remove default_storage calls * make labels/reports more similar * expand functions out * refactor to use refs where possible * refactor copy section to be similar * unify db lookup * move shared code to generic section * move ready out * docstrings * move even more functions out * move references inline of the classes * clean up refs * fix init * fix ensure dir * remove unneeded tries * cleanup diff * more cleanup * fix tests * use SUBDIR
This commit is contained in:
@@ -1,69 +1,31 @@
|
||||
"""label app specification."""
|
||||
"""Config options for the label app."""
|
||||
|
||||
import hashlib
|
||||
import logging
|
||||
import os
|
||||
import shutil
|
||||
import warnings
|
||||
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 IntegrityError, OperationalError, ProgrammingError
|
||||
|
||||
from maintenance_mode.core import maintenance_mode_on, set_maintenance_mode
|
||||
|
||||
import InvenTree.helpers
|
||||
import InvenTree.ready
|
||||
|
||||
logger = logging.getLogger('inventree')
|
||||
from generic.templating.apps import TemplatingMixin
|
||||
|
||||
|
||||
class LabelConfig(AppConfig):
|
||||
"""App configuration class for the 'label' app."""
|
||||
class LabelConfig(TemplatingMixin, AppConfig):
|
||||
"""Configuration class for the "label" app."""
|
||||
|
||||
name = 'label'
|
||||
db = 'label'
|
||||
|
||||
def ready(self):
|
||||
"""This function is called whenever the label app is loaded."""
|
||||
# skip loading if plugin registry is not loaded or we run in a background thread
|
||||
if (
|
||||
not InvenTree.ready.isPluginRegistryLoaded()
|
||||
or not InvenTree.ready.isInMainThread()
|
||||
):
|
||||
return
|
||||
|
||||
if not InvenTree.ready.canAppAccessDatabase(allow_test=False):
|
||||
return # pragma: no cover
|
||||
|
||||
with maintenance_mode_on():
|
||||
try:
|
||||
self.create_labels() # pragma: no cover
|
||||
except (
|
||||
AppRegistryNotReady,
|
||||
IntegrityError,
|
||||
OperationalError,
|
||||
ProgrammingError,
|
||||
):
|
||||
# Database might not yet be ready
|
||||
warnings.warn(
|
||||
'Database was not ready for creating labels', stacklevel=2
|
||||
)
|
||||
|
||||
set_maintenance_mode(False)
|
||||
|
||||
def create_labels(self):
|
||||
def create_defaults(self):
|
||||
"""Create all default templates."""
|
||||
# Test if models are ready
|
||||
import label.models
|
||||
|
||||
try:
|
||||
import label.models
|
||||
except Exception: # pragma: no cover
|
||||
# Database is not ready yet
|
||||
return
|
||||
assert bool(label.models.StockLocationLabel is not None)
|
||||
|
||||
# Create the categories
|
||||
self.create_labels_category(
|
||||
self.create_template_dir(
|
||||
label.models.StockItemLabel,
|
||||
'stockitem',
|
||||
[
|
||||
{
|
||||
'file': 'qr.html',
|
||||
@@ -75,9 +37,8 @@ class LabelConfig(AppConfig):
|
||||
],
|
||||
)
|
||||
|
||||
self.create_labels_category(
|
||||
self.create_template_dir(
|
||||
label.models.StockLocationLabel,
|
||||
'stocklocation',
|
||||
[
|
||||
{
|
||||
'file': 'qr.html',
|
||||
@@ -96,9 +57,8 @@ class LabelConfig(AppConfig):
|
||||
],
|
||||
)
|
||||
|
||||
self.create_labels_category(
|
||||
self.create_template_dir(
|
||||
label.models.PartLabel,
|
||||
'part',
|
||||
[
|
||||
{
|
||||
'file': 'part_label.html',
|
||||
@@ -117,9 +77,8 @@ class LabelConfig(AppConfig):
|
||||
],
|
||||
)
|
||||
|
||||
self.create_labels_category(
|
||||
self.create_template_dir(
|
||||
label.models.BuildLineLabel,
|
||||
'buildline',
|
||||
[
|
||||
{
|
||||
'file': 'buildline_label.html',
|
||||
@@ -131,72 +90,18 @@ class LabelConfig(AppConfig):
|
||||
],
|
||||
)
|
||||
|
||||
def create_labels_category(self, model, ref_name, labels):
|
||||
"""Create folder and database entries for the default templates, if they do not already exist."""
|
||||
# Create root dir for templates
|
||||
src_dir = Path(__file__).parent.joinpath('templates', 'label', ref_name)
|
||||
def get_src_dir(self, ref_name):
|
||||
"""Get the source directory."""
|
||||
return Path(__file__).parent.joinpath('templates', self.name, ref_name)
|
||||
|
||||
dst_dir = settings.MEDIA_ROOT.joinpath('label', 'inventree', ref_name)
|
||||
|
||||
if not dst_dir.exists():
|
||||
logger.info("Creating required directory: '%s'", dst_dir)
|
||||
dst_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# Create labels
|
||||
for label in labels:
|
||||
self.create_template_label(model, src_dir, ref_name, label)
|
||||
|
||||
def create_template_label(self, model, src_dir, ref_name, label):
|
||||
"""Ensure a label template is in place."""
|
||||
filename = os.path.join('label', 'inventree', ref_name, label['file'])
|
||||
|
||||
src_file = src_dir.joinpath(label['file'])
|
||||
dst_file = settings.MEDIA_ROOT.joinpath(filename)
|
||||
|
||||
to_copy = False
|
||||
|
||||
if dst_file.exists():
|
||||
# File already exists - let's see if it is the "same"
|
||||
|
||||
if InvenTree.helpers.hash_file(dst_file) != InvenTree.helpers.hash_file(
|
||||
src_file
|
||||
): # pragma: no cover
|
||||
logger.info("Hash differs for '%s'", filename)
|
||||
to_copy = True
|
||||
|
||||
else:
|
||||
logger.info("Label template '%s' is not present", filename)
|
||||
to_copy = True
|
||||
|
||||
if to_copy:
|
||||
logger.info("Copying label template '%s'", dst_file)
|
||||
# Ensure destination dir exists
|
||||
dst_file.parent.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# Copy file
|
||||
shutil.copyfile(src_file, dst_file)
|
||||
|
||||
# Check if a label matching the template already exists
|
||||
try:
|
||||
if model.objects.filter(label=filename).exists():
|
||||
return # pragma: no cover
|
||||
except Exception:
|
||||
logger.exception(
|
||||
"Failed to query label for '%s' - you should run 'invoke update' first!",
|
||||
filename,
|
||||
)
|
||||
|
||||
logger.info("Creating entry for %s '%s'", model, label['name'])
|
||||
|
||||
try:
|
||||
model.objects.create(
|
||||
name=label['name'],
|
||||
description=label['description'],
|
||||
label=filename,
|
||||
filters='',
|
||||
enabled=True,
|
||||
width=label['width'],
|
||||
height=label['height'],
|
||||
)
|
||||
except Exception:
|
||||
logger.warning("Failed to create label '%s'", label['name'])
|
||||
def get_new_obj_data(self, data, filename):
|
||||
"""Get the data for a new template db object."""
|
||||
return {
|
||||
'name': data['name'],
|
||||
'description': data['description'],
|
||||
'label': filename,
|
||||
'filters': '',
|
||||
'enabled': True,
|
||||
'width': data['width'],
|
||||
'height': data['height'],
|
||||
}
|
||||
|
@@ -96,8 +96,13 @@ class LabelTemplate(InvenTree.models.InvenTreeMetadataModel):
|
||||
|
||||
abstract = True
|
||||
|
||||
@classmethod
|
||||
def getSubdir(cls) -> str:
|
||||
"""Return the subdirectory for this label."""
|
||||
return cls.SUBDIR
|
||||
|
||||
# Each class of label files will be stored in a separate subdirectory
|
||||
SUBDIR = 'label'
|
||||
SUBDIR: str = 'label'
|
||||
|
||||
# Object we will be printing against (will be filled out later)
|
||||
object_to_print = None
|
||||
|
@@ -30,7 +30,7 @@ class LabelTest(InvenTreeAPITestCase):
|
||||
def setUpTestData(cls):
|
||||
"""Ensure that some label instances exist as part of init routine."""
|
||||
super().setUpTestData()
|
||||
apps.get_app_config('label').create_labels()
|
||||
apps.get_app_config('label').create_defaults()
|
||||
|
||||
def test_default_labels(self):
|
||||
"""Test that the default label templates are copied across."""
|
||||
|
Reference in New Issue
Block a user