Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a new field that convert images to webp #175

Merged
merged 3 commits into from
Oct 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions insalan/components/image_field.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import os
from io import BytesIO

from django.core.exceptions import ValidationError
from django.db import models
from PIL import Image
import pillow_avif

class ImageField(models.ImageField):
"""
A subclass of Django's ImageField that convert the image to a webp format
"""

def __init__(self, *args, **kwargs):
self.convert_to_webp = kwargs.pop("convert_to_webp", True)
super().__init__(*args, **kwargs)

def pre_save(self, model_instance, add):
file = super().pre_save(model_instance, add)
if not file:
return file
if self.convert_to_webp:
try:
img = Image.open(file)
if img.format not in ["WEBP", "AVIF"]:
buffer = BytesIO()
img.save(buffer, format="WEBP")
buffer.seek(0)
file.save(
os.path.basename(os.path.splitext(file.name)[0]) + ".webp",
buffer,
save=False,
)
except Exception as e:
raise ValidationError(f"Invalid image file: {e}") from e
return file
6 changes: 4 additions & 2 deletions insalan/partner/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
from django.db import models
from django.utils.translation import gettext_lazy as _

from insalan.components.image_field import ImageField

class Partner(models.Model):
class PartnerType(models.TextChoices):
"""There are two types of sponsors"""
Expand All @@ -22,11 +24,11 @@ class Meta:
max_length=200, verbose_name=_("Nom du partenaire/sponsor")
)
url: models.URLField = models.URLField(verbose_name=_("URL"))
logo: models.FileField = models.FileField(
logo: models.FileField = ImageField(
verbose_name=_("Logo"),
upload_to="partners",
validators=[
FileExtensionValidator(allowed_extensions=["png", "jpg", "jpeg", "svg", "webp"])
FileExtensionValidator(allowed_extensions=["png", "jpg", "jpeg", "svg", "webp", "avif"])
],
)
partner_type: models.CharField = models.CharField(
Expand Down
14 changes: 12 additions & 2 deletions insalan/partner/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
from django.urls import reverse
from rest_framework import status
from rest_framework.test import APITestCase
from django.core.files.uploadedfile import SimpleUploadedFile
from PIL import Image

from insalan.user.models import User

Expand All @@ -35,9 +37,17 @@ def create_test_img(file_name: str = "test.png") -> SimpleUploadedFile:
"""
Create a test image file.
"""
test_img = io.BytesIO(b"test image")
# Create an image using Pillow
image = Image.new('RGB', (100, 100), color = (73, 109, 137))

# Save the image to a BytesIO object
test_img = io.BytesIO()
image.save(test_img, format='PNG')
test_img.seek(0)
test_img.name = file_name
return SimpleUploadedFile(test_img.name, test_img.getvalue())

# Return the SimpleUploadedFile object
return SimpleUploadedFile(test_img.name, test_img.getvalue(), content_type='image/png')


class UserTestCase(TestCase):
Expand Down
6 changes: 4 additions & 2 deletions insalan/pizza/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
from django.utils.translation import gettext_lazy as _
from django.contrib.postgres.fields import ArrayField

from insalan.components.image_field import ImageField

class PaymentMethod(models.TextChoices):
"""
Payment method choices
Expand Down Expand Up @@ -52,11 +54,11 @@ class Meta:
blank=True,
null=True,
)
image: models.FileField = models.FileField(
image: models.FileField = ImageField(
verbose_name=_("Image"),
upload_to="pizzas",
validators=[
FileExtensionValidator(allowed_extensions=["png", "jpg", "jpeg", "svg", "webp"])
FileExtensionValidator(allowed_extensions=["png", "jpg", "jpeg", "svg", "webp", "avif"])
],
null=True,
blank=True,
Expand Down
2 changes: 1 addition & 1 deletion insalan/tournament/management/commands/populate.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ def generate_logo(self):
test_img.name = (
generate_garbage(randint(5, 20))
+ "."
+ choice(["png", "jpg", "jpeg", "svg", "webp"])
+ choice(["png", "jpg", "jpeg", "svg", "webp", "avif"])
)
return SimpleUploadedFile(test_img.name, test_img.getvalue())

Expand Down
6 changes: 4 additions & 2 deletions insalan/tournament/models/caster.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
from django.core.validators import FileExtensionValidator
from django.utils.translation import gettext_lazy as _

from insalan.components.image_field import ImageField

class Caster(models.Model):
"""
A Caster is someone that can cast a tournament.
Expand All @@ -12,13 +14,13 @@ class Caster(models.Model):
blank=False,
verbose_name=_("Nom du casteur")
)
image = models.FileField(
image = ImageField(
verbose_name=_("Photo de profil"),
blank=True,
null=True,
upload_to="profile-pictures",
validators=[
FileExtensionValidator(allowed_extensions=["png", "jpg", "jpeg", "svg", "webp"])
FileExtensionValidator(allowed_extensions=["png", "jpg", "jpeg", "svg", "webp", "avif"])
],
)
tournament = models.ForeignKey(
Expand Down
6 changes: 4 additions & 2 deletions insalan/tournament/models/event.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@

from . import tournament

from insalan.components.image_field import ImageField

class Event(models.Model):
"""
An Event is any single event that is characterized by the following:
Expand Down Expand Up @@ -41,13 +43,13 @@ class Event(models.Model):
validators=[MinValueValidator(1), MaxValueValidator(12)],
)
ongoing = models.BooleanField(verbose_name=_("En cours"), default=False)
logo: models.FileField = models.FileField(
logo: models.FileField = ImageField(
verbose_name=_("Logo"),
blank=True,
null=True,
upload_to="event-icons",
validators=[
FileExtensionValidator(allowed_extensions=["png", "jpg", "jpeg", "svg", "webp"])
FileExtensionValidator(allowed_extensions=["png", "jpg", "jpeg", "svg", "webp", "avif"])
],
)

Expand Down
5 changes: 3 additions & 2 deletions insalan/tournament/models/tournament.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from django.contrib.postgres.fields import ArrayField

from . import team, caster, group, bracket, swiss
from insalan.components.image_field import ImageField

def in_thirty_days():
"""Return now + 30 days"""
Expand Down Expand Up @@ -51,13 +52,13 @@ class Tournament(models.Model):
default=in_thirty_days,
null=False,
)
logo: models.FileField = models.FileField(
logo: models.FileField = ImageField(
verbose_name=_("Logo"),
blank=True,
null=True,
upload_to="tournament-icons",
validators=[
FileExtensionValidator(allowed_extensions=["png", "jpg", "jpeg", "svg", "webp"])
FileExtensionValidator(allowed_extensions=["png", "jpg", "jpeg", "svg", "webp", "avif"])
],
)
# Tournament player slot prices
Expand Down
6 changes: 4 additions & 2 deletions insalan/user/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
from django.utils.translation import gettext_lazy as _
from django.core.validators import FileExtensionValidator

from insalan.components.image_field import ImageField

class UserManager(BaseUserManager):
"""
Managers the User objects (kind of like a serializer but not quite that)
Expand Down Expand Up @@ -75,13 +77,13 @@ def __init__(self, *args, **kwargs):
USERNAME_FIELD = "username"
EMAIL_FIELD = "email"

image = models.FileField(
image = ImageField(
verbose_name=_("photo de profil"),
blank=True,
null=True,
upload_to="profile-pictures",
validators=[
FileExtensionValidator(allowed_extensions=["png", "jpg", "jpeg", "svg", "webp"])
FileExtensionValidator(allowed_extensions=["png", "jpg", "jpeg", "svg", "webp", "avif"])
],
)

Expand Down
2 changes: 1 addition & 1 deletion insalan/user/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ class UserRegisterSerializer(serializers.ModelSerializer):
image = serializers.FileField(
required=False,
validators=[
FileExtensionValidator(allowed_extensions=["png", "jpg", "jpeg", "svg", "webp"])
FileExtensionValidator(allowed_extensions=["png", "jpg", "jpeg", "svg", "webp", "avif"])
],
)
name = serializers.CharField(write_only=True, required=False, allow_blank=True) #If this field is filled, something bad happened (bot), purposely ambiguous nament
Expand Down
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,4 @@ reportlab==4.0.9
pillow==10.2.0
APScheduler==3.10.4
drf-yasg==1.21.7
pillow-avif-plugin==1.4.6
Loading