Move "rebuild_thumbnails" to being a management command

- Does not run every time the server reboots
- Has to be called manually
- Normally does not need to be run and is a long-running process!
This commit is contained in:
Oliver 2021-10-05 08:05:26 +11:00
parent 17df4ca91e
commit 0c04bfaa85
4 changed files with 83 additions and 63 deletions

View File

@ -0,0 +1,70 @@
"""
Custom management command to rebuild thumbnail images
- May be required after importing a new dataset, for example
"""
import os
import logging
from PIL import UnidentifiedImageError
from django.core.management.base import BaseCommand
from django.conf import settings
from django.db.utils import OperationalError, ProgrammingError
from company.models import Company
from part.models import Part
logger = logging.getLogger("inventree-thumbnails")
class Command(BaseCommand):
"""
Rebuild all thumbnail images
"""
def rebuild_thumbnail(self, model):
"""
Rebuild the thumbnail specified by the "image" field of the provided model
"""
if not model.image:
return
img = model.image
url = img.thumbnail.name
loc = os.path.join(settings.MEDIA_ROOT, url)
if not os.path.exists(loc):
logger.info(f"Generating thumbnail image for '{img}'")
try:
model.image.render_variations(replace=False)
except FileNotFoundError:
logger.error(f"ERROR: Image file '{img}' is missing")
except UnidentifiedImageError:
logger.error(f"ERROR: Image file '{img}' is not a valid image")
def handle(self, *args, **kwargs):
logger.setLevel(logging.INFO)
logger.info("Rebuilding Part thumbnails")
for part in Part.objects.exclude(image=None):
try:
self.rebuild_thumbnail(part)
except (OperationalError, ProgrammingError):
logger.error("ERROR: Database read error.")
break
logger.info("Rebuilding Company thumbnails")
for company in Company.objects.exclude(image=None):
try:
self.rebuild_thumbnail(company)
except (OperationalError, ProgrammingError):
logger.error("ERROR: abase read error.")
break

View File

@ -23,29 +23,4 @@ class CompanyConfig(AppConfig):
This function is called whenever the Company app is loaded. This function is called whenever the Company app is loaded.
""" """
if canAppAccessDatabase(): pass
self.generate_company_thumbs()
def generate_company_thumbs(self):
from .models import Company
logger.debug("Checking Company image thumbnails")
try:
for company in Company.objects.all():
if company.image:
url = company.image.thumbnail.name
loc = os.path.join(settings.MEDIA_ROOT, url)
if not os.path.exists(loc):
logger.info("InvenTree: Generating thumbnail for Company '{c}'".format(c=company.name))
try:
company.image.render_variations(replace=False)
except FileNotFoundError:
logger.warning(f"Image file '{company.image}' missing")
except UnidentifiedImageError:
logger.warning(f"Image file '{company.image}' is invalid")
except (OperationalError, ProgrammingError):
# Getting here probably meant the database was in test mode
pass

View File

@ -24,40 +24,8 @@ class PartConfig(AppConfig):
""" """
if canAppAccessDatabase(): if canAppAccessDatabase():
self.generate_part_thumbnails()
self.update_trackable_status() self.update_trackable_status()
def generate_part_thumbnails(self):
"""
Generate thumbnail images for any Part that does not have one.
This function exists mainly for legacy support,
as any *new* image uploaded will have a thumbnail generated automatically.
"""
from .models import Part
logger.debug("InvenTree: Checking Part image thumbnails")
try:
# Only check parts which have images
for part in Part.objects.exclude(image=None):
if part.image:
url = part.image.thumbnail.name
loc = os.path.join(settings.MEDIA_ROOT, url)
if not os.path.exists(loc):
logger.info("InvenTree: Generating thumbnail for Part '{p}'".format(p=part.name))
try:
part.image.render_variations(replace=False)
except FileNotFoundError:
logger.warning(f"Image file '{part.image}' missing")
pass
except UnidentifiedImageError:
logger.warning(f"Image file '{part.image}' is invalid")
except (OperationalError, ProgrammingError):
# Exception if the database has not been migrated yet
pass
def update_trackable_status(self): def update_trackable_status(self):
""" """
Check for any instances where a trackable part is used in the BOM Check for any instances where a trackable part is used in the BOM

View File

@ -127,13 +127,20 @@ def worker(c):
@task @task
def rebuild(c): def rebuild_models(c):
""" """
Rebuild database models with MPTT structures Rebuild database models with MPTT structures
""" """
manage(c, "rebuild_models") manage(c, "rebuild_models", pty=True)
@task
def rebuild_thumbnails(c):
"""
Rebuild missing image thumbnails
"""
manage(c, "rebuild_thumbnails", pty=True)
@task @task
def clean_settings(c): def clean_settings(c):
@ -143,7 +150,7 @@ def clean_settings(c):
manage(c, "clean_settings") manage(c, "clean_settings")
@task(post=[rebuild]) @task(post=[rebuild_models, rebuild_thumbnails])
def migrate(c): def migrate(c):
""" """
Performs database migrations. Performs database migrations.
@ -341,7 +348,7 @@ def export_records(c, filename='data.json'):
print("Data export completed") print("Data export completed")
@task(help={'filename': 'Input filename'}, post=[rebuild]) @task(help={'filename': 'Input filename'}, post=[rebuild_models, rebuild_thumbnails])
def import_records(c, filename='data.json'): def import_records(c, filename='data.json'):
""" """
Import database records from a file Import database records from a file
@ -399,7 +406,7 @@ def delete_data(c, force=False):
manage(c, 'flush') manage(c, 'flush')
@task(post=[rebuild]) @task(post=[rebuild_models, rebuild_thumbnails])
def import_fixtures(c): def import_fixtures(c):
""" """
Import fixture data into the database. Import fixture data into the database.