401 lines
16 KiB
Python
401 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")
|