import mimetypes from django.http import FileResponse, Http404 from django.shortcuts import get_object_or_404 from django.urls import reverse from django.views import View from django.views.generic import DetailView, ListView, TemplateView from .models import Article, ArticleCitation, ArticleSection, MainProduct, ProductVideo, SubProduct, SubProductVersion from .release_assets import FILE_FIELD_BY_ASSET_KEY, RELEASE_ASSET_KEYS, URL_FIELD_BY_ASSET_KEY from .release_context import build_archive_context, build_release_context VIDEO_PREVIEW_LIMIT = 3 VIDEO_ARCHIVE_PAGE_SIZE = 6 def _product_video_preview(queryset, archive_url): total = queryset.count() return { "product_videos": queryset[:VIDEO_PREVIEW_LIMIT], "videos_total_count": total, "videos_archive_url": archive_url if total > VIDEO_PREVIEW_LIMIT else "", } class ReleaseAssetDownloadView(View): def get(self, request, version_id, asset): if asset not in RELEASE_ASSET_KEYS: raise Http404 version = get_object_or_404(SubProductVersion, pk=version_id, is_active=True) parent = version.sub_product or version.main_product if parent is None or not parent.is_active: raise Http404 url_field = URL_FIELD_BY_ASSET_KEY[asset] if (getattr(version, url_field) or "").strip(): raise Http404 file_field = FILE_FIELD_BY_ASSET_KEY[asset] release_file = getattr(version, file_field) if not release_file: raise Http404 filename = version.release_download_filename(asset) content_type, _ = mimetypes.guess_type(filename) return FileResponse( release_file.open("rb"), as_attachment=True, filename=filename, content_type=content_type or "application/octet-stream", ) class ProductOverviewView(ListView): model = MainProduct template_name = "products/overview.html" context_object_name = "main_products" queryset = MainProduct.objects.filter(is_active=True).prefetch_related( "sub_products" ) class MainProductDetailView(DetailView): model = MainProduct template_name = "products/main_detail.html" context_object_name = "main_product" slug_url_kwarg = "main_slug" queryset = MainProduct.objects.filter(is_active=True).prefetch_related( "sub_products" ) def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) context["articles"] = self.object.articles.prefetch_related( "sections", "citations" ).all() videos = self.object.videos.filter(is_active=True).order_by("order", "pk") context.update( _product_video_preview( videos, reverse("products:main_product_videos", kwargs={"main_slug": self.object.slug}), ) ) release_context = build_release_context( self.object.distribution, self.object.versions.filter(is_active=True), self.object, ) release_context["versions_archive_url"] = self.object.get_versions_archive_url() context.update(release_context) return context class MainProductOlderVersionsView(TemplateView): template_name = "products/versions_archive.html" def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) main_product = get_object_or_404( MainProduct, slug=self.kwargs["main_slug"], is_active=True, ) active_versions = main_product.versions.filter(is_active=True) context["main_product"] = main_product context["sub_product"] = None context["product_detail_url"] = main_product.get_absolute_url() context.update( build_archive_context( main_product.distribution, active_versions, main_product ) ) return context class SubProductDetailView(TemplateView): template_name = "products/sub_detail.html" def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) main_product = get_object_or_404( MainProduct, slug=self.kwargs["main_slug"], is_active=True, ) sub_product = get_object_or_404( SubProduct, slug=self.kwargs["sub_slug"], main_product=main_product, is_active=True, ) context["main_product"] = main_product context["sub_product"] = sub_product context["articles"] = sub_product.articles.prefetch_related( "sections", "citations" ).all() videos = sub_product.videos.filter(is_active=True).order_by("order", "pk") context.update( _product_video_preview( videos, reverse( "products:sub_product_videos", kwargs={"main_slug": main_product.slug, "sub_slug": sub_product.slug}, ), ) ) context["siblings"] = ( SubProduct.objects.filter(main_product=main_product, is_active=True) .exclude(pk=sub_product.pk) .order_by("order", "name") ) active_versions = sub_product.versions.filter(is_active=True) release_context = build_release_context( sub_product.distribution, active_versions, sub_product ) release_context["versions_archive_url"] = sub_product.get_versions_archive_url() context.update(release_context) return context class ProductVideoArchiveView(ListView): model = ProductVideo template_name = "videos/archive.html" context_object_name = "videos" paginate_by = VIDEO_ARCHIVE_PAGE_SIZE def get_parent(self): main_product = get_object_or_404( MainProduct, slug=self.kwargs["main_slug"], is_active=True, ) sub_slug = self.kwargs.get("sub_slug") if sub_slug: return get_object_or_404( SubProduct, slug=sub_slug, main_product=main_product, is_active=True, ) return main_product def get_queryset(self): self.parent_object = self.get_parent() return self.parent_object.videos.filter(is_active=True).order_by("order", "pk") def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) context.update( { "library_title": f"{self.parent_object.name} videos", "library_description": "Tutorials, demonstrations, and technical guides.", "library_back_url": self.parent_object.get_absolute_url(), "library_back_label": f"Back to {self.parent_object.name}", "pagination_range": context["paginator"].get_elided_page_range(context["page_obj"].number), } ) return context class SubProductOlderVersionsView(TemplateView): template_name = "products/versions_archive.html" def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) main_product = get_object_or_404( MainProduct, slug=self.kwargs["main_slug"], is_active=True, ) sub_product = get_object_or_404( SubProduct, slug=self.kwargs["sub_slug"], main_product=main_product, is_active=True, ) active_versions = sub_product.versions.filter(is_active=True) context["main_product"] = main_product context["sub_product"] = sub_product context["product_detail_url"] = sub_product.get_absolute_url() context.update( build_archive_context( sub_product.distribution, active_versions, sub_product ) ) return context