From 0b5be72725ffd6cd10490fbd202388e22c7f8522 Mon Sep 17 00:00:00 2001
From: Andrea Lepori <aleporia@gmail.com>
Date: Tue, 12 Nov 2024 15:27:12 +0100
Subject: commit edits for new feature

---
 requirements.txt                           |   1 +
 server/templates/server/data_download.html | 102 +++++++++++++++++++++++++++++
 server/templates/server/doc_type.html      |   2 +-
 server/urls.py                             |   2 +
 server/views.py                            |  84 +++++++++++++++++++++++-
 templates/registration/base_admin.html     |   5 ++
 version.txt                                |   2 +-
 7 files changed, 195 insertions(+), 3 deletions(-)
 create mode 100644 server/templates/server/data_download.html

diff --git a/requirements.txt b/requirements.txt
index 05c1064..99eaa57 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -8,3 +8,4 @@ django-debug-toolbar
 requests
 authlib
 django-impersonate
+pypdf
diff --git a/server/templates/server/data_download.html b/server/templates/server/data_download.html
new file mode 100644
index 0000000..59b180d
--- /dev/null
+++ b/server/templates/server/data_download.html
@@ -0,0 +1,102 @@
+{% extends 'registration/base_admin.html' %}
+
+{% block title %}Admin - Richiesta dati{% endblock %}
+
+{% block breadcrumb %}
+  <a href="{% url 'server'%}" class="breadcrumb hide-on-med-and-down">Admin</a>
+  <a class="breadcrumb hide-on-med-and-down">Richiesta dati</a>
+{% endblock %}
+
+{% block content %}
+
+<div class="row">
+  <div class="col l4 offset-l4 m8 offset-m2 s12">
+    <div class="card">
+        <div class="card-content">
+          <div class="row">
+            <div class="col s12">
+              <div class="chips" id="doc_search"></div>
+            </div>
+          </div>
+          <div class="row">
+            <div class="col s12">
+              <p>
+                <label>
+                  <input class="filled-in" name="personal" type="checkbox"/>
+                  <span>Dati personali</span>
+                </label>
+              </p>
+              <p>
+                <label>
+                  <input class="filled-in" name="medic" type="checkbox"/>
+                  <span>Dati medici</span>
+                </label>
+              </p>
+              <p>
+                <label>
+                  <input class="filled-in" name="attachments" type="checkbox"/>
+                  <span>Allegati (vaccinazioni e tessera cassa malati)</span>
+                </label>
+              </p>
+            </div>
+          </div>
+          <div class="row">
+            <div class="col s12">
+              <a onclick="send()" class="waves-effect waves-light btn {{color}}"><i class="material-icons left">download</i>Scarica</a> 
+            </div>
+          </div>
+          <div class="row" id="loading" hidden>
+            <div class="col s12">
+              <h6 id="progress_text">Preparazione dei documenti</h6>
+              <div class="progress">
+                  <div id="progress_bar" class="indeterminate"></div>
+                  <div class="determinate" style="width: 0%"></div>
+              </div>
+              <p id="under_text"></p>
+            </div>
+          </div>
+        </div>
+    </div>
+  </div>
+</div>
+{% endblock %}
+
+{% block script %}
+$(document).ready(function(){
+    {% if error %}
+      M.toast({html: '{{ error }}', classes: 'orange'})
+    {% endif %}
+});
+
+document.addEventListener('DOMContentLoaded', function() {
+  var options = {
+    placeholder: 'Documento',
+    autocompleteOptions: {
+      data: {
+        {% for doc in docs %}
+          '{{ doc.name }}': null,
+        {% endfor %}
+      },
+    },
+    limit: 1,
+  };
+
+  var elems = document.querySelectorAll('.chips');
+  var instances = M.Chips.init(elems, options);
+});
+
+function send() {
+  var chips = M.Chips.getInstance(document.getElementById('doc_search'));
+  var personal = document.getElementsByName('personal')[0].checked;
+  var medic = document.getElementsByName('medic')[0].checked;
+  var attachments = document.getElementsByName('attachments')[0].checked;
+
+  var loadbox = document.getElementById('loading');
+  var progress = document.getElementById('progress_bar');
+  var progress_text = document.getElementById('progress_text');
+  var under_text = document.getElementById('under_text');
+
+  loadbox.hidden = false;
+
+}
+{% endblock%}
\ No newline at end of file
diff --git a/server/templates/server/doc_type.html b/server/templates/server/doc_type.html
index 35dd9f3..023ea5e 100644
--- a/server/templates/server/doc_type.html
+++ b/server/templates/server/doc_type.html
@@ -147,7 +147,7 @@
         <div class="collapsible-body"><span>
           <div class="hide-on-med-and-down">
             <a class="waves-effect waves-light btn {{color}}" onclick="send('e{{doctype.id}}')"><i class="material-icons left">edit</i>Modifica tipo</a>
-            <a class="waves-effect waves-light btn {{color}}" onclick="send('p{{doctype.id}}')"><i class="material-icons left">file_download</i>Scarica CSV</a>
+            <a class="waves-effect waves-light btn {{color}}" onclick="send('d{{doctype.id}}')"><i class="material-icons left">file_download</i>Scarica CSV</a>
             {% if doctype.medical_data %}
             <a class="waves-effect waves-light btn {{color}}" onclick="send('m{{doctype.id}}')"><i class="material-icons left">download_for_offline</i>Scarica CSV (con dati medici)</a>
             {% endif %}
diff --git a/server/urls.py b/server/urls.py
index 5937707..7906dfa 100644
--- a/server/urls.py
+++ b/server/urls.py
@@ -22,5 +22,7 @@ urlpatterns = [
     path('progress', views.get_progress, name='progress'),
     path('request', views.data_request, name='request'),
     path('debug-uc', views.debug_uc, name='debug-uc'),
+    path('download-data', views.download_data, name='download-data'),
+    path('data-progress', views.download_data_progress, name='data-progress'),
     path('media/<int:id>/<str:t>/<str:flag>', views.media_request, name='media'),
 ]
diff --git a/server/views.py b/server/views.py
index ef13e45..b9e2dc7 100644
--- a/server/views.py
+++ b/server/views.py
@@ -28,6 +28,7 @@ from PIL import Image, UnidentifiedImageError
 import zipfile
 import json
 import threading
+from pypdf import PdfMerger
 
 # custom staff check function for non primary group staff members
 def isStaff(user):
@@ -416,7 +417,7 @@ def ulist(request):
                 # get template and build context
                 template = get_template('server/download_doc.html')
                 doc = [document, KeyVal.objects.filter(
-                    container=document), document.personal_data, document.medical_data, document.user.groups.values_list('name', flat=True)[0]]
+                    container=document), document.personal_data, document.medical_data]
                 context = {'doc': doc, 'vac': vac_file,
                            'health': health_file, 'sign_doc_file': sign_doc_file}
                 # render context
@@ -1898,6 +1899,87 @@ def debug_uc(request):
     }
     return render(request, 'server/debug_usercode.html', context)
 
+@user_passes_test(isStaff)
+def download_data(request):
+    groups = getGroups(request)
+
+    # get all doctypes the user has access to
+    doctypes = DocumentType.objects.filter(group__in=groups)
+    # sort by name
+    doctypes = doctypes.order_by("name")
+
+    context = {
+        "docs": doctypes
+    }
+
+    return render(request, 'server/data_download.html', context)
+
+def download_data_progress(request):
+    # if user wants to download result
+    if 'download' in request.GET:
+        # if job is completed
+        if request.session['status']:
+            data = BytesIO(base64.b64decode(request.session['result']))
+            data.seek(0)
+            return FileResponse(data, as_attachment=True, filename="documents_" + datetime.now().strftime("%H_%M-%d_%m_%y") + ".pdf")
+
+    # otherwise return status
+    data = [request.session['progress'], request.session['total'], request.session['status']]
+    return HttpResponse(json.dumps(data))
+
+def prepare_documents(docs, session_key):
+    pdf_list = []
+
+    # get session
+    session = SessionStore(session_key=session_key)
+    for i in docs:
+        vac_file = ""
+        health_file = ""
+        sign_doc_file = ""
+
+        # prepare pictures in base64
+        if i.medical_data:
+            if i.medical_data.vac_certificate.name:
+                with open(i.medical_data.vac_certificate.name, 'rb') as image_file:
+                    vac_file = base64.b64encode(
+                        image_file.read()).decode()
+
+            if i.medical_data.health_care_certificate.name:
+                with open(i.medical_data.health_care_certificate.name, 'rb') as image_file:
+                    health_file = base64.b64encode(
+                        image_file.read()).decode()
+        if i.signed_doc:
+            with open(i.signed_doc.name, 'rb') as image_file:
+                sign_doc_file = base64.b64encode(
+                    image_file.read()).decode()
+
+        template = get_template('server/download_doc.html')
+        doc = [i, KeyVal.objects.filter(
+            container=i), i.personal_data, i.medical_data]
+        context = {'doc': doc, 'vac': vac_file,
+                    'health': health_file, 'sign_doc_file': sign_doc_file}
+        # render context
+        html = template.render(context)
+        # render pdf using wkhtmltopdf
+        pdf = pdfkit.from_string(html, False)
+        # append file
+        pdf_list.append(pdf)
+
+        session['progress'] += 1
+        session.save()
+
+    merger = PdfMerger()
+    for pdf in pdf_list:
+        merger.append(BytesIO(pdf))
+
+    mem = BytesIO()
+    merger.write(mem)
+    mem.seek(0)
+    # save result
+    session['result'] = base64.b64encode(mem.getvalue()).decode()
+    session['status'] = True
+    session.save()
+
 @user_passes_test(isStaff)
 def data_request(request):
     context = {}
diff --git a/templates/registration/base_admin.html b/templates/registration/base_admin.html
index 9643e35..c414fea 100644
--- a/templates/registration/base_admin.html
+++ b/templates/registration/base_admin.html
@@ -19,6 +19,11 @@
       box-shadow: 0 1px 0 0 {{hexcolor}} !important;
     }
 
+    .chips.focus {
+      border-bottom: 1px solid {{hexcolor}} !important;
+      box-shadow: 0 1px 0 0 {{hexcolor}} !important;
+    }
+
     input[type=password]:focus, .materialize-textarea:focus:not([readonly]) {
       border-bottom: 1px solid {{hexcolor}} !important;
       box-shadow: 0 1px 0 0 {{hexcolor}} !important;
diff --git a/version.txt b/version.txt
index c0053a4..153f555 100644
--- a/version.txt
+++ b/version.txt
@@ -1,2 +1,2 @@
 version=0.7
-rev=26
+rev=27
-- 
cgit v1.2.1