diff --git a/smash/web/api_urls.py b/smash/web/api_urls.py index 1efb028f94a31f4d4c27d32a536c4a4d88d7d65e..6776cbcef22c105d28233688b1ff58581f153aa1 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 0000000000000000000000000000000000000000..575e6b50cdb7ddd485ff25040b1b17d746da37a3 --- /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 782f8d5ba79e2aa93e8361023872a950f9acad2c..e6261de6fc6c19c4cc3787da5bc298180f13664d 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 0000000000000000000000000000000000000000..2fcd41c470b5c816c8f50aaeee5085da983df710 --- /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 c13a07129603005fb0a2d6cb508573299822d5cb..c7e8f13c3c6b00025e70cb01e07dcf3ed8347a83 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')