diff --git a/atdb/requirements/base.txt b/atdb/requirements/base.txt index 38935ee2a06d85b69c5d5bafc3a26dfeb44da837..eb63b89a13274589c3866ed061fdba550b169cb4 100644 --- a/atdb/requirements/base.txt +++ b/atdb/requirements/base.txt @@ -18,3 +18,4 @@ requests-oauthlib==1.3.1 six==1.15.0 whitenoise==5.0.1 pytz==2022.6 +xhtml2pdf==0.2.15 \ No newline at end of file diff --git a/atdb/taskdatabase/urls.py b/atdb/taskdatabase/urls.py index 8197a156896420b5850ab71f0036d5db6dc875e3..26a4b3a9912f2410f962d386a617c71df10a1434 100644 --- a/atdb/taskdatabase/urls.py +++ b/atdb/taskdatabase/urls.py @@ -104,6 +104,7 @@ urlpatterns = [ # ancillary dataproducts endpoints (retrieved by archiver service for copy to LTA dcache). path('get_summary_html/<sas_id>', views.GetSummaryHtml, name='get-summary-html'), path('get_summary_json/<sas_id>', views.GetSummaryJson, name='get-summary-json'), + path('get_summary_pdf/<sas_id>', views.GetSummaryPdf, name='get-summary-pdf'), # --- controller resources --- path('tasks/<int:pk>/setstatus/<new_status>/<page>', views.TaskSetStatus, name='task-setstatus-view'), diff --git a/atdb/taskdatabase/views.py b/atdb/taskdatabase/views.py index de0520ad734d990c4060b5d6e5d50d01652ea73b..d77a03781f9bad89b00cf93ce41da6c53020c16a 100644 --- a/atdb/taskdatabase/views.py +++ b/atdb/taskdatabase/views.py @@ -15,6 +15,7 @@ from django.contrib.auth.decorators import login_required from django.views.generic import ListView from django.contrib import messages + from rest_framework import generics from rest_framework.response import Response from django.http import JsonResponse, HttpResponse @@ -26,9 +27,8 @@ from django_tables2.views import SingleTableMixin from django.shortcuts import render, redirect, reverse from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger from django.contrib.admin.views.decorators import staff_member_required -from django.db.models import Count, F, Func, ExpressionWrapper, DurationField -from django.db.models.functions import TruncHour, TruncDay -from django.db.models import IntegerField, DateTimeField +from xhtml2pdf import pisa +from io import BytesIO from rest_framework.request import Request #from silk.profiling.profiler import silk_profile @@ -1716,6 +1716,15 @@ def GetSummaryHtml(request, sas_id): queryset = Task.objects.filter(sas_id=sas_id) task = queryset[0] + # this would be the django template way of combining a html template with context. + # but that didn't add any of the styling, so I did it the basic way (see below). + # TODO: remove the commented out code later + + # template = get_template("taskdatabase/validation/summary.html") + # summary_html = algorithms.construct_summary(task) + # context = {'task': task, 'my_summary': summary_html} + # html = template.render(context) + # add some basic layout without using the summary.html template head_html=""" <head> @@ -1751,6 +1760,63 @@ def GetSummaryHtml(request, sas_id): }) +def GetSummaryPdf(request, sas_id): + """ + Construct and return a summary html structure for given sas_id + This is the same informtion and algorithm as used when the user clicks the SUM button on the Validation page. + See documentation: https://drive.google.com/file/d/16R8L06OFiKHFHBUA6FhrNVZVAaQBC2tU/view?usp=sharing + + example: /atdb/get_summary_pdf/606942 + """ + try: + + # use a trick to be able to use the existing task based code + queryset = Task.objects.filter(sas_id=sas_id) + task = queryset[0] + + # add some basic layout without using the summary.html template + head_html = """ + <head> + <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css"> + <link href='https://fonts.googleapis.com/css?family=Raleway' rel='stylesheet' type='text/css'> + </head> + """ + + body_html = """ + <html> + <body> + <div class="container-fluid details-container"> + <div class='card'><table> + <div class="card-body"> + <table class="table table-striped"> + """ + + # create the core html + summary_html = algorithms.construct_summary(task) + + # close all the tags + footer_html = "</table></div></div></div></body></html>" + + # combine the html sections + html = head_html + body_html + summary_html + footer_html + + # Create a BytesIO object to receive the PDF data + result = BytesIO() + + # Convert HTML to PDF + # TODO: fonts zijn fout in dev (ook op production?) + pdf = pisa.pisaDocument(BytesIO(html.encode("UTF-8")), result) + pdf_name = sas_id + '_summary.pdf' + if not pdf.err: + # Return the PDF as a response + response = HttpResponse(result.getvalue(), content_type='application/pdf') + response['Content-Disposition'] = f'attachment; filename={pdf_name}' + return response + + except: + return HttpResponse(f'Failed to generate PDF: {pdf.err}') + + def GetSummaryJson(request, sas_id): """ Construct and return a summary json structure for given sas_id