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}'