From fc855e1f0a9f9c09ea35db46d7507eb79e0c1304 Mon Sep 17 00:00:00 2001 From: Piotr Gawron <piotr.gawron@uni.lu> Date: Fri, 8 Sep 2017 10:18:48 +0200 Subject: [PATCH] export to excel --- .gitignore | 4 +++ requirements.txt | 6 ++++- smash/web/static/css/export.css | 9 +++++++ smash/web/templates/export/index.html | 25 ++++++++++++------- smash/web/templates/visits/add.html | 6 ----- smash/web/tests/test_view_export.py | 14 +++++++++-- smash/web/urls.py | 3 ++- smash/web/views/export.py | 35 ++++++++++++++++++++++----- 8 files changed, 77 insertions(+), 25 deletions(-) create mode 100644 smash/web/static/css/export.css diff --git a/.gitignore b/.gitignore index 5f846bdc..9eca034d 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,10 @@ smash/~/ # Disable python bytecode *.pyc +#vim swap files +*.swp +#vim backup files +*~ # Disable local developer settings local_settings.py diff --git a/requirements.txt b/requirements.txt index fa5a7c14..67644e19 100644 --- a/requirements.txt +++ b/requirements.txt @@ -8,4 +8,8 @@ python-docx==0.8.6 django-cleanup==0.4.2 django_cron==0.5.0 django-two-factor-auth==1.6.1 -nexmo \ No newline at end of file +nexmo +django-excel==0.0.9 +pyexcel-xls==0.5.0 +pyexcel==0.5.3 + diff --git a/smash/web/static/css/export.css b/smash/web/static/css/export.css new file mode 100644 index 00000000..ffab2323 --- /dev/null +++ b/smash/web/static/css/export.css @@ -0,0 +1,9 @@ +.cell { + display: table-cell; + border-bottom: 1px solid black; + border: 1px solid #BBBBBB; + padding:4px; + text-align: center; + vertical-align: middle; + +} diff --git a/smash/web/templates/export/index.html b/smash/web/templates/export/index.html index 8c050114..a4e8aef2 100644 --- a/smash/web/templates/export/index.html +++ b/smash/web/templates/export/index.html @@ -3,6 +3,7 @@ {% block styles %} {{ block.super }} + <link rel="stylesheet" href="{% static 'css/export.css' %}"> {% endblock styles %} {% block ui_active_tab %}'export'{% endblock ui_active_tab %} @@ -18,15 +19,21 @@ {% block maincontent %} <div> - <a href="{% url 'web.views.export_to_csv' 'subjects' %}" class="btn btn-app"> - <i class="fa fa-download"></i> - Subjects - </a> - <br/> - <a href="{% url 'web.views.export_to_csv' 'appointments' %}" class="btn btn-app"> - <i class="fa fa-download"></i> - Appointments - </a> + <h3>Subjects</h3> + <ul> + <li><a href="{% url 'web.views.export_to_excel' 'subjects' %}"><i class="fa fa-file-excel-o"></i> XLS - + Excel</a> + </li> + <li><a href="{% url 'web.views.export_to_excel' 'appointments' %}"><i class="fa fa-file-text-o"></i> CSV - + Text based</a></li> + </ul> + <h3>Appointments</h3> + <ul> + <li><a href="{% url 'web.views.export_to_csv' 'subjects' %}"><i class="fa fa-file-excel-o"></i> XLS - + Excel</a></li> + <li><a href="{% url 'web.views.export_to_csv' 'appointments' %}"><i class="fa fa-file-text-o"></i> CSV - + Text based</a></li> + </ul> </div> <div class="box-body"> diff --git a/smash/web/templates/visits/add.html b/smash/web/templates/visits/add.html index 9e03dbec..749641be 100644 --- a/smash/web/templates/visits/add.html +++ b/smash/web/templates/visits/add.html @@ -25,12 +25,6 @@ <a href="{% url 'web.views.visits' %}" class="btn btn-block btn-default">Cancel</a> </div> - {% comment %} - <div class="box-header with-border"> - <h3 class="box-title">Details of a visit</h3> - </div> - {% endcomment %} - <form method="post" action="" class="form-horizontal"> {% csrf_token %} diff --git a/smash/web/tests/test_view_export.py b/smash/web/tests/test_view_export.py index 1fc16657..f15c2cf4 100644 --- a/smash/web/tests/test_view_export.py +++ b/smash/web/tests/test_view_export.py @@ -6,12 +6,22 @@ from . import LoggedInTestCase class TestExportView(LoggedInTestCase): - def test_export_subjects(self): + def test_export_subjects_to_csv(self): create_subject() response = self.client.get(reverse('web.views.export_to_csv', kwargs={'data_type': "subjects"})) self.assertEqual(response.status_code, 200) - def test_export_appointments(self): + def test_export_appointments_to_csv(self): create_appointment() response = self.client.get(reverse('web.views.export_to_csv', kwargs={'data_type': "appointments"})) self.assertEqual(response.status_code, 200) + + def test_export_subjects_to_excel(self): + create_subject() + response = self.client.get(reverse('web.views.export_to_excel', kwargs={'data_type': "subjects"})) + self.assertEqual(response.status_code, 200) + + def test_export_appointments_to_excel(self): + create_appointment() + response = self.client.get(reverse('web.views.export_to_excel', kwargs={'data_type': "appointments"})) + self.assertEqual(response.status_code, 200) diff --git a/smash/web/urls.py b/smash/web/urls.py index c19857fc..a2e1a019 100644 --- a/smash/web/urls.py +++ b/smash/web/urls.py @@ -160,7 +160,8 @@ urlpatterns = [ #################### url(r'^export$', views.export.export, name='web.views.export'), - url(r'^export/(?P<data_type>[A-z]+)$', views.export.export_to_csv, name='web.views.export_to_csv'), + url(r'^export/csv/(?P<data_type>[A-z]+)$', views.export.export_to_csv, name='web.views.export_to_csv'), + url(r'^export/xls/(?P<data_type>[A-z]+)$', views.export.export_to_excel, name='web.views.export_to_excel'), #################### # CONFIGURATION # diff --git a/smash/web/views/export.py b/smash/web/views/export.py index e5a25053..359386c2 100644 --- a/smash/web/views/export.py +++ b/smash/web/views/export.py @@ -1,6 +1,7 @@ # coding=utf-8 import csv +import django_excel as excel from django.contrib.auth.decorators import login_required from django.http import HttpResponse @@ -24,7 +25,23 @@ def export_to_csv(request, data_type="subjects"): return e500_error(request) writer = csv.writer(response, quotechar=str(u'"'), quoting=csv.QUOTE_ALL) for row in data: - writer.writerow(row) + writer.writerow([s.encode("utf-8") for s in row]) + + return response + + +@login_required +def export_to_excel(request, data_type="subjects"): + filename = data_type + '-' + get_today_midnight_date().strftime("%Y-%m-%d") + ".xls" + if data_type == "subjects": + data = get_subjects_as_array() + elif data_type == "appointments": + data = get_appointments_as_array() + else: + return e500_error(request) + + response = excel.make_response_from_array(data, 'xls', file_name=filename) + response['Content-Disposition'] = 'attachment; filename="' + filename + '"' return response @@ -46,8 +63,11 @@ def get_subjects_as_array(): for subject in subjects: row = [] for field in subject_fields: - row.append(getattr(subject, field.name)) - result.append([unicode(s).replace("\n", ";").replace("\r", ";").encode("utf-8") for s in row]) + cell = getattr(subject, field.name) + if cell is None: + cell = "" + row.append(cell) + result.append([unicode(s).replace("\n", ";").replace("\r", ";") for s in row]) return result @@ -69,15 +89,18 @@ def get_appointments_as_array(): appointments = Appointment.objects.order_by('-datetime_when') for appointment in appointments: - row = [appointment.visit.subject.nd_number, appointment.visit.subject.last_name, - appointment.visit.subject.first_name, appointment.visit.follow_up_title()] + if appointment.visit is not None: + row = [appointment.visit.subject.nd_number, appointment.visit.subject.last_name, + appointment.visit.subject.first_name, appointment.visit.follow_up_title()] + else: + row = ["---", "---", "---", "---"] for field in appointments_fields: row.append(getattr(appointment, field.name)) type_string = "" for appointment_type in appointment.appointment_types.all(): type_string += appointment_type.code + "," row.append(type_string) - result.append([unicode(s).replace("\n", ";").replace("\r", ";").encode("utf-8") for s in row]) + result.append([unicode(s).replace("\n", ";").replace("\r", ";") for s in row]) return result -- GitLab