Merge branch 'master' into 'Javascript-continious-upload-translate'

# Conflicts:
#   photo21/settings.py
This commit is contained in:
loulous27 2025-11-23 15:31:56 +01:00
commit 7a0bf9485b
5 changed files with 77 additions and 13 deletions

View file

@ -0,0 +1,25 @@
"""A generator of small picture to test very larges galeries"""
from PIL import Image
from PIL import ImageDraw
import argparse
parser = argparse.ArgumentParser(
description="A generator of small picture to test very larges galeries"
)
parser.add_argument("count", help="Numbers of photo to generate", type=int)
parser.add_argument(
"-outputfolder", help="The outputfolders by default : ./photos/", default="photos/"
)
args = parser.parse_args()
for i in range(args.count):
if (100//5 * (i + 1)) % args.count == 0: # affichage tout les 5%
print(f"Image {i+1} : {(i+1)/args.count:.0%}")
img = Image.new(mode="RGB",size=(100,100),color=(0,0,0))
ImageDraw.Draw(img).text((0,0),str(i+1),(255,255,255))
img.save(args.outputfolder+f"img_{i+1}.jpg",)

View file

@ -37,6 +37,11 @@ ALLOWED_HOSTS = [
"photos-dev.crans.org", "photos-dev.crans.org",
] ]
INTERNAL_IPS = [
"127.0.0.1",
"localhost",
]
# Admins receive server errors, this is useful to be notified of potential bugs # Admins receive server errors, this is useful to be notified of potential bugs
ADMINS = [ ADMINS = [
("admin", "photos-admin@lists.crans.org"), ("admin", "photos-admin@lists.crans.org"),
@ -68,9 +73,12 @@ INSTALLED_APPS = [
"allauth_note_kfet", "allauth_note_kfet",
"crispy_forms", "crispy_forms",
"photologue", "photologue",
"photo21" "photo21",
] ]
if DEBUG:
INSTALLED_APPS += ["debug_toolbar",] # For debug and optimisations
MIDDLEWARE = [ MIDDLEWARE = [
"django.middleware.security.SecurityMiddleware", "django.middleware.security.SecurityMiddleware",
"django.contrib.sessions.middleware.SessionMiddleware", "django.contrib.sessions.middleware.SessionMiddleware",
@ -81,8 +89,10 @@ MIDDLEWARE = [
"django.middleware.clickjacking.XFrameOptionsMiddleware", "django.middleware.clickjacking.XFrameOptionsMiddleware",
"django.middleware.locale.LocaleMiddleware", "django.middleware.locale.LocaleMiddleware",
"django.contrib.sites.middleware.CurrentSiteMiddleware", "django.contrib.sites.middleware.CurrentSiteMiddleware",
"allauth.account.middleware.AccountMiddleware", "allauth.account.middleware.AccountMiddleware", # For the django =< 5.0
] ]
if DEBUG :
MIDDLEWARE += ["debug_toolbar.middleware.DebugToolbarMiddleware",]
ROOT_URLCONF = "photo21.urls" ROOT_URLCONF = "photo21.urls"
@ -122,6 +132,13 @@ DATABASES = {
} }
} }
CACHES = {
"default": {
"BACKEND": "django.core.cache.backends.locmem.LocMemCache",
"LOCATION": "Master",
}
}
# Password validation # Password validation
# https://docs.djangoproject.com/en/2.2/ref/settings/#auth-password-validators # https://docs.djangoproject.com/en/2.2/ref/settings/#auth-password-validators

View file

@ -14,6 +14,9 @@ from django.contrib import admin
from django.urls import include, path, re_path from django.urls import include, path, re_path
from django.views.i18n import JavaScriptCatalog from django.views.i18n import JavaScriptCatalog
if settings.DEBUG :
from debug_toolbar.toolbar import debug_toolbar_urls
from .views import IndexView, MediaAccess from .views import IndexView, MediaAccess
urlpatterns = [ urlpatterns = [
@ -29,6 +32,7 @@ urlpatterns = [
# In production media are served through NGINX with X-Accel-Redirect # In production media are served through NGINX with X-Accel-Redirect
if settings.DEBUG: if settings.DEBUG:
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
urlpatterns += debug_toolbar_urls()
else: else:
urlpatterns.append( urlpatterns.append(
re_path("^media/(?P<path>.*)", MediaAccess.as_view(), name="media") re_path("^media/(?P<path>.*)", MediaAccess.as_view(), name="media")

View file

@ -3,7 +3,6 @@
import logging import logging
import os import os
import random
import unicodedata import unicodedata
from datetime import datetime from datetime import datetime
from functools import partial from functools import partial
@ -16,6 +15,7 @@ from django.conf import settings
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
from django.core.files.base import ContentFile from django.core.files.base import ContentFile
from django.core.validators import RegexValidator from django.core.validators import RegexValidator
from django.core.cache import caches
from django.db import models from django.db import models
from django.template.defaultfilters import slugify from django.template.defaultfilters import slugify
from django.urls import reverse from django.urls import reverse
@ -25,6 +25,8 @@ from django.utils.timezone import now
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from PIL import Image, ImageFile, ImageFilter from PIL import Image, ImageFile, ImageFilter
logger = logging.getLogger("photologue.models") logger = logging.getLogger("photologue.models")
# Default limit for gallery.latest # Default limit for gallery.latest
@ -184,13 +186,15 @@ class Gallery(models.Model):
def sample(self, public=True): def sample(self, public=True):
"""Return a sample of photos, ordered at random.""" """Return a sample of photos, ordered at random."""
count = 1 count = 1
if count > self.photo_count(): nb = self.photo_count(public) #Optimisation don't do twice the SQL requests
count = self.photo_count() if nb < count:
count = nb
if public: if public:
photo_set = self.photos.filter(is_public=True) photo_set = self.photos.filter(is_public=True)
else: else:
photo_set = self.photos photo_set = self.photos
return random.sample(list(photo_set), count) return photo_set.order_by("?")[:count] # Use native SQL random
def photo_count(self, public=True): def photo_count(self, public=True):
"""Return a count of all the photos in this gallery.""" """Return a count of all the photos in this gallery."""
@ -721,10 +725,16 @@ class PhotoSizeCache:
def __init__(self): def __init__(self):
self.__dict__ = self.__state self.__dict__ = self.__state
if not len(self.sizes):
sizes = PhotoSize.objects.all() cached = caches.get("PhotoSizeCache",None)
for size in sizes: if cached is None :
self.sizes[size.name] = size if not len(self.sizes):
sizes = PhotoSize.objects.all()
for size in sizes:
self.sizes[size.name] = size
caches.set("PhotoSizeCache",self)
else :
self = cached
def reset(self): def reset(self):
global size_method_map global size_method_map

View file

@ -20,10 +20,16 @@ from django.views.generic.dates import ArchiveIndexView, YearArchiveView
from django.views.generic.detail import DetailView from django.views.generic.detail import DetailView
from django.views.generic.edit import DeleteView, FormView from django.views.generic.edit import DeleteView, FormView
from PIL import Image from PIL import Image
from django.contrib.auth import get_user_model
from .forms import UploadForm from .forms import UploadForm
from .models import Gallery, Photo, Tag from .models import Gallery, Photo, Tag
# Cette ligne renvoie le modèle d'utilisateur actif (le natif ou le vôtre)
User = get_user_model()
class GalleryDateView(LoginRequiredMixin): class GalleryDateView(LoginRequiredMixin):
model = Gallery model = Gallery
@ -135,11 +141,11 @@ class GalleryDetailView(LoginRequiredMixin, DetailView):
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs) context = super().get_context_data(**kwargs)
# Non-staff members only see public photos # Non-staff members only see public photos + prefetch all owners informations (Optimisation)
if self.request.user.is_staff: if self.request.user.is_staff:
context["photos"] = self.object.photos.all() context["photos"] = self.object.photos.all().select_related('owner')
else: else:
context["photos"] = self.object.photos.filter(is_public=True) context["photos"] = self.object.photos.filter(is_public=True).select_related('onwer')
# List owners # List owners
context["owners"] = [] context["owners"] = []
@ -147,6 +153,8 @@ class GalleryDetailView(LoginRequiredMixin, DetailView):
if photo.owner not in context["owners"]: if photo.owner not in context["owners"]:
context["owners"].append(photo.owner) context["owners"].append(photo.owner)
# Filter on owner # Filter on owner
if "owner" in self.kwargs: if "owner" in self.kwargs:
context["photos"] = context["photos"].filter(owner__id=self.kwargs["owner"]) context["photos"] = context["photos"].filter(owner__id=self.kwargs["owner"])