muistox/gallery/models/photo.py

102 lines
No EOL
4.2 KiB
Python

import os
import random
from datetime import datetime
from django.db import models
from django.urls import reverse
from imagekit.models import ImageSpecField
from imagekit.processors import ResizeToFit
from gallery.exif import Exif
from gallery.models import Album
def get_upload_path(instance, filename):
return os.path.join('albums', str(instance.album.slug), filename)
class Photo(models.Model):
album = models.ForeignKey(Album, on_delete=models.CASCADE, related_name='photos', verbose_name="Album")
slug = models.CharField(unique=True, max_length=15, editable=False, verbose_name="Photo Slug")
photo = models.ImageField(upload_to=get_upload_path, height_field='height', width_field='width', verbose_name="Photo")
# Thumbnail-versiot
photo_sm = ImageSpecField(source='photo', processors=[ResizeToFit(320, 320)], format='JPEG', options={'quality': 70})
photo_md = ImageSpecField(source='photo', processors=[ResizeToFit(720, 720)], format='JPEG', options={'quality': 80})
photo_bg = ImageSpecField(source='photo', processors=[ResizeToFit(1920, 1920)], format='JPEG', options={'quality': 90})
width = models.PositiveIntegerField(default=0, editable=False, verbose_name="Photo Width")
height = models.PositiveIntegerField(default=0, editable=False, verbose_name="Photo Height")
taken_at = models.DateTimeField(blank=True, null=True, editable=False, verbose_name="Taken at")
exif = models.JSONField(blank=True, null=True, editable=False, verbose_name="Exif Metadata")
is_favorite = models.BooleanField(default=False, verbose_name="Is Favorite")
views = models.PositiveIntegerField(default=0, verbose_name="Views")
likes = models.PositiveIntegerField(default=0, verbose_name="Likes")
@property
def orientation(self):
return "Portrait" if self.height > self.width else "Landscape"
@property
def aspect_ratio(self):
return self.width / self.height
@staticmethod
def generate_unique_slug(album, datetime_taken):
"""
Luo uniikin slugin, joka perustuu ottopäivämäärään.
Lisää tarvittaessa satunnaisluvun, jos slug on jo olemassa.
"""
base_slug = datetime_taken.strftime('%y%m%d%H%M%S') # esim. 240304153045
slug = f"{base_slug}{random.randint(0, 9)}"
while Photo.objects.filter(slug=slug).exists():
slug = f"{base_slug}{random.randint(10, 99)}" # Jos törmäys, lisää 2 satunnaisnumeroa
return slug
def add_like(self):
self.likes += 1
self.save(update_fields=['likes'])
def add_view(self):
self.views += 1
self.save(update_fields=['views'])
def get_next(self):
return self.__class__.objects.filter(taken_at__gt=self.taken_at, album=self.album).order_by('taken_at').first()
def get_prev(self):
return self.__class__.objects.filter(taken_at__lt=self.taken_at, album=self.album).order_by('-taken_at').first()
def extract_metadata(self):
"""Lukee Exif-metadatan ja asettaa tiedot ennen kuvan tallennusta."""
if not self.photo:
return # Jos kuvaa ei ole, ei tehdä mitään
try:
exif_data = Exif(self.photo.file) # Luetaan Exif-metadata suoraan muistista
self.taken_at = exif_data.datetimeoriginal or datetime.now()
self.exif = exif_data.data or None
except Exception as e:
print(f"Exif-tiedon lukeminen epäonnistui: {e}")
self.taken_at = datetime.now()
def save(self, *args, **kwargs):
"""Ennen tallennusta luetaan Exif ja asetetaan slug, jos se puuttuu."""
if not self.slug:
self.extract_metadata() # Varmistetaan, että ottoaika on saatavilla
self.slug = self.generate_unique_slug(self.album, self.taken_at or datetime.now())
super().save(*args, **kwargs)
def get_absolute_url(self):
return reverse('gallery:photo_url', kwargs={'album_slug': self.album.slug, 'photo_slug': self.slug})
class Meta:
constraints = [models.UniqueConstraint(fields=['slug'], name='unique_photo_slug')]
verbose_name_plural = "Photos"
ordering = ('-taken_at',)
def __str__(self):
return f'{self.slug} ({self.orientation}) {self.is_favorite}'