alpinesmuseum-public/catalog/views.py

348 lines
12 KiB
Python

import io
from functools import reduce
from operator import or_
from cms.utils import get_page_url_without_locale, most_strict_site_match
from django import forms
from django.conf import settings
from django.contrib.admin.views.decorators import staff_member_required
from django.contrib.sites.models import Site
from django.db.models import Count, F, Q
from django.db.models.functions import Length
from django.http import FileResponse, JsonResponse
from django.shortcuts import get_object_or_404, redirect, render
from django.utils.decorators import method_decorator
from django.views import View
from django.views.generic.edit import FormView
from rest_framework.exceptions import ValidationError
from rest_framework.views import APIView
from wagtail.core.models import Locale, Page
from catalog.labels import create_label_1, create_label_3, register_font
from catalog.models import Ad, Comment, Item, Tag, item_tags
from .serializers import CommentSerializer
def index(request):
try:
default = Site.objects.get(config__default=True)
except Site.DoesNotExist:
default = Site.objects.first()
return redirect(default.domain)
def catalog(request):
return render(request, "catalog/index.html")
class LabelForm(forms.Form):
# inventory_nr = forms.CharField()
title = forms.CharField()
date = forms.CharField()
participant = forms.CharField()
categories = forms.CharField()
description = forms.CharField(widget=forms.Textarea)
def create_label(self):
register_font()
buffer = io.BytesIO()
create_label_1(buffer, "",
self.cleaned_data['title'],
self.cleaned_data['date'],
self.cleaned_data['participant'],
self.cleaned_data['categories'],
self.cleaned_data['description'])
buffer.seek(0)
return buffer
class LabelView(FormView):
template_name = 'catalog/label.html'
form_class = LabelForm
success_url = '/labels/create/'
@method_decorator(staff_member_required)
def dispatch(self, *args, **kwargs):
return super().dispatch(*args, **kwargs)
def form_valid(self, form):
buffer = form.create_label()
return FileResponse(buffer, as_attachment=False, filename='label.pdf')
class ItemLabelView(View):
@method_decorator(staff_member_required)
def dispatch(self, *args, **kwargs):
return super().dispatch(*args, **kwargs)
def create_label(self, obj):
register_font()
buffer = io.BytesIO()
tags_str = item_tags(obj.tags.filter(is_active=True), hide_categories=['object_type', 'who'])
create_label_1(buffer, obj.inventory_number_eb, obj.title_short or "",
obj.date_short or "", obj.participant_short or "",
tags_str, obj.description_short or '')
buffer.seek(0)
return buffer
def get(self, request, *args, **kwargs):
obj = get_object_or_404(Item, pk=kwargs.get('id'))
buffer = self.create_label(obj)
return FileResponse(buffer, as_attachment=False, filename='label.pdf')
class ItemBackLabelView(View):
@method_decorator(staff_member_required)
def dispatch(self, *args, **kwargs):
return super().dispatch(*args, **kwargs)
def create_label(self, obj):
register_font()
buffer = io.BytesIO()
tags_str = item_tags(obj.tags.filter(is_active=True), hide_categories=['object_type', 'who'])
create_label_3(buffer, obj.history_short or "", obj.participant_short or "")
buffer.seek(0)
return buffer
def get(self, request, *args, **kwargs):
obj = get_object_or_404(Item, pk=kwargs.get('id'))
buffer = self.create_label(obj)
return FileResponse(buffer, as_attachment=False, filename='label.pdf')
def get_website_mode(request, default_mode, default_host='www.e-fundbuero.ch'):
"""Get the website mode: internal/external/minimal."""
models_dict = getattr(settings, 'WEBSITE_MODE', {})
host = request.META.get('HTTP_HOST', default_host)
try:
return models_dict.get(host, default_mode)
except KeyError:
return default_mode
class SearchJSONView(View):
def filter_by_tags(self, qs, tags, update_display_count=True):
"""
Filtering a QS with given tags.
We group tag per "category". And we filter with following rule:
tags within a group are using "OR"
different groups of tags are using "AND"
For example, "30er", "40er" and "foto" should be filtered like this:
objects with "30er" or "40" and and with "foto".
"""
# grouping tags
tags_list = Tag.objects.filter(slug__in=tags.split(","))
tags_with_categories = {}
for tag in tags_list:
if tag.category not in tags_with_categories.keys():
tags_with_categories[tag.category] = []
tags_with_categories[tag.category].append(tag)
# Filtering
for tags_l in tags_with_categories.values():
qs = qs.filter(reduce(or_, [ Q(tags=tag) for tag in tags_l]))
# updating tags count
if update_display_count:
tags_list.update(display_count=F("display_count") + 1)
return qs.distinct()
def get(self, request, *args, **kwargs):
query = request.GET.get("query", None)
tags = request.GET.get("tags", None) # ?tags=slug1;slug2
site = request.GET.get("site", settings.SITE_ID)
results = []
qs = Item.objects.filter(is_active=True)
if tags is not None and len(tags) > 0:
qs = self.filter_by_tags(qs, tags)
elif query is not None and len(query) > 0:
query = query.strip()
if len(query) > 0:
filters = (
Q(title__icontains=query)
| Q(description_short__icontains=query)
| Q(history_short__icontains=query)
| Q(inventory_number__icontains=query)
| Q(inventory_number_eb__icontains=query)
| Q(participant_short__icontains=query)
| Q(date_short__icontains=query)
| Q(title_short__icontains=query)
)
if query.startswith("i-"):
filters |= Q(id=query[2:])
qs = qs.filter(filters)
qs = qs.filter(site=site)
qs = qs.annotate(
num_comments=Count("comments", filter=Q(comments__status=Comment.VALIDATED)),
image_length=Length('image')
)
qs = qs.prefetch_related('tags', 'group')
mode = get_website_mode(request, 'minimal')
if mode == 'external':
qs = qs.exclude(
Q(Q(image__isnull=True) | Q(image_length__exact=0))
& Q(youtube_url__isnull=True)
)
else:
qs = qs.exclude(Q(image__isnull=True) | Q(image_length__exact=0))
for obj in qs:
results.append(obj.export_as_json())
return JsonResponse({"catalog": results, "count": len(results)})
class TagsJSONView(View):
def get(self, request, *args, **kwargs):
site = request.GET.get("site", settings.SITE_ID)
data = {
category: list(
Item.objects.filter(Q(site=site, tags__category=category, tags__is_active=True))
.exclude(tags__name="")
.distinct()
.values(name=F("tags__name"), slug=F("tags__slug")).order_by("tags__name")
)
for category, _ in Tag.CATEGORIES
}
return JsonResponse(data)
class ModeJSONView(View):
def get(self, request, *args, **kwargs):
mode = get_website_mode(request, 'minimal')
return JsonResponse({'mode': mode})
class AdsJSONView(View):
def get(self, request, *args, **kwargs):
ads_list = []
ads = Ad.objects.filter(site=request.GET.get("site", settings.SITE_ID))
for ad in ads:
video_fr = ''
if ad.video_fr:
video_fr = ad.video_fr.url
video_it = ''
if ad.video_it:
video_it = ad.video_it.url
video_en = ''
if ad.video_en:
video_en = ad.video_en.url
ads_list.append(
{
"de": ad.video_de.url,
"de_title": ad.title_de,
"de_text": ad.text_de,
"fr": video_fr,
"fr_title": ad.title_fr,
"fr_text": ad.text_fr,
"en": video_en,
"en_title": ad.title_en,
"en_text": ad.text_en,
"it": video_it,
"it_title": ad.title_it,
"it_text": ad.text_it,
"prio": ad.prio,
"show_per_page": ad.show_per_page
}
)
return JsonResponse({"results": ads_list, "count": len(ads_list)})
class ItemJSONView(View):
def get(self, request, *args, **kwargs):
obj = get_object_or_404(Item, pk=kwargs["id"], is_active=True)
return JsonResponse(obj.export_as_json())
def get_pages(site, locale):
all_pages = Page.objects.all().filter(locale=locale).exclude(url_path="/")
pages_except_relative_root = filter(lambda p: get_page_url_without_locale(p) != site.domain, all_pages)
pages_for_this_site = [
p for p in pages_except_relative_root if most_strict_site_match(p) == site and p.live is True
]
return pages_for_this_site
class SitesJSONView(View):
def get(self, request, *args, **kwargs):
result = dict()
active_locale = Locale.get_active()
for site in Site.objects.all():
result[site.domain] = {
'id': site.id,
'name': site.name,
'urls': {
page.localized.title if not page.specific.menu_link_text else page.specific.menu_link_text: (page.get_url(), page.show_in_menus)
for page in get_pages(site, active_locale)
},
'color': site.config.color,
'scrolling_text': site.config.scrolling_text,
'default': site.config.default
}
return JsonResponse(result)
class ItemCommentsJSONView(View): # (LoginRequiredMixin, View)
"""
View to fetch / create comments related to an item. Need to be logged-in.
"""
def get(self, request, *args, **kwargs):
"""Return thr"""
obj = get_object_or_404(Item, pk=kwargs["id"])
qs = Comment.objects.filter(item=obj, status=Comment.VALIDATED)
results = []
for comment in qs:
results.append(
{
"added_on": str(comment.added_on),
"author": comment.author,
"field_specific": comment.field_specific,
"status": comment.get_status_display(),
"comment": comment.comment,
}
)
return JsonResponse({"results": results, "count": len(results)})
class ParticipateJSONView(APIView):
authentication_classes = []
def get(self, request):
return JsonResponse({"msg": "this endpoint is only available through POST."})
def post(self, request):
try:
serializer = CommentSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
user_data = serializer.validated_data
comment = Comment.objects.create(
object_id=user_data.get("object_id", None),
object_type=user_data.get("object_type", None),
description=user_data["desc"],
attachment=user_data["file"],
first_name=user_data["first_name"],
last_name=user_data["last_name"],
birth_year=user_data.get("birth_year"),
address=user_data.get("address"),
post_code=user_data.get("post_code"),
city=user_data.get("city"),
phone=user_data.get("phone"),
email=user_data["mail"],
keep=user_data.get("keep", None),
site=Site.objects.get(id=settings.SITE_ID)
)
return JsonResponse({"status": "ok"})
except ValidationError as exc:
return JsonResponse(exc.detail)