From e564a4df440700283264864e829f2fc555df378c Mon Sep 17 00:00:00 2001
From: Piotr Gawron <piotr.gawron@uni.lu>
Date: Mon, 3 Apr 2017 16:01:37 +0200
Subject: [PATCH] list of appointments for unfinished appointments is
 dynamically loaded

---
 smash/web/api_urls.py                      | 20 +++--
 smash/web/api_views/appointment.py         | 95 ++++++++++++++++++++++
 smash/web/templates/appointments/list.html | 76 +++++++++--------
 smash/web/tests/test_api_appointment.py    | 41 ++++++++++
 smash/web/views/appointment.py             |  9 +-
 5 files changed, 200 insertions(+), 41 deletions(-)
 create mode 100644 smash/web/api_views/appointment.py
 create mode 100644 smash/web/tests/test_api_appointment.py

diff --git a/smash/web/api_urls.py b/smash/web/api_urls.py
index 1efb028f..6776cbce 100644
--- a/smash/web/api_urls.py
+++ b/smash/web/api_urls.py
@@ -15,15 +15,25 @@ Including another URLconf
 """
 from django.conf.urls import url
 
-from web.api_views import worker, location, subject, appointment_type
+from web.api_views import worker, location, subject, appointment_type, appointment
 
 urlpatterns = [
+    # appointments
+    url(r'^appointments/(?P<type>[A-z]+)$', appointment.appointments, name='web.api.appointments'),
+
+    # appointment types
+    url(r'^appointment_types$', appointment_type.appointment_types, name='web.api.appointment_types'),
+
+    # subjects data
     url(r'^cities$', subject.cities, name='web.api.cities'),
     url(r'^countries$', subject.countries, name='web.api.countries'),
-    url(r'^specializations$', worker.specializations, name='web.api.specializations'),
-    url(r'^units$', worker.units, name='web.api.units'),
-    url(r'^locations$', location.locations, name='web.api.locations'),
     url(r'^referrals$', subject.referrals, name='web.api.referrals'),
-    url(r'^appointment_types$', appointment_type.appointment_types, name='web.api.appointment_types'),
     url(r'^subjects/(?P<type>[A-z]+)$', subject.subjects, name='web.api.subjects'),
+
+    # locations
+    url(r'^locations$', location.locations, name='web.api.locations'),
+
+    # worker data
+    url(r'^specializations$', worker.specializations, name='web.api.specializations'),
+    url(r'^units$', worker.units, name='web.api.units'),
 ]
diff --git a/smash/web/api_views/appointment.py b/smash/web/api_views/appointment.py
new file mode 100644
index 00000000..575e6b50
--- /dev/null
+++ b/smash/web/api_views/appointment.py
@@ -0,0 +1,95 @@
+import traceback
+
+from django.contrib.auth.decorators import login_required
+from django.http import JsonResponse
+
+from web.models import Appointment
+from web.views import e500_error
+from web.views.appointment import APPOINTMENT_LIST_GENERIC, APPOINTMENT_LIST_UNFINISHED, APPOINTMENT_LIST_APPROACHING
+from web.views.notifications import get_filter_locations, \
+    get_today_midnight_date, \
+    get_unfinished_appointments
+
+
+@login_required
+def get_appointments(request, type):
+    if type == APPOINTMENT_LIST_GENERIC:
+        return Appointment.objects.filter(location__in=get_filter_locations(request.user)).order_by("-datetime_when")
+    elif type == APPOINTMENT_LIST_UNFINISHED:
+        return get_unfinished_appointments(request.user).order_by("-datetime_when")
+    elif type == APPOINTMENT_LIST_APPROACHING:
+        return Appointment.objects.filter(
+            datetime_when__gt=get_today_midnight_date(),
+            location__in=get_filter_locations(request.user),
+            status=Appointment.APPOINTMENT_STATUS_SCHEDULED
+        ).order_by("-datetime_when")
+    else:
+        raise TypeError("Unknown query type: " + type)
+
+
+@login_required
+def appointments(request, type):
+    try:
+        # id of the query from dataTable: https://datatables.net/manual/server-side
+        draw = int(request.GET.get("draw", "-1"))
+
+        start = int(request.GET.get("start", "0"))
+        length = int(request.GET.get("length", "10"))
+        order = int(request.GET.get("order[0][column]", "0"))
+
+        filters = []
+        column_id = 0
+        while request.GET.get("columns[" + str(column_id) + "][search][value]", "unknown") != "unknown":
+            val = request.GET.get("columns[" + str(column_id) + "][search][value]", "unknown")
+            if val != "":
+                filters.append([request.GET.get("columns[" + str(column_id) + "][data]"), val])
+            column_id += 1
+
+        all_appointments = get_appointments(request, type)
+
+        count = all_appointments.count()
+
+        sliced_subjects = all_appointments[start:(start + length)]
+
+        appointments = sliced_subjects
+
+        count_filtered = all_appointments.count()
+
+        data = []
+        for appointment in appointments:
+            data.append(serialize_appointment(appointment))
+
+        return JsonResponse({
+            "draw": draw,
+            "recordsTotal": count,
+            "recordsFiltered": count_filtered,
+            "data": data,
+        })
+    except:
+        traceback.print_exc()
+        return e500_error(request)
+
+
+def serialize_appointment(appointment):
+    subject = ""
+    if appointment.visit is not None:
+        title = appointment.visit.follow_up_title()
+        subject = appointment.visit.subject.first_name + " " + appointment.visit.subject.last_name + " (" + appointment.visit.subject.nd_number + ")"
+    else:
+        title = appointment.comment
+
+    type = ""
+    for appointment_type in appointment.appointment_types.all():
+        type += appointment_type.code + ", "
+    time = ""
+    if appointment.datetime_when is not None:
+        time = appointment.datetime_when.strftime("%Y-%m-%d")
+    result = {
+        "subject": subject,
+        "title": title,
+        "type": type,
+        "datetime_when": time,
+        "comment": appointment.comment,
+        "id": appointment.id,
+    }
+    return result
diff --git a/smash/web/templates/appointments/list.html b/smash/web/templates/appointments/list.html
index 782f8d5b..e6261de6 100644
--- a/smash/web/templates/appointments/list.html
+++ b/smash/web/templates/appointments/list.html
@@ -6,9 +6,6 @@
     <!-- DataTables -->
     <link rel="stylesheet" href="{% static 'AdminLTE/plugins/datatables/dataTables.bootstrap.css' %}">
 
-    <!-- fullCalendar 2.2.5-->
-    <link rel="stylesheet" href="{% static 'AdminLTE/plugins/fullcalendar/fullcalendar.min.css' %}">
-    <link rel="stylesheet" href="{% static 'AdminLTE/plugins/fullcalendar/fullcalendar.print.css' %}" media="print">
 {% endblock styles %}
 
 {% block ui_active_tab %}'appointments'{% endblock ui_active_tab %}
@@ -24,7 +21,7 @@
 {% block maincontent %}
     <div class="row">
         <div class="col-md-16">
-            <table id="approaching_table" class="table table-bordered table-striped">
+            <table id="table" class="table table-bordered table-striped">
                 <thead>
                 <tr>
                     <th>Subject</th>
@@ -32,41 +29,54 @@
                     <th>Type</th>
                     <th>Date</th>
                     <th>Details</th>
+                    <th>Edit</th>
                 </tr>
                 </thead>
                 <tbody>
-
-                {% for approach in appointment_list %}
-                    <tr>
-                        {% if approach.visit  == None %}
-                            <td>
-                            </td>
-                            <td>
-                            </td>
-                        {% else %}
-                            <td>
-                                {{ approach.visit.subject.first_name }} {{ approach.visit.subject.last_name }}
-                                ({{ approach.visit.subject.nd_number }})
-                            </td>
-                            <td>
-                                {{ approach.visit.follow_up_title }}
-                            </td>
-                        {% endif %}
-                        <td>
-                            {% for type in approach.appointment_types.all %}
-                                {{ type.code }},
-                            {% endfor %}
-                        </td>
-                        <td>{{ approach.datetime_when | date:"Y-m-d H:i" }}</td>
-                        <td>
-                            <a href="{% url 'web.views.appointment_edit' approach.id %}" type="button"
-                               class="btn btn-block btn-default">Details</a>
-                        </td>
-                    </tr>
-                {% endfor %}
                 </tbody>
+                <tfoot style="display: table-header-group;"/>
             </table>
 
         </div>
     </div>
 {% endblock maincontent %}
+
+{% block scripts %}
+    {{ block.super }}
+
+    <script src="{% static 'AdminLTE/plugins/datatables/jquery.dataTables.min.js' %}"></script>
+    <script src="{% static 'AdminLTE/plugins/datatables/dataTables.bootstrap.min.js' %}"></script>
+
+    <script>
+        $(function () {
+            var table = $('#table').DataTable({
+                serverSide: true,
+                processing: true,
+                ordering: false,
+                ajax: "{% url 'web.api.appointments' list_type %}",
+                columns: [
+                    {"data": "subject"},
+                    {"data": "title"},
+                    {"data": "type"},
+                    {"data": "datetime_when"},
+                    {"data": "comment"},
+                    {"data": null},
+                ],
+                columnDefs: [{
+                    "targets": 5,
+                    "data": "id",
+                    "defaultContent": '<a href="#" type="button" class="btn btn-block btn-default">Edit</a>'
+                }]
+            });
+
+            $('#table tbody').on('click', 'a', function () {
+                var data = table.row($(this).parents('tr')).data();
+                var url = "{% url 'web.views.appointment_edit' 12345 %}".replace(/12345/, data.id.toString());
+                window.location.href = url;
+            });
+
+            $('#table_filter').css("display", "none");
+        });
+    </script>
+
+{% endblock scripts %}
diff --git a/smash/web/tests/test_api_appointment.py b/smash/web/tests/test_api_appointment.py
new file mode 100644
index 00000000..2fcd41c4
--- /dev/null
+++ b/smash/web/tests/test_api_appointment.py
@@ -0,0 +1,41 @@
+# coding=utf-8
+
+from django.contrib.auth.models import User
+from django.test import Client
+from django.test import TestCase
+from django.urls import reverse
+
+from web.tests.functions import create_subject, create_worker, create_visit, create_appointment
+from web.views.appointment import APPOINTMENT_LIST_GENERIC
+
+
+class TestApi(TestCase):
+    def setUp(self):
+        self.subject = create_subject()
+        self.client = Client()
+        username = 'piotr'
+        password = 'top_secret'
+        self.user = User.objects.create_user(
+            username=username, email='jacob@bla', password=password)
+        self.worker = create_worker(self.user)
+        self.client.login(username=username, password=password)
+
+    def test_appointments_invalid(self):
+        response = self.client.get(reverse('web.api.appointments', kwargs={'type': "bla"}))
+        self.assertEqual(response.status_code, 500)
+
+    def test_appointments_valid(self):
+        name = "Piotrek"
+        self.subject.first_name = name
+        self.subject.save()
+        visit = create_visit(self.subject)
+        create_appointment(visit)
+        appointment2 = create_appointment(visit)
+        appointment2.visit = None
+        appointment2.save()
+
+        url = reverse('web.api.appointments', kwargs={'type': APPOINTMENT_LIST_GENERIC})
+        response = self.client.get(url)
+
+        self.assertEqual(response.status_code, 200)
+        self.assertTrue(name in response.content)
diff --git a/smash/web/views/appointment.py b/smash/web/views/appointment.py
index c13a0712..c7e8f13c 100644
--- a/smash/web/views/appointment.py
+++ b/smash/web/views/appointment.py
@@ -7,6 +7,10 @@ from . import wrap_response
 from ..forms import AppointmentDetailForm, AppointmentAddForm, AppointmentEditForm, SubjectEditForm
 from ..models import Appointment
 
+APPOINTMENT_LIST_GENERIC = "GENERIC"
+APPOINTMENT_LIST_UNFINISHED = "UNFINISHED"
+APPOINTMENT_LIST_APPROACHING = "APPROACHING"
+
 
 def appointments(request):
     approaching_list = Appointment.objects.filter(
@@ -26,9 +30,8 @@ def appointments(request):
 
 
 def unfinished_appointments(request):
-    appointments = get_unfinished_appointments(request.user)
     context = {
-        'appointment_list': appointments,
+        'list_type': APPOINTMENT_LIST_UNFINISHED,
     }
 
     return wrap_response(request, "appointments/list.html", context)
@@ -84,7 +87,7 @@ def appointment_edit(request, id):
                 subject_form.save()
             the_appointment = get_object_or_404(Appointment, id=id)
             if (the_appointment.status != Appointment.APPOINTMENT_STATUS_SCHEDULED) and (
-                the_appointment.visit is not None):
+                        the_appointment.visit is not None):
                 return redirect('web.views.visit_details', id=the_appointment.visit.id)
             else:
                 return redirect('web.views.appointments')
-- 
GitLab