235 lines
7.5 KiB
Python
235 lines
7.5 KiB
Python
import random
|
|
|
|
from django.db.models import Prefetch
|
|
from django.http import Http404
|
|
from django.http import JsonResponse
|
|
from django.shortcuts import render
|
|
from django.urls import reverse
|
|
from django.views import View
|
|
from django.views.generic import DetailView, ListView, TemplateView
|
|
|
|
from apps.products.models import MainProduct, SubProduct
|
|
from apps.projects.models import ProjectBrief
|
|
|
|
from .forms import ContactForm
|
|
from .models import (
|
|
AboutSection,
|
|
ContactSubmission,
|
|
ContactSubmissionAttachment,
|
|
CustomPage,
|
|
FAQEntry,
|
|
HeroSection,
|
|
HomepageSection,
|
|
PageVideo,
|
|
)
|
|
|
|
VIDEO_PREVIEW_LIMIT = 3
|
|
VIDEO_ARCHIVE_PAGE_SIZE = 6
|
|
|
|
|
|
def _video_preview_context(queryset, archive_url):
|
|
total = queryset.count()
|
|
return {
|
|
"videos": queryset[:VIDEO_PREVIEW_LIMIT],
|
|
"videos_total_count": total,
|
|
"videos_archive_url": archive_url if total > VIDEO_PREVIEW_LIMIT else "",
|
|
}
|
|
|
|
|
|
def _homepage_products_catalog_queryset():
|
|
return (
|
|
MainProduct.objects.filter(is_active=True)
|
|
.prefetch_related(
|
|
Prefetch(
|
|
"sub_products",
|
|
queryset=SubProduct.objects.filter(is_active=True).order_by("order", "name"),
|
|
)
|
|
)
|
|
.order_by("order", "name")
|
|
)
|
|
|
|
|
|
class HomeView(TemplateView):
|
|
template_name = "pages/home.html"
|
|
|
|
def get_context_data(self, **kwargs):
|
|
ctx = super().get_context_data(**kwargs)
|
|
ctx["hero"] = HeroSection.objects.first()
|
|
ctx["homepage_sections"] = (
|
|
HomepageSection.objects.filter(is_active=True)
|
|
.prefetch_related("items")
|
|
.order_by("order")
|
|
)
|
|
ctx["homepage_products"] = (
|
|
MainProduct.objects.filter(is_active=True, show_on_homepage=True)
|
|
.order_by("homepage_order", "order")
|
|
)
|
|
ctx["homepage_products_catalog"] = _homepage_products_catalog_queryset()
|
|
ctx["website_project_briefs"] = (
|
|
ProjectBrief.objects.filter(show_on_website=True)
|
|
.order_by("-submitted_at")
|
|
)
|
|
return ctx
|
|
|
|
|
|
class AboutView(TemplateView):
|
|
template_name = "pages/about.html"
|
|
|
|
def get_context_data(self, **kwargs):
|
|
ctx = super().get_context_data(**kwargs)
|
|
ctx["about_sections"] = (
|
|
AboutSection.objects.filter(is_active=True)
|
|
.prefetch_related("items")
|
|
.order_by("order")
|
|
)
|
|
return ctx
|
|
|
|
|
|
class FAQView(ListView):
|
|
model = FAQEntry
|
|
template_name = "pages/faq.html"
|
|
context_object_name = "faq_entries"
|
|
queryset = FAQEntry.objects.filter(is_active=True)
|
|
|
|
def get_context_data(self, **kwargs):
|
|
ctx = super().get_context_data(**kwargs)
|
|
videos = PageVideo.objects.filter(
|
|
page=PageVideo.PAGE_FAQ,
|
|
is_active=True,
|
|
).order_by("order")
|
|
preview = _video_preview_context(videos, reverse("pages:video_archive", kwargs={"library": PageVideo.PAGE_FAQ}))
|
|
ctx["faq_videos"] = preview["videos"]
|
|
ctx["videos_total_count"] = preview["videos_total_count"]
|
|
ctx["videos_archive_url"] = preview["videos_archive_url"]
|
|
return ctx
|
|
|
|
|
|
class ContactView(View):
|
|
template_name = "pages/contact.html"
|
|
|
|
def _new_captcha(self, request):
|
|
a, b = random.randint(1, 9), random.randint(1, 9)
|
|
request.session["captcha_answer"] = a + b
|
|
return f"{a} + {b}"
|
|
|
|
def get(self, request, *args, **kwargs):
|
|
videos = PageVideo.objects.filter(
|
|
page=PageVideo.PAGE_CONTACT,
|
|
is_active=True,
|
|
).order_by("order")
|
|
preview = _video_preview_context(
|
|
videos,
|
|
reverse("pages:video_archive", kwargs={"library": PageVideo.PAGE_CONTACT}),
|
|
)
|
|
return render(
|
|
request,
|
|
self.template_name,
|
|
{
|
|
"form": ContactForm(),
|
|
"captcha_question": self._new_captcha(request),
|
|
"contact_videos": preview["videos"],
|
|
"videos_total_count": preview["videos_total_count"],
|
|
"videos_archive_url": preview["videos_archive_url"],
|
|
},
|
|
)
|
|
|
|
def post(self, request, *args, **kwargs):
|
|
form = ContactForm(
|
|
request.POST,
|
|
file_list=request.FILES.getlist("attachments"),
|
|
)
|
|
expected = request.session.get("captcha_answer")
|
|
captcha_question = self._new_captcha(request)
|
|
|
|
captcha_ok = False
|
|
try:
|
|
captcha_ok = int(request.POST.get("captcha_answer", "")) == expected
|
|
except (ValueError, TypeError):
|
|
pass
|
|
|
|
if form.is_valid() and captcha_ok:
|
|
submission = form.save()
|
|
for uploaded_file, original_name in form.cleaned_data.get(
|
|
"attachments", []
|
|
):
|
|
attachment = ContactSubmissionAttachment(
|
|
submission=submission,
|
|
original_filename=original_name,
|
|
)
|
|
attachment.file.save(original_name, uploaded_file, save=True)
|
|
return JsonResponse({"success": True})
|
|
|
|
errors: dict = {}
|
|
if not captcha_ok:
|
|
errors["captcha"] = ["Incorrect answer — please try again."]
|
|
errors.update(
|
|
{field: [str(e) for e in errs] for field, errs in form.errors.items()}
|
|
)
|
|
return JsonResponse(
|
|
{"success": False, "errors": errors, "captcha_question": captcha_question},
|
|
status=400,
|
|
)
|
|
|
|
|
|
class CustomPageView(DetailView):
|
|
model = CustomPage
|
|
template_name = "pages/custom_page.html"
|
|
context_object_name = "custom_page"
|
|
slug_url_kwarg = "slug"
|
|
|
|
def get_queryset(self):
|
|
return CustomPage.objects.filter(is_published=True)
|
|
|
|
def get_context_data(self, **kwargs):
|
|
ctx = super().get_context_data(**kwargs)
|
|
ctx["page_sections"] = (
|
|
self.object.sections.filter(is_active=True)
|
|
.prefetch_related("items")
|
|
.order_by("order")
|
|
)
|
|
ctx["homepage_products"] = (
|
|
MainProduct.objects.filter(is_active=True, show_on_homepage=True)
|
|
.order_by("homepage_order", "order")
|
|
)
|
|
ctx["homepage_products_catalog"] = _homepage_products_catalog_queryset()
|
|
return ctx
|
|
|
|
|
|
class PageVideoArchiveView(ListView):
|
|
model = PageVideo
|
|
template_name = "videos/archive.html"
|
|
context_object_name = "videos"
|
|
paginate_by = VIDEO_ARCHIVE_PAGE_SIZE
|
|
|
|
PAGE_CONFIG = {
|
|
PageVideo.PAGE_CONTACT: ("Contact videos", "Guides and updates from the Tecvico team.", "pages:contact"),
|
|
PageVideo.PAGE_FAQ: ("FAQ videos", "Video answers to common questions.", "pages:faq"),
|
|
}
|
|
|
|
def get_page_config(self):
|
|
try:
|
|
return self.PAGE_CONFIG[self.kwargs["library"]]
|
|
except KeyError as exc:
|
|
raise Http404("Video library not found.") from exc
|
|
|
|
def get_queryset(self):
|
|
self.get_page_config()
|
|
return PageVideo.objects.filter(
|
|
page=self.kwargs["library"],
|
|
is_active=True,
|
|
).order_by("order", "pk")
|
|
|
|
def get_context_data(self, **kwargs):
|
|
context = super().get_context_data(**kwargs)
|
|
title, description, source_url_name = self.get_page_config()
|
|
context.update(
|
|
{
|
|
"library_title": title,
|
|
"library_description": description,
|
|
"library_back_url": reverse(source_url_name),
|
|
"library_back_label": "Back to page",
|
|
"pagination_range": context["paginator"].get_elided_page_range(context["page_obj"].number),
|
|
}
|
|
)
|
|
return context
|