129 lines
4.1 KiB
Python
129 lines
4.1 KiB
Python
from django.conf import settings
|
|
from django.db import models
|
|
from django.db.models.signals import post_delete
|
|
from django.dispatch import receiver
|
|
|
|
import json
|
|
import os
|
|
import shutil
|
|
from pathlib import Path
|
|
from PIL import Image, ImageOps
|
|
from tinymce.models import HTMLField
|
|
from map_location.fields import LocationField
|
|
|
|
from app.models.category import Category
|
|
from common.form.audio_file import AudioFileField
|
|
from common.form.img_with_preview import ThumbnailImageField
|
|
|
|
|
|
def overwrite_img_upload(instance: 'Place', filename: str):
|
|
path = instance.fixed_os_path('img.jpg')
|
|
if path.is_file():
|
|
os.remove(path)
|
|
return instance.fixed_save_url('img.jpg')
|
|
|
|
|
|
def overwrite_audio_upload(instance: 'Place', filename: str):
|
|
path = instance.fixed_os_path('audio.mp3')
|
|
if path.is_file():
|
|
os.remove(path)
|
|
return instance.fixed_save_url('audio.mp3')
|
|
|
|
|
|
class Place(models.Model):
|
|
category: 'models.ForeignKey[Category]' = models.ForeignKey(
|
|
'Category', on_delete=models.CASCADE, related_name='places',
|
|
verbose_name='Kategorie')
|
|
|
|
sort = models.IntegerField('Sortierung', default=0)
|
|
isExtended = models.BooleanField('Bis 1.1.2025 verstecken')
|
|
title = models.CharField('Titel', max_length=100)
|
|
image = ThumbnailImageField('Bild', blank=True, null=True,
|
|
upload_to=overwrite_img_upload) # type: ignore
|
|
audio = AudioFileField('Audio', blank=True, null=True,
|
|
upload_to=overwrite_audio_upload)
|
|
location = LocationField('Position', blank=True, null=True, options={
|
|
'map': {
|
|
'center': [49.895, 10.890],
|
|
'zoom': 14,
|
|
},
|
|
})
|
|
description = HTMLField('Beschreibung')
|
|
created = models.DateTimeField('Erstellt', auto_now_add=True)
|
|
|
|
class Meta:
|
|
verbose_name = 'Ort'
|
|
verbose_name_plural = 'Orte'
|
|
ordering = ('sort',)
|
|
|
|
def __str__(self) -> str:
|
|
return self.title
|
|
|
|
def fixed_os_path(self, name: str) -> Path:
|
|
return settings.MEDIA_ROOT / str(self.pk) / name
|
|
|
|
def fixed_save_url(self, name: str) -> str:
|
|
if self.pk is None:
|
|
next_id = Place.objects.count() + 1
|
|
return f'{next_id}/{name}'
|
|
return f'{self.pk}/{name}'
|
|
|
|
@property
|
|
def cover_image_url(self) -> 'str|None':
|
|
if self.image:
|
|
return self.image.url.replace('img.jpg', 'cov.jpg')
|
|
return None
|
|
|
|
def save(self, *args, **kwargs):
|
|
rv = super().save(*args, **kwargs)
|
|
self.update_cover_image()
|
|
Place.update_json()
|
|
return rv
|
|
|
|
def update_cover_image(self):
|
|
if self.image:
|
|
img = Image.open(self.image.path)
|
|
thumb = ImageOps.fit(img, (600, 400))
|
|
# img.thumbnail((600, 400))
|
|
path = self.image.path.replace('img.jpg', 'cov.jpg')
|
|
# path.parent.mkdir(parents=True, exist_ok=True)
|
|
thumb.save(path, 'jpeg')
|
|
# else:
|
|
# path = self.fixed_os_path('cov.jpg')
|
|
# if path.exists():
|
|
# os.remove(path)
|
|
|
|
@staticmethod
|
|
def update_json():
|
|
with open(settings.MEDIA_ROOT / 'places.json', 'w') as fp:
|
|
json.dump(Place.asJson(), fp)
|
|
|
|
@staticmethod
|
|
def asJson() -> 'list[dict[str, str]]':
|
|
rv = []
|
|
for x in Place.objects.all():
|
|
rv.append({
|
|
'id': x.pk,
|
|
'name': x.title,
|
|
'loc': [round(x.location.lat, 6),
|
|
round(x.location.long, 6)] if x.location else None,
|
|
'cat': x.category.pk,
|
|
'cov': x.cover_image_url,
|
|
'img': x.image.url if x.image else None,
|
|
'audio': x.audio.url if x.audio else None,
|
|
'later': x.isExtended,
|
|
'desc': x.description,
|
|
})
|
|
return rv
|
|
|
|
@staticmethod
|
|
def recreateThumbnails() -> None:
|
|
for x in Place.objects.all():
|
|
x.update_cover_image()
|
|
|
|
|
|
@receiver(post_delete, sender=Place)
|
|
def on_delete_Place(sender, instance: 'Place', using, **kwargs):
|
|
shutil.rmtree(settings.MEDIA_ROOT / str(instance.pk), ignore_errors=True)
|
|
Place.update_json()
|