Merge pull request #703 from SchrodingersGat/fixes

Check for missing part thumbnails
This commit is contained in:
Oliver 2020-04-07 14:38:44 +10:00 committed by GitHub
commit 5447bc4356
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 167 additions and 14 deletions

View File

@ -14,6 +14,41 @@ from django.core.exceptions import ValidationError
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from .version import inventreeVersion, inventreeInstanceName from .version import inventreeVersion, inventreeInstanceName
from .settings import MEDIA_URL, STATIC_URL
def getMediaUrl(filename):
"""
Return the qualified access path for the given file,
under the media directory.
"""
return os.path.join(MEDIA_URL, str(filename))
def getStaticUrl(filename):
"""
Return the qualified access path for the given file,
under the static media directory.
"""
return os.path.join(STATIC_URL, str(filename))
def getBlankImage():
"""
Return the qualified path for the 'blank image' placeholder.
"""
return getStaticUrl("img/blank_image.png")
def getBlankThumbnail():
"""
Return the qualified path for the 'blank image' thumbnail placeholder.
"""
return getStaticUrl("img/blank_image.thumbnail.png")
def TestIfImage(img): def TestIfImage(img):
@ -66,7 +101,7 @@ def isNull(text):
True if the text looks like a null value True if the text looks like a null value
""" """
return str(text).strip().lower() in ['top', 'null', 'none', 'empty', 'false', '-1'] return str(text).strip().lower() in ['top', 'null', 'none', 'empty', 'false', '-1', '']
def decimal2string(d): def decimal2string(d):

View File

@ -1,3 +1,4 @@
from django.test import TestCase from django.test import TestCase
import django.core.exceptions as django_exceptions import django.core.exceptions as django_exceptions
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
@ -7,6 +8,8 @@ from . import helpers
from mptt.exceptions import InvalidMove from mptt.exceptions import InvalidMove
from decimal import Decimal
from stock.models import StockLocation from stock.models import StockLocation
@ -72,6 +75,29 @@ class TestHelpers(TestCase):
self.assertFalse(helpers.str2bool(s)) self.assertFalse(helpers.str2bool(s))
self.assertFalse(helpers.str2bool(s, test=False)) self.assertFalse(helpers.str2bool(s, test=False))
def test_isnull(self):
for s in ['null', 'none', '', '-1', 'false']:
self.assertTrue(helpers.isNull(s))
for s in ['yes', 'frog', 'llama', 'true']:
self.assertFalse(helpers.isNull(s))
def testStaticUrl(self):
self.assertEqual(helpers.getStaticUrl('test.jpg'), '/static/test.jpg')
self.assertEqual(helpers.getBlankImage(), '/static/img/blank_image.png')
self.assertEqual(helpers.getBlankThumbnail(), '/static/img/blank_image.thumbnail.png')
def testMediaUrl(self):
self.assertEqual(helpers.getMediaUrl('xx/yy.png'), '/media/xx/yy.png')
def testDecimal2String(self):
self.assertEqual(helpers.decimal2string(Decimal('1.2345000')), '1.2345')
self.assertEqual(helpers.decimal2string('test'), 'test')
class TestQuoteWrap(TestCase): class TestQuoteWrap(TestCase):
""" Tests for string wrapping """ """ Tests for string wrapping """

View File

@ -1,7 +1,36 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import os
from django.apps import AppConfig from django.apps import AppConfig
from django.db.utils import OperationalError, ProgrammingError
from django.conf import settings
class CompanyConfig(AppConfig): class CompanyConfig(AppConfig):
name = 'company' name = 'company'
def ready(self):
"""
This function is called whenever the Company app is loaded.
"""
self.generate_company_thumbs()
def generate_company_thumbs(self):
from .models import Company
print("InvenTree: 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):
print("InvenTree: Generating thumbnail for Company '{c}'".format(c=company.name))
company.image.render_variations(replace=False)
except (OperationalError, ProgrammingError):
print("Could not generate Company thumbnails")

View File

@ -0,0 +1,20 @@
# Generated by Django 2.2.10 on 2020-04-07 01:16
import company.models
from django.db import migrations
import stdimage.models
class Migration(migrations.Migration):
dependencies = [
('company', '0013_auto_20200406_0131'),
]
operations = [
migrations.AlterField(
model_name='company',
name='image',
field=stdimage.models.StdImageField(blank=True, null=True, upload_to=company.models.rename_company_image),
),
]

View File

@ -17,10 +17,12 @@ from django.db.models import Sum
from django.apps import apps from django.apps import apps
from django.urls import reverse from django.urls import reverse
from django.conf import settings
from markdownx.models import MarkdownxField from markdownx.models import MarkdownxField
from stdimage.models import StdImageField
from InvenTree.helpers import getMediaUrl, getBlankImage, getBlankThumbnail
from InvenTree.fields import InvenTreeURLField, RoundingDecimalField from InvenTree.fields import InvenTreeURLField, RoundingDecimalField
from InvenTree.status_codes import OrderStatus from InvenTree.status_codes import OrderStatus
from common.models import Currency from common.models import Currency
@ -90,7 +92,13 @@ class Company(models.Model):
link = InvenTreeURLField(blank=True, help_text=_('Link to external company information')) link = InvenTreeURLField(blank=True, help_text=_('Link to external company information'))
image = models.ImageField(upload_to=rename_company_image, max_length=255, null=True, blank=True) image = StdImageField(
upload_to=rename_company_image,
null=True,
blank=True,
variations={'thumbnail': (128, 128)},
delete_orphans=True,
)
notes = MarkdownxField(blank=True) notes = MarkdownxField(blank=True)
@ -110,10 +118,18 @@ class Company(models.Model):
""" Return the URL of the image for this company """ """ Return the URL of the image for this company """
if self.image: if self.image:
return os.path.join(settings.MEDIA_URL, str(self.image.url)) return getMediaUrl(self.image.url)
else: else:
return os.path.join(settings.STATIC_URL, 'img/blank_image.png') return getBlankImage()
def get_thumbnail_url(self):
""" Return the URL for the thumbnail image for this Company """
if self.image:
return getMediaUrl(self.image.thumbnail.url)
else:
return getBlankThumbnail()
@property @property
def part_count(self): def part_count(self):
""" The number of parts supplied by this company """ """ The number of parts supplied by this company """

View File

@ -32,7 +32,7 @@ class CompanySerializer(InvenTreeModelSerializer):
url = serializers.CharField(source='get_absolute_url', read_only=True) url = serializers.CharField(source='get_absolute_url', read_only=True)
part_count = serializers.CharField(read_only=True) part_count = serializers.CharField(read_only=True)
image = serializers.CharField(source='get_image_url', read_only=True) image = serializers.CharField(source='get_thumbnail_url', read_only=True)
class Meta: class Meta:
model = Company model = Company
@ -64,7 +64,7 @@ class SupplierPartSerializer(InvenTreeModelSerializer):
part_detail = PartBriefSerializer(source='part', many=False, read_only=True) part_detail = PartBriefSerializer(source='part', many=False, read_only=True)
supplier_name = serializers.CharField(source='supplier.name', read_only=True) supplier_name = serializers.CharField(source='supplier.name', read_only=True)
supplier_logo = serializers.CharField(source='supplier.get_image_url', read_only=True) supplier_logo = serializers.CharField(source='supplier.get_thumbnail_url', read_only=True)
pricing = serializers.CharField(source='unit_pricing', read_only=True) pricing = serializers.CharField(source='unit_pricing', read_only=True)

View File

@ -46,7 +46,7 @@ InvenTree | {% trans "Company" %} - {{ company.name }}
<col width='25'> <col width='25'>
{% if company.website %} {% if company.website %}
<tr> <tr>
<td><span class='fas fa-link'></span></td> <td><span class='fas fa-globe'></span></td>
<td>{% trans "Website" %}</td> <td>{% trans "Website" %}</td>
<td><a href="{{ company.website }}">{{ company.website }}</a></td> <td><a href="{{ company.website }}">{{ company.website }}</a></td>
</tr> </tr>

View File

@ -1,7 +1,35 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import os
from django.db.utils import OperationalError, ProgrammingError
from django.apps import AppConfig from django.apps import AppConfig
from django.conf import settings
class PartConfig(AppConfig): class PartConfig(AppConfig):
name = 'part' name = 'part'
def ready(self):
"""
This function is called whenever the Part app is loaded.
"""
self.generate_part_thumbnails()
def generate_part_thumbnails(self):
from .models import Part
print("InvenTree: Checking Part image thumbnails")
try:
for part in Part.objects.all():
if part.image:
url = part.image.thumbnail.name
loc = os.path.join(settings.MEDIA_ROOT, url)
if not os.path.exists(loc):
print("InvenTree: Generating thumbnail for Part '{p}'".format(p=part.name))
part.image.render_variations(replace=False)
except (OperationalError, ProgrammingError):
print("Could not generate Part thumbnails")

View File

@ -10,7 +10,6 @@ import os
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.urls import reverse from django.urls import reverse
from django.conf import settings
from django.db import models, transaction from django.db import models, transaction
from django.db.models import Sum from django.db.models import Sum
@ -300,9 +299,9 @@ class Part(models.Model):
""" Return the URL of the image for this part """ """ Return the URL of the image for this part """
if self.image: if self.image:
return os.path.join(settings.MEDIA_URL, str(self.image.url)) return helpers.getMediaUrl(self.image.url)
else: else:
return os.path.join(settings.STATIC_URL, 'img/blank_image.png') return helpers.getBlankImage()
def get_thumbnail_url(self): def get_thumbnail_url(self):
""" """
@ -310,9 +309,9 @@ class Part(models.Model):
""" """
if self.image: if self.image:
return os.path.join(settings.MEDIA_URL, str(self.image.thumbnail.url)) return helpers.getMediaUrl(self.image.thumbnail.url)
else: else:
return os.path.join(settings.STATIC_URL, 'img/blank_image.thumbnail.png') return helpers.getBlankThumbnail()
def validate_unique(self, exclude=None): def validate_unique(self, exclude=None):
""" Validate that a part is 'unique'. """ Validate that a part is 'unique'.

View File

@ -11,7 +11,7 @@
{% if item.serialized %} {% if item.serialized %}
<p><i>{{ item.part.full_name}} # {{ item.serial }}</i></p> <p><i>{{ item.part.full_name}} # {{ item.serial }}</i></p>
{% else %} {% else %}
<p><i>{{ item.quantity }} &times {{ item.part.full_name }}</i></p> <p><i>{% decimal item.quantity %} &times {{ item.part.full_name }}</i></p>
{% endif %} {% endif %}
<p> <p>
<div class='btn-group'> <div class='btn-group'>