diff --git a/smash/web/api_views/serialization_utils.py b/smash/web/api_views/serialization_utils.py index 2f75ab119f04eac917af3473de5b9d79336397d4..165eeac4bef8fb0987b1369148aed4766bd042c8 100644 --- a/smash/web/api_views/serialization_utils.py +++ b/smash/web/api_views/serialization_utils.py @@ -21,4 +21,25 @@ def location_to_str(location): result = "" if location is not None: result = unicode(location.name) - return result \ No newline at end of file + return result + + +def add_column(result, name, field_name, column_list, param, columns_used_in_study=None, visible_param=None, + sortable=True): + add = True + if columns_used_in_study: + add = getattr(columns_used_in_study, field_name) + if add: + if visible_param is not None: + visible = visible_param + elif column_list is None: + visible = True + else: + visible = getattr(column_list, field_name) + result.append({ + "type": field_name, + "name": name, + "filter": param, + "visible": visible, + "sortable": sortable + }) diff --git a/smash/web/api_views/subject.py b/smash/web/api_views/subject.py index 5a43f3924846ad29f1ec7337487630747822a37f..2d7cb49ae64d187ad658ff0b443057637c46ff6a 100644 --- a/smash/web/api_views/subject.py +++ b/smash/web/api_views/subject.py @@ -5,7 +5,7 @@ from django.db.models import Count, Case, When, Min, Max from django.db.models import Q from django.http import JsonResponse -from web.api_views.serialization_utils import bool_to_yes_no, flying_team_to_str,location_to_str +from web.api_views.serialization_utils import bool_to_yes_no, flying_team_to_str, location_to_str, add_column from web.models import StudySubject, Visit, Appointment, Subject, SubjectColumns, StudyColumns, Study, ContactAttempt from web.models.constants import SUBJECT_TYPE_CHOICES, GLOBAL_STUDY_ID from web.models.study_subject_list import SUBJECT_LIST_GENERIC, SUBJECT_LIST_NO_VISIT, SUBJECT_LIST_REQUIRE_CONTACT, \ @@ -32,25 +32,6 @@ def referrals(request): }) -def add_column(result, name, field_name, column_list, param, columns_used_in_study=None, visible_param=None): - add = True - if columns_used_in_study: - add = getattr(columns_used_in_study, field_name) - if add: - if visible_param is not None: - visible = visible_param - elif column_list is None: - visible = True - else: - visible = getattr(column_list, field_name) - result.append({ - "type": field_name, - "name": name, - "filter": param, - "visible": visible - }) - - @login_required def get_subject_columns(request, subject_list_type): study = Study.objects.filter(id=GLOBAL_STUDY_ID)[0] @@ -81,7 +62,7 @@ def get_subject_columns(request, subject_list_type): add_column(result, "Postponed", "postponed", study_subject_columns, "yes_no_filter", study.columns) add_column(result, "Info sent", "information_sent", study_subject_columns, "yes_no_filter", study.columns) add_column(result, "Type", "type", study_subject_columns, "type_filter", study.columns) - add_column(result, "Edit", "edit", None, None) + add_column(result, "Edit", "edit", None, None, sortable=False) for visit_number in range(1, 9): visit_key = "visit_" + str(visit_number) add_column(result, "Visit " + str(visit_number), visit_key, None, "visit_filter", diff --git a/smash/web/api_views/visit.py b/smash/web/api_views/visit.py index 06ecbaae8c92b496e6fd9d8df5bc16aec3df0277..80eef6027e5bc1adca60ee4c84c92bceb25d0751 100644 --- a/smash/web/api_views/visit.py +++ b/smash/web/api_views/visit.py @@ -4,7 +4,7 @@ from django.contrib.auth.decorators import login_required from django.db.models import Q from django.http import JsonResponse -from web.api_views.serialization_utils import bool_to_yes_no, flying_team_to_str, location_to_str +from web.api_views.serialization_utils import bool_to_yes_no, flying_team_to_str, location_to_str, add_column from web.models import AppointmentType, Appointment from web.models import SubjectColumns from web.models import Visit, Study, VisitColumns, StudyVisitList, StudyColumns @@ -18,25 +18,6 @@ from web.views.notifications import get_unfinished_visits, get_active_visits_wit logger = logging.getLogger(__name__) -def add_column(result, name, field_name, column_list, param, columns_used_in_study=None, visible_param=None): - add = True - if columns_used_in_study: - add = getattr(columns_used_in_study, field_name) - if add: - if visible_param is not None: - visible = visible_param - elif column_list is None: - visible = True - else: - visible = getattr(column_list, field_name) - result.append({ - "type": field_name, - "name": name, - "filter": param, - "visible": visible - }) - - # noinspection PyUnusedLocal @login_required def get_visit_columns(request, visit_list_type): @@ -66,12 +47,14 @@ def get_visit_columns(request, visit_list_type): add_column(result, "Post mail sent", "post_mail_sent", visit_columns, "yes_no_filter") add_column(result, "Visit number", "visit_number", visit_columns, "integer_filter") add_column(result, "Appointments in progress", "visible_appointment_types_in_progress", visit_list, - "appointment_type_filter") - add_column(result, "Done appointments", "visible_appointment_types_done", visit_list, "appointment_type_filter") + "appointment_type_filter", sortable=False) + add_column(result, "Done appointments", "visible_appointment_types_done", visit_list, "appointment_type_filter", + sortable=False) add_column(result, "Missing appointments", "visible_appointment_types_missing", visit_list, - "appointment_type_filter") - add_column(result, "All appointments", "visible_appointment_types", visit_columns, "appointment_type_filter") - add_column(result, "Edit", "edit", None, None) + "appointment_type_filter", sortable=False) + add_column(result, "All appointments", "visible_appointment_types", visit_columns, "appointment_type_filter", + sortable=False) + add_column(result, "Edit", "edit", None, None, sortable=False) return JsonResponse({"columns": result}) @@ -124,6 +107,31 @@ def get_visits_order(visits_to_be_ordered, order_column, order_direction): return result +def filter_by_appointment_in_progress(visits_to_filter, appointment_type): + result = visits_to_filter.filter(Q(appointment__appointment_types=appointment_type) & Q( + appointment__status=Appointment.APPOINTMENT_STATUS_SCHEDULED)) + return result + + +def filter_by_appointment_done(visits_to_filter, appointment_type): + result = visits_to_filter.filter(Q(appointment__appointment_types=appointment_type) & Q( + appointment__status=Appointment.APPOINTMENT_STATUS_FINISHED)) + return result + + +def filter_by_appointment_missing(visits_to_filter, appointment_type): + result = filter_by_appointment(visits_to_filter, appointment_type).exclude( + Q(appointment__appointment_types=appointment_type), Q( + appointment__status=Appointment.APPOINTMENT_STATUS_FINISHED) | Q( + appointment__status=Appointment.APPOINTMENT_STATUS_SCHEDULED)) + return result + + +def filter_by_appointment(visits_to_filter, appointment_type): + result = visits_to_filter.filter(appointment_types=appointment_type) + return result + + def get_visits_filtered(visits_to_be_filtered, filters): result = visits_to_be_filtered for row in filters: @@ -143,6 +151,14 @@ def get_visits_filtered(visits_to_be_filtered, filters): result = result.filter(post_mail_sent=(value == "true")) elif column == "visit_number": result = result.filter(visit_number=int(value)) + elif column == "visible_appointment_types_in_progress": + result = filter_by_appointment_in_progress(result, value) + elif column == "visible_appointment_types_done": + result = filter_by_appointment_done(result, value) + elif column == "visible_appointment_types_missing": + result = filter_by_appointment_missing(result, value) + elif column == "visible_appointment_types": + result = filter_by_appointment(result, value) else: message = "UNKNOWN filter: " if column is None: diff --git a/smash/web/static/js/smash.js b/smash/web/static/js/smash.js index aa9575cd678eb5099434ab93c12aa9264463c9ec..6da68884c152b60a3e56823dc4e808d4b604e76c 100644 --- a/smash/web/static/js/smash.js +++ b/smash/web/static/js/smash.js @@ -83,7 +83,7 @@ if (!String.prototype.startsWith) { }; } -function createColumn(dataType, name, filter, visible, renderFunction) { +function createColumn(dataType, name, filter, visible, sortable, renderFunction) { if (renderFunction === undefined) { renderFunction = function (data, type, row, meta) { return row[dataType]; @@ -94,7 +94,8 @@ function createColumn(dataType, name, filter, visible, renderFunction) { "name": name, "filter": filter, "visible": visible, - "render": renderFunction + "render": renderFunction, + "sortable": sortable }; } @@ -103,7 +104,7 @@ function getColumns(columns, getSubjectEditUrl) { for (var i = 0; i < columns.length; i++) { var columnRow = columns[i]; if (columnRow.type === "edit") { - result.push(createColumn("id", columnRow.name, columnRow.filter, columnRow.visible, function (data, type, row) { + result.push(createColumn("id", columnRow.name, columnRow.filter, columnRow.visible, columnRow.sortable, function (data, type, row) { var url = getSubjectEditUrl(row.id.toString()); return '<a href="' + url + '" type="button" class="btn btn-block btn-default">Edit</a>'; @@ -116,10 +117,10 @@ function getColumns(columns, getSubjectEditUrl) { }; })(); - result.push(createColumn(columnRow.type, columnRow.name, columnRow.filter, columnRow.visible, renderFunction)); + result.push(createColumn(columnRow.type, columnRow.name, columnRow.filter, columnRow.visible, columnRow.sortable, renderFunction)); } else { - result.push(createColumn(columnRow.type, columnRow.name, columnRow.filter, columnRow.visible)); + result.push(createColumn(columnRow.type, columnRow.name, columnRow.filter, columnRow.visible, columnRow.sortable)); } } return result; @@ -212,6 +213,7 @@ function createTable(params) { var subject_types_url = params.subject_types_url; var locations_url = params.locations_url; var flying_teams_url = params.flying_teams_url; + var appointment_types_url = params.appointment_types_url; var subjects_url = params.subjects_url; var columnsDefinition = params.columns; @@ -277,6 +279,19 @@ function createTable(params) { }); }); + $(tableElement).find('tfoot div[name="appointment_type_filter"]').each(function () { + var obj = $(this); + obj.html('<select style="width:80px"><option value selected="selected">---</option></select>'); + var select = $('select', obj); + $.get(appointment_types_url, function (data) { + $.each(data.appointment_types, function (index, appointment_type) { + select.append('<option value="' + appointment_type.id + '">' + appointment_type.type + '</option>'); + }); + if (worker_locations.length === 1) { + select.val(worker_locations[0].id); + } + }); + }); $(tableElement).find('tfoot div[name="type_filter"]').each(function () { var obj = $(this); @@ -296,7 +311,12 @@ function createTable(params) { for (var i = 0; i < columnsDefinition.length; i++) { var column = columnsDefinition[i]; columns.push({"data": column.type}); - columnDefs.push({"targets": i, "render": column.render, visible: column.visible}); + columnDefs.push({ + "targets": i, + "render": column.render, + visible: column.visible, + orderable: column.sortable + }); } @@ -333,7 +353,6 @@ function createTable(params) { // Get the column API object var column = table.column($(this).attr('data-column')); - console.log($(this).attr('data-column')); // Toggle the visibility column.visible(visible); }); diff --git a/smash/web/templates/visits/index.html b/smash/web/templates/visits/index.html index d3dc4a027cb7d5b1e05b79eb2b98c7870f80b54c..eb25d83536ca7336b78e770abc4c2e9aae082f63 100644 --- a/smash/web/templates/visits/index.html +++ b/smash/web/templates/visits/index.html @@ -44,6 +44,7 @@ createVisitsTable({ locations_url: "{% url 'web.api.locations' %}", flying_teams_url: "{% url 'web.api.flying_teams' %}", + appointment_types_url: "{% url 'web.api.appointment_types' %}", subjects_url: "{% url 'web.api.visits' visit_list_type %}", tableElement: document.getElementById("table"), columns: getColumns(data.columns, getVisitEditUrl),