alpinesmuseum-public/catalog/models.py

402 lines
16 KiB
Python

from io import BytesIO
from django.conf import settings
from django.contrib.sites.models import Site
from django.core.files import File
from django.core.mail import send_mail
from django.db import models
from django.db.models.signals import post_save, pre_save
from django.dispatch import receiver
from django.urls import reverse
from django.utils import timezone
from django.utils.translation import gettext
from django.utils.translation import gettext_lazy as _
from PIL import Image
class SiteConfig(models.Model):
site = models.OneToOneField(Site, on_delete=models.CASCADE, related_name="config", primary_key=True)
color = models.CharField(max_length=7, default="#ffe600")
scrolling_text = models.CharField(max_length=2048, blank=True, null=True)
default = models.BooleanField(default=False)
@staticmethod
def pre_save(sender, instance, update_fields, **kwargs):
if instance.default:
for obj in sender.objects.filter(default=True):
obj.default = False
obj.save()
pre_save.connect(SiteConfig.pre_save, SiteConfig)
def make_thumbnail(image, size=(512, 512)):
"""Makes thumbnails of given size from given image"""
im = Image.open(image.path)
im = im.convert('RGB') # convert mode
im.thumbnail(size) # resize imag
thumb_io = BytesIO() # create a BytesIO object
im.save(thumb_io, 'JPEG', quality=85, # save image to BytesIO object
icc_profile = im.info.get('icc_profile',''))
thumbnail = File(thumb_io, name=image.name) # create a django friendly File object
return thumbnail
SHORT_VALUE_LENGTH = 50
TAG_CATEGORIES_ORDER = ['misc', 'objektuntergruppe', 'where', 'when',
'object_type', 'who']
THUMB_SIZE = (512, 512)
class ItemGroup(models.Model):
title = models.CharField(_("title"), max_length=50, unique=True)
slug = models.SlugField(_("slug"))
site = models.ForeignKey(Site, on_delete=models.CASCADE)
def __str__(self):
return self.title
class Meta:
verbose_name = _("item group")
verbose_name_plural = _("item groups")
class Thesaurus(models.Model):
name = models.CharField(_("name"), max_length=50, db_index=True)
slug = models.SlugField(_("slug"))
parent = models.ForeignKey("Thesaurus", on_delete=models.SET_NULL, null=True, verbose_name=_("parent"))
full_path = models.CharField(_("full path"), max_length=300, db_index=True, unique=True)
site = models.ForeignKey(Site, on_delete=models.CASCADE)
def __str__(self):
return self.full_path
class Meta:
verbose_name = _("thesaurus")
verbose_name_plural = _("thesaurus")
class Tag(models.Model):
CATEGORIES = (
("when", _("Dekade")),
("object_type", _("Objektart")),
("who", _("Besitzer")),
("where", _("Region")),
("misc", _("Thema")),
)
name = models.CharField(_("name"), max_length=100, db_index=True)
slug = models.SlugField(_("slug"), db_index=True)
is_active = models.BooleanField(_("is active"), default=True)
display_count = models.IntegerField(_("display count"), default=0)
category = models.CharField(_("category"),
max_length=20, db_index=True, choices=CATEGORIES, default="when"
)
def __str__(self):
return self.name
class Meta:
ordering = ('category', 'name',)
verbose_name = _("tag")
verbose_name_plural = _("tags")
class Item(models.Model):
STATUS_QUO = '1_status_quo'
STATUS = (
(STATUS_QUO, _('Status Quo')),
('2_redigiert', _('Objekte redigiert')),
('3_aufgehangt', _('Objekte aufgehängt')),
)
# To Later enable status for Items
# DRAFT = 'draft'
# PUBLISH_STATUS = (
# ('under_review', _("Under Review")),
# ('published', _("Published")),
# (DRAFT, _("Draft")),
# )
# publish_status = models.CharField(
# _("publish status"), max_length=64, blank=False, null=False, choices=PUBLISH_STATUS,
# default=DRAFT
# )
is_active = models.BooleanField(_("is active"), default=True)
status = models.CharField(max_length=15, choices=STATUS, default=STATUS_QUO)
inventory_number = models.CharField(
_("Inventory number"), max_length=30, db_index=True, blank=True, null=True
)
inventory_number_eb = models.CharField(
_("Inventory number (eb)"), max_length=4, db_index=True, blank=True, null=True
)
research = models.BooleanField(_("recherche"), default=False)
prio = models.BooleanField(_("prio"), default=False)
title = models.CharField(_("title"), max_length=400)
title_short = models.CharField(
_("title (short)"), max_length=50, blank=True, null=True, help_text=_("use on the label")
)
participant = models.CharField(
_("participant"), max_length=256, blank=True, null=True
)
participant_short = models.CharField(
_("participant short"), max_length=27, blank=True, null=True, help_text=_("use on the label")
)
date = models.CharField(_("date"), max_length=50, blank=True, null=True)
date_short = models.CharField(
_("date (short)"), max_length=50, blank=True, null=True, help_text=_("use on the label")
)
group = models.ForeignKey(ItemGroup, on_delete=models.SET_NULL, null=True, blank=True, verbose_name=_("group"), editable=False)
thesaurus_obj = models.ManyToManyField(Thesaurus, blank=True, editable=False)
thesaurus = models.TextField(blank=True, null=True, editable=False)
tags = models.ManyToManyField(Tag, limit_choices_to={"is_active": True}, blank=True)
site = models.ForeignKey(Site, on_delete=models.CASCADE)
size = models.CharField(_("size"), max_length=100, blank=True, null=True, editable=False)
size_short = models.CharField(
_("size (short)"), max_length=100, blank=True, null=True, editable=False
)
description = models.TextField(_("description"), blank=True, null=True)
description_short = models.CharField(
_("description (short)"), max_length=213, blank=True, null=True, help_text=_("use on the label")
)
history = models.TextField(_("history"), null=True, blank=True)
history_short = models.CharField( # back of label
_("history (short)"), max_length=463, blank=True, null=True, help_text=_("use on the label")
)
youtube_url = models.URLField(
_("youtube url"), max_length=200, blank=True, null=True
)
image_path = models.CharField(
_("image path"), max_length=300, blank=True, null=True, editable=False
)
image_name = models.CharField(
_("image name"), max_length=200, blank=True, null=True, editable=False
)
image_suffix = models.CharField(
_("image suffix"), max_length=10, blank=True, null=True, editable=False
)
image = models.ImageField(_("image"), upload_to="items/",
null=True, blank=True,
height_field="image_height", width_field="image_width")
image_height = models.IntegerField(null=True, blank=True, editable=False)
image_width = models.IntegerField(null=True, blank=True, editable=False)
thumbnail = models.ImageField(_("thumbnail"), upload_to="items_thumbnails/",
null=True, blank=True, editable=False,
height_field="thumbnail_height", width_field="thumbnail_width")
thumbnail_height = models.IntegerField(null=True, blank=True, editable=False)
thumbnail_width = models.IntegerField(null=True, blank=True, editable=False)
video = models.FileField(
upload_to="videos/",
verbose_name=_("Video"),
null=True,
blank=True,
editable=False,
help_text=_("Upload only gif files"),
)
notes = models.TextField(_("notes"), null=True, blank=True, help_text=_("für den Administrator"))
def __str__(self):
return self.title
class Meta:
verbose_name = _("item")
verbose_name_plural = _("items")
def export_as_json(self):
palette = ["083d77", "ebebd3", "f4d35e", "ee964b", "f95738"]
img = None
thumbnail = None
if bool(self.image):
img = self.image.url
if bool(self.thumbnail):
thumbnail = self.thumbnail.url
video_url = None
if bool(self.video):
video_url = self.video.url
group_title = ""
if self.group is not None:
group_title = self.group.title
data = {
"url": reverse("item", args=[self.id]),
"inventory_number": self.inventory_number_eb or f"i-{self.id}",
"title": self.title_short or '',
"participant": self.participant_short or "",
"date": self.date_short or "",
"group": group_title,
"size": self.size or '',
"description": self.description_short or "",
"history": self.history_short or "",
"image": img,
"image_height": self.image_height,
"image_width": self.image_width,
"thumbnail": thumbnail,
"thumbnail_height": self.thumbnail_height,
"thumbnail_width": self.thumbnail_width,
"video": video_url,
"prio": self.prio,
"thesaurus": '', # [item.full_path for item in self.thesaurus_obj.all()],
"tags": [
{"name": tag.name, "slug": tag.slug}
for tag in get_tags(self.tags.all())
],
}
if self.youtube_url is not None:
data["youtube"] = self.youtube_url
if hasattr(self, "num_comments"):
data["validated_comments_count"] = self.num_comments
return data
def save(self, *args, **kwargs):
super().save(*args, **kwargs)
if self.pk is not None and self.image: # check if Item has image (avoid item with video)
self.thumbnail = make_thumbnail(self.image, size=THUMB_SIZE)
super().save(*args, **kwargs)
def get_tags(qs, hide_categories=None):
final_tag_list = []
tags_per_group = {}
for tag_obj in qs:
if len(tag_obj.name) == 0 or tag_obj.is_active is False:
continue
if tag_obj.category not in tags_per_group:
tags_per_group[tag_obj.category] = []
tags_per_group[tag_obj.category].append(tag_obj)
for category in TAG_CATEGORIES_ORDER:
if category in tags_per_group:
if hide_categories is not None:
if category in hide_categories:
continue
for tag in tags_per_group[category]:
final_tag_list.append(tag)
return final_tag_list
def item_tags(qs, hide_categories=None):
final_tag_list = get_tags(qs, hide_categories=hide_categories)
return " ".join(["#{}".format(tag.name) for tag in final_tag_list if len(tag.name) > 0])
class Comment(models.Model):
ADDED = "1_added"
VALIDATED = "2_validated"
REFUSED = "3_refused"
LABELLED = "4_labelled"
STATUS = (
(ADDED, _("Added")),
(VALIDATED, _("Validated")),
(LABELLED, _("Labelled")),
(REFUSED, _("Refused"))
)
KEEP_CHOICES = (
("forever", _("forever")),
("until_2021", _("until 2021")),
)
item = models.ForeignKey(
Item, on_delete=models.CASCADE, related_name="comments", null=True, blank=True, verbose_name=_("item")
)
added_on = models.DateTimeField(_("added on"), default=timezone.now, editable=False)
status = models.CharField(_("status"),
max_length=15, db_index=True, choices=STATUS, default=ADDED
)
site = models.ForeignKey(Site, on_delete=models.CASCADE)
object_id = models.CharField(_("Object ID"), max_length=300, null=True, blank=True)
object_type = models.CharField(
_("object type"), max_length=300, null=True, blank=True
)
description = models.TextField(_("Description"))
attachment = models.FileField(_("Attachment"), upload_to="uploads/", null=True, blank=True)
first_name = models.CharField(_("first name"), max_length=300)
last_name = models.CharField(_("last name"), max_length=300)
address = models.CharField(_("address"), max_length=300, null=True, blank=True)
post_code = models.CharField(_("post code"), max_length=300, null=True, blank=True)
birth_year = models.CharField(_("birth year"), max_length=300, null=True, blank=True)
city = models.CharField(_("city"), max_length=300, null=True, blank=True)
phone = models.CharField(_("phone"), max_length=300, null=True, blank=True)
email = models.EmailField(_("email"))
keep = models.CharField(
_("keep"), max_length=300, choices=KEEP_CHOICES, null=True, blank=True
)
admin_comment = models.TextField(_("Admin comment"), null=True, blank=True)
misc_data = models.JSONField(default=dict)
@staticmethod
def post_comment_save(sender, instance, created, **kwargs):
if created:
# Send email
comment_admin_url = reverse('admin:catalog_comment_change', args=[instance.id])
url = f"https://www.e-fundbuero.ch{comment_admin_url}"
send_mail(
gettext('New message'),
gettext('You have a new message. You can retrieve it from %(url)s.') % {'url': url},
'noreply@e-fundbuero.ch',
[settings.ADMIN_EMAIL],
fail_silently=True,
)
def __str__(self):
if self.object_id is not None and len(self.object_id) > 0:
return self.object_id
elif self.object_type is not None and len(self.object_type) > 0:
return self.object_type
else:
return "comment {}".format(self.pk)
class Meta:
verbose_name = _("comment")
verbose_name_plural = _("comments")
post_save.connect(Comment.post_comment_save, sender=Comment)
class Ad(models.Model):
title = models.CharField(_("ad title"), max_length=400)
video_de = models.FileField(
upload_to="ads/videos/de/",
verbose_name=_("Video (German)"),
help_text="Upload only mp4 video",
)
title_de = models.CharField(_("title (German)"), max_length=300, default="", null=False, blank=True)
text_de = models.TextField(_("Text (German)"), default="", null=False, blank=True)
video_fr = models.FileField(
upload_to="ads/videos/fr/",
verbose_name=_("Video (French)"),
help_text="Upload only mp4 video",
null=True,
blank=True,
)
title_fr = models.CharField(_("title (French)"), max_length=300,default="", null=False, blank=True)
text_fr = models.TextField(_("Text (French)"), default="", null=False, blank=True)
video_it = models.FileField(
upload_to="ads/videos/it/",
verbose_name=_("Video (Italian)"),
help_text="Upload only mp4 video",
null=True,
blank=True,
)
title_it = models.CharField(_("title (Italian)"), max_length=300, default="", null=False, blank=True)
text_it = models.TextField(_("Text (Italian)"), default="", null=False, blank=True)
video_en = models.FileField(
upload_to="ads/videos/en/",
verbose_name=_("Video (English)"),
help_text="Upload only mp4 video",
null=True,
blank=True,
)
title_en = models.CharField(_("title (English)"), max_length=300, default="", null=False, blank=True)
text_en = models.TextField(_("Text (English)"), default="", null=False, blank=True)
prio = models.BooleanField(_("prio"), default=False)
show_per_page = models.BooleanField(_("show per page"), default=False)
site = models.ForeignKey(Site, on_delete=models.CASCADE)
def __str__(self):
return self.title
class Meta:
verbose_name = _("ad")
verbose_name_plural = _("ads")