mirror of
				https://github.com/inventree/InvenTree.git
				synced 2025-10-30 20:55:42 +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