From 0c021d0fe0a305a368d7fa1ca758014c94ab8c25 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Josef=20Kol=C3=A1=C5=99?= Date: Tue, 23 Jan 2024 21:36:19 +0100 Subject: [PATCH] feat(profiles): pre-cache profile picture dimensions --- charts/templates/web-deployment.yaml | 5 ++++ ...ight_userprofile_picture_width_and_more.py | 30 +++++++++++++++++++ fiesta/apps/accounts/models/profile.py | 14 +++++++++ fiesta/apps/fiestatables/columns.py | 17 +++++++---- fiesta/apps/files/_patches.py | 27 +++++++++++++++++ fiesta/apps/files/apps.py | 5 ++++ fiesta/fiesta/settings/project.py | 1 + 7 files changed, 94 insertions(+), 5 deletions(-) create mode 100644 fiesta/apps/accounts/migrations/0027_userprofile_picture_height_userprofile_picture_width_and_more.py create mode 100644 fiesta/apps/files/_patches.py diff --git a/charts/templates/web-deployment.yaml b/charts/templates/web-deployment.yaml index 6505978d..eda4406a 100644 --- a/charts/templates/web-deployment.yaml +++ b/charts/templates/web-deployment.yaml @@ -7,6 +7,11 @@ metadata: {{- include "fiesta.componentLabels" "web" | nindent 4 }} spec: replicas: 3 + strategy: + type: RollingUpdate + rollingUpdate: + maxSurge: 1 + maxUnavailable: 1 selector: matchLabels: {{- include "fiesta.selectorLabels" . | nindent 6 }} diff --git a/fiesta/apps/accounts/migrations/0027_userprofile_picture_height_userprofile_picture_width_and_more.py b/fiesta/apps/accounts/migrations/0027_userprofile_picture_height_userprofile_picture_width_and_more.py new file mode 100644 index 00000000..d5f086a5 --- /dev/null +++ b/fiesta/apps/accounts/migrations/0027_userprofile_picture_height_userprofile_picture_width_and_more.py @@ -0,0 +1,30 @@ +# Generated by Django 4.2.8 on 2024-01-23 20:16 + +import apps.accounts.models.profile +import apps.files.storage +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('accounts', '0026_alter_userprofile_avatar_slug'), + ] + + operations = [ + migrations.AddField( + model_name='userprofile', + name='picture_height', + field=models.PositiveSmallIntegerField(blank=True, editable=False, null=True, verbose_name='profile picture width'), + ), + migrations.AddField( + model_name='userprofile', + name='picture_width', + field=models.PositiveSmallIntegerField(blank=True, editable=False, null=True, verbose_name='profile picture width'), + ), + migrations.AlterField( + model_name='userprofile', + name='picture', + field=models.ImageField(blank=True, height_field='picture_height', null=True, storage=apps.files.storage.NamespacedFilesStorage('profile-picture', has_permission=apps.accounts.models.profile.has_permission_for_profile_picture_view), upload_to=apps.files.storage.NamespacedFilesStorage.upload_to, verbose_name='profile picture', width_field='picture_width'), + ), + ] diff --git a/fiesta/apps/accounts/models/profile.py b/fiesta/apps/accounts/models/profile.py index 9f8a2a6c..82201d8c 100644 --- a/fiesta/apps/accounts/models/profile.py +++ b/fiesta/apps/accounts/models/profile.py @@ -107,6 +107,20 @@ class Gender(TextChoices): verbose_name=_("profile picture"), null=True, blank=True, + width_field="picture_width", + height_field="picture_height", + ) + picture_width = models.PositiveSmallIntegerField( + verbose_name=_("profile picture width"), + null=True, + blank=True, + editable=False, + ) + picture_height = models.PositiveSmallIntegerField( + verbose_name=_("profile picture width"), + null=True, + blank=True, + editable=False, ) interests = ArrayFieldWithDisplayableChoices( diff --git a/fiesta/apps/fiestatables/columns.py b/fiesta/apps/fiestatables/columns.py index 1bb0f2c3..68ecf4f8 100644 --- a/fiesta/apps/fiestatables/columns.py +++ b/fiesta/apps/fiestatables/columns.py @@ -5,7 +5,7 @@ import django_tables2 as tables from django.contrib.humanize.templatetags.humanize import NaturalTimeFormatter from django.db.models import Choices, Model -from django.db.models.fields.files import FieldFile +from django.db.models.fields.files import ImageFieldFile from django.utils.html import format_html from django_countries import countries from django_countries.fields import Country @@ -14,24 +14,31 @@ # TODO: probably not needed anymore class ImageColumn(tables.Column): - def render(self, value: FieldFile): - return format_html('', value.url) + def render(self, value: ImageFieldFile): + return format_html( + '', + value.url, + value.width, + value.height, + ) def value(self, record, value): return value.url class AvatarColumn(ImageColumn): - def render(self, value: FieldFile): + def render(self, value: ImageFieldFile): return format_html( """
- +
""", value.url, + value.width, + value.height, ) diff --git a/fiesta/apps/files/_patches.py b/fiesta/apps/files/_patches.py new file mode 100644 index 00000000..cf3a6d7d --- /dev/null +++ b/fiesta/apps/files/_patches.py @@ -0,0 +1,27 @@ +from __future__ import annotations + + +def monkeypatch_image_dimensions_caching(): + from django.core.files.images import ImageFile, get_image_dimensions + + def _get_image_dimensions(self): + from numbers import Number + + if not hasattr(self, "_dimensions_cache"): + close = self.closed + if self.field.width_field and self.field.height_field: + width = getattr(self.instance, self.field.width_field) + height = getattr(self.instance, self.field.height_field) + # check if the fields have proper values + if isinstance(width, Number) and isinstance(height, Number): + self._dimensions_cache = (width, height) + else: + self.open() + self._dimensions_cache = get_image_dimensions(self, close=close) + else: + self.open() + self._dimensions_cache = get_image_dimensions(self, close=close) + + return self._dimensions_cache + + ImageFile._get_image_dimensions = _get_image_dimensions diff --git a/fiesta/apps/files/apps.py b/fiesta/apps/files/apps.py index 42374e6c..f58cf25d 100644 --- a/fiesta/apps/files/apps.py +++ b/fiesta/apps/files/apps.py @@ -2,7 +2,12 @@ from django.apps import AppConfig +from apps.files._patches import monkeypatch_image_dimensions_caching + class FilesConfig(AppConfig): default_auto_field = "django.db.models.BigAutoField" name = "apps.files" + + def ready(self): + monkeypatch_image_dimensions_caching() diff --git a/fiesta/fiesta/settings/project.py b/fiesta/fiesta/settings/project.py index 9aa98ef2..4a241c58 100644 --- a/fiesta/fiesta/settings/project.py +++ b/fiesta/fiesta/settings/project.py @@ -75,6 +75,7 @@ def DEFAULT_FROM_EMAIL(self): "apps.pickup_system.apps.PickupSystemConfig", "apps.dashboard.apps.DashboardConfig", "apps.esncards.apps.ESNcardsConfig", + "apps.files.apps.FilesConfig", "apps.fiestaforms.apps.FiestaFormsConfig", "apps.fiestarequests.apps.FiestaRequestsConfig", "apps.fiestatables.apps.FiestaTablesConfig",