diff options
Diffstat (limited to 'pyblackbird_cc/resources/views.py')
-rw-r--r-- | pyblackbird_cc/resources/views.py | 431 |
1 files changed, 0 insertions, 431 deletions
diff --git a/pyblackbird_cc/resources/views.py b/pyblackbird_cc/resources/views.py deleted file mode 100644 index 3bf3d22..0000000 --- a/pyblackbird_cc/resources/views.py +++ /dev/null @@ -1,431 +0,0 @@ -import logging -import os -import tempfile -from collections.abc import Generator -from dataclasses import dataclass -from typing import Optional - -from django.conf import settings -from django.contrib import messages -from django.contrib.auth.decorators import login_required -from django.core.paginator import Paginator -from django.db import IntegrityError -from django.db import transaction -from django.shortcuts import get_object_or_404 -from django.shortcuts import redirect -from django.shortcuts import render - -from . import services -from .forms import ResourceCreateForm -from .forms import ResourceUpdateMetadataForm -from .forms import ResourceUpdatePDFsForm -from .forms import ResourceUpdateThumbnailsForm -from .models import PDFPageSnapshot -from .models import PDFResource -from .models import Resource -from .models import ResourceCategory -from .models import ResourceSubcategory -from .s3 import get_presigned_obj_url -from .s3 import upload_files_to_s3 -from .s3 import upload_snapshotted_pages_to_s3 -from .s3 import upload_to_s3 - -logger = logging.getLogger(__name__) - - -# I want to create a dataclass here to hold the resource information to pass to the view -@dataclass -class ResourceInfo: - id: int - name: str - description: str - card_description: str - main_resource_category_name: str - main_resource_category_colour_css_class: str - main_resource_badge_foreground_colour: str - subcategories: str | None - age_range: str | None - pdf_filenames: list[str] - pdf_urls: list[Optional[str]] - snapshot_urls: dict[str, list[str]] - thumbnail_filenames: list[str] - thumbnail_urls: list[Optional[str]] - feature_slot: int - created: str - updated: str - - -@login_required -def create_featured(request): - return render(request, "resources/create_featured_resource.html") - - -def _extract_metadata_from_resource(resource_obj) -> ResourceInfo | None: - """ - This function extracts the resource information from the model object and returns it as a - ResourceInfo object - :param resource_obj: - :return: - """ - pdf_resource_filenames = [ - x.file_name for x in PDFResource.objects.filter(resource=resource_obj).all() - ] - pdf_resources = PDFResource.objects.filter(resource=resource_obj).all() - snapshot_dict = {} - for p in pdf_resources: - snapshot_dict[p.file_name] = [ - x.file_name for x in PDFPageSnapshot.objects.filter(pdf_file=p).all() - ] - snapshot_url_dict = {} - # Iterate through the snapshot dict and generate the URLs - for k, v in snapshot_dict.items(): - snapshot_url_dict[k] = [ - get_presigned_obj_url( - settings.AWS_STORAGE_BUCKET_NAME, - f"snapshotted_pages/{f}", - ) - for f in v - ] - pdf_urls = [ - get_presigned_obj_url(settings.AWS_STORAGE_BUCKET_NAME, f"pdfuploads/{f}") - for f in pdf_resource_filenames - ] - thumbnail_urls = [ - get_presigned_obj_url(settings.AWS_STORAGE_BUCKET_NAME, f"thumbnails/{f}") - for f in resource_obj.thumbnail_filenames - ] - try: - arc_name = resource_obj.subcategories.name if resource_obj.subcategories else None - return ResourceInfo( - id=resource_obj.id, - name=resource_obj.name, - description=resource_obj.description, - card_description=resource_obj.card_description, - main_resource_category_name=resource_obj.main_resource_category.name, - main_resource_category_colour_css_class=resource_obj.main_resource_category.colour_css_class, - main_resource_badge_foreground_colour=resource_obj.main_resource_category.badge_foreground_colour, - subcategories=arc_name, - age_range=resource_obj.age_range, - pdf_filenames=pdf_resource_filenames, - pdf_urls=pdf_urls, - snapshot_urls=snapshot_url_dict, - thumbnail_filenames=resource_obj.thumbnail_filenames, - thumbnail_urls=thumbnail_urls, - feature_slot=resource_obj.feature_slot, - created=resource_obj.created_at, - updated=resource_obj.updated_at, - ) - except Exception: - logging.exception("Error extracting resource information: ") - return None - - -@login_required -def index(request): - resource_objs = Resource.objects.all() - categories = ResourceCategory.objects.all() - category = request.GET.get("category", "all") - - resource_list = [_extract_metadata_from_resource(r) for r in resource_objs] - - # Create a separate queryset for Featured resources - featured_resources = [r for r in resource_list if r.feature_slot] - featured_resources = sorted(featured_resources, key=lambda resource: resource.feature_slot) - - if category != "all": - resource_list = [r for r in resource_list if r.main_resource_category_name == category] - - paginator = Paginator(resource_list, 20) - page_number = request.GET.get("page") - page_obj = paginator.get_page(page_number) - - context = { - "page_obj": page_obj, - "categories": categories, - "featured_resources": featured_resources, - "selected_category": category, - } - return render(request, "resources/resource_list.html", context) - - -def create_metadata(pdf_files) -> Generator[tuple[services.PDFMetadata, str], None, None]: - """ - Generates PDF metadata and snapshot images for a list of PDF files. - - This function takes a list of PDF file objects, creates temporary files for each one, - and then uses the `services.get_pdf_metadata_from_path` and `services.export_pages_as_images` functions to extract metadata and snapshot images for each PDF. - - The function yields a tuple containing the PDF metadata and a list of snapshot image paths for each PDF file. - - Args: - pdf_files (list[django.core.files.uploadedfile.InMemoryUploadedFile]): A list of PDF file objects. - - Yields: - tuple[servies.PDFMetadata, list[str]]: A tuple containing the PDF metadata and a list of snapshot image paths for each PDF file. - """ - with tempfile.TemporaryDirectory() as temp_dir: - for pdf_file in pdf_files: - file_path = os.path.join(temp_dir, pdf_file.name) - - with open(file_path, "wb") as temp_file: - for chunk in pdf_file.chunks(): - temp_file.write(chunk) - - metadata = services.get_pdf_metadata_from_path(file_path) - snapshot_images = services.export_pages_as_images(file_path) - - yield metadata, snapshot_images - - -@login_required -def create_resource(request): - if request.method == "POST": - form = ResourceCreateForm(request.POST, request.FILES) - - if form.is_valid(): - pdf_files = form.cleaned_data["pdf_files"] - thumbnail_files = form.cleaned_data["thumbnail_files"] - name = form.cleaned_data["name"] - description = form.cleaned_data["description"] - card_description = form.cleaned_data["card_description"] - resource_type = form.cleaned_data["resource_type"] - age_range = form.cleaned_data["age_range"] - curriculum = form.cleaned_data["curriculum"] - main_resource_category = form.cleaned_data["main_resource_category"] - subcategories = form.cleaned_data["subcategories"] - feature_slot = form.cleaned_data["feature_slot"] - - # We use get here because we know these categories exist - subcategories_objs = [ResourceSubcategory.objects.get(name=x) for x in subcategories] - - try: - with transaction.atomic(): - resource = Resource( - name=name, - description=description, - card_description=card_description, - resource_type=resource_type, - age_range=age_range, - curriculum=curriculum, - main_resource_category=main_resource_category, - feature_slot=feature_slot, - ) - resource.save() - resource.subcategories.set(subcategories_objs) - - metadata_generator = create_metadata(pdf_files) - snapshotted_pages = [] - - for metadata, snapshot_images in metadata_generator: - pdf_resource = PDFResource.objects.create( - resource=resource, - file_name=os.path.basename(metadata.file_name), - file_size=metadata.file_size, - ) - - for snapshot_image in snapshot_images: - PDFPageSnapshot.objects.create( - name="test", - file_name=os.path.basename(snapshot_image), - pdf_file=pdf_resource, - ) - - snapshotted_pages.append(snapshot_images) - - resource.thumbnail_filenames = [f.name for f in thumbnail_files] - resource.save() - - # Reset the file pointers for pdf_files - for pdf_file in pdf_files: - pdf_file.seek(0) - - if not upload_to_s3(pdf_files, thumbnail_files, snapshotted_pages): - raise Exception("Error uploading files to S3") - - return redirect("resources:resource_detail", resource_id=resource.id) - except IntegrityError: - slot = form.cleaned_data["feature_slot"] - messages.add_message( - request, - messages.ERROR, - f"Feature slot {slot} is already " - "in use. Quit this form and remove from existing " - "resource.", - ) - except Exception: - logger.exception("Error creating resource") - form.add_error(None, "An error occurred while creating the resource.") - else: - # extract form errors - errors = {} - for field in form: - if field.errors: - errors[field.name] = field.errors - - # add non-field errors - if form.non_field_errors(): - errors["non_field_errors"] = form.non_field_errors() - - # render form with errors - return render( - request, - "resources/resource_create.html", - {"form": form, "errors": errors}, - ) - else: - form = ResourceCreateForm() - - return render(request, "resources/resource_create.html", {"form": form}) - - -@login_required -def resource_detail(request, resource_id): - """ - This function returns the resource detail page. - """ - resource_obj = get_object_or_404(Resource, pk=resource_id) - resource_metadata = _extract_metadata_from_resource(resource_obj) - resource = { - "id": resource_obj.id, - "name": resource_obj.name, - "description": resource_obj.description, - "card_description": resource_obj.card_description, - "resource_type": resource_obj.resource_type.name, - "main_resource_category": resource_obj.main_resource_category.name, - "main_resource_category_colour_css_class": resource_obj.main_resource_category.colour_css_class, - "additional_resource_category": ( - resource_obj.subcategories.name if resource_obj.subcategories else None - ), - "age_range": resource_obj.age_range, - "curriculum": resource_obj.curriculum, - "pdf_filenames": resource_metadata.pdf_filenames, - "pdf_urls": resource_metadata.pdf_urls, - "thumbnails": list( - zip( - resource_metadata.thumbnail_urls, - resource_metadata.thumbnail_filenames, - strict=False, - ), - ), - "thumbnail_filenames": resource_metadata.thumbnail_filenames, - "thumbnail_urls": resource_metadata.thumbnail_urls, - "snapshot_urls": resource_metadata.snapshot_urls, - "created": resource_metadata.created, - "updated": resource_metadata.updated, - } - return render(request, "resources/resource_detail.html", {"resource": resource}) - - -@login_required() -def update_resource_thumbnails(request, pk): - resource = get_object_or_404(Resource, pk=pk) - if request.method == "POST": - form = ResourceUpdateThumbnailsForm(request.POST, request.FILES) - if form.is_valid(): - thumbnail_files = form.cleaned_data["thumbnail_files"] - resource.thumbnail_filenames = [f.name for f in thumbnail_files] - upload_files_to_s3(thumbnail_files, "thumbnails") - resource.save() - return redirect("resources:resource_detail", resource_id=resource.id) - - else: - form = ResourceUpdateThumbnailsForm(resource=pk) - - return render(request, "resources/update_thumbnails.html", {"form": form, "resource": resource}) - - -@login_required -def hx_download_button(request): - """ - This is an HTMX view that is called when the user clicks the download button. - :param: - :return: - """ - pdf = request.GET.get("rn") - res = Resource.objects.get(pdf_filename=pdf) - return render( - request, - "resources/hx_download_button.html", - {"pdf_url": _extract_metadata_from_resource(res).pdf_url}, - ) - - -@login_required -def update_resource_metadata(request, pk): # Change resource_id to pk - resource = get_object_or_404(Resource, pk=pk) - - if request.method == "POST": - form = ResourceUpdateMetadataForm(request.POST, instance=resource) - if form.is_valid(): - form.save() - return redirect( - "resources:resource_detail", - resource_id=resource.pk, - ) # Use pk instead of resource_id - else: - form = ResourceUpdateMetadataForm(instance=resource) - - return render( - request, - "resources/resource_metadata_update.html", - {"form": form, "resource": resource}, - ) - - -@login_required() -def add_resource_pdfs(request, pk): - """ - Adds PDF files to a resource in the system. - - This view handles the process of adding PDF files to an existing resource. - It allows the user to upload one or more PDF files, which are then processed and associated with the resource. - The view creates PDFResource and PDFPageSnapshot objects to represent the uploaded PDFs and their page snapshots, and uploads the files to S3 storage. - - Args: - request (django.http.request.HttpRequest): The HTTP request object. - pk (int): The primary key of the resource to which the PDFs will be added. - - Returns: - django.http.response.HttpResponse: A redirect to the resource detail page upon successful PDF upload. - """ - resource = get_object_or_404(Resource, pk=pk) - if request.method == "POST": - form = ResourceUpdatePDFsForm(resource.get_absolute_url(), request.POST, request.FILES) - if form.is_valid(): - pdf_files = form.cleaned_data["pdf_files"] - - metadata_generator = create_metadata(pdf_files) - - snapshotted_pages = [] - - for metadata, snapshot_images in metadata_generator: - # TODO replace or add? This needs to be decided here - pdf_resource = PDFResource.objects.create( - resource=resource, - file_name=os.path.basename(metadata.file_name), - file_size=metadata.file_size, - ) - - for snapshot_image in snapshot_images: - PDFPageSnapshot.objects.create( - name="test", - file_name=os.path.basename(snapshot_image), - pdf_file=pdf_resource, - ) - - snapshotted_pages.append(snapshot_images) - - # Reset the file pointers for pdf_files - for pdf_file in pdf_files: - pdf_file.seek(0) - - upload_files_to_s3(pdf_files, "pdfuploads") - if not upload_snapshotted_pages_to_s3(snapshotted_pages): - raise Exception("Error uploading snapshotted pages to S3") - - return redirect("resources:resource_detail", resource_id=resource.id) - - else: - form = ResourceUpdatePDFsForm(resource.get_absolute_url(), resource=pk) - - return render(request, "resources/update_pdfs.html", {"form": form, "resource": resource}) |