Skip to content
Snippets Groups Projects
Commit 373bc97a authored by Piotr Gawron's avatar Piotr Gawron
Browse files

Merge branch '174-unfinished-appointments' into 'master'

Resolve "Unfinished appointments"

Closes #174

See merge request NCER-PD/scheduling-system!111
parents d3249073 ccd5f8e9
No related branches found
No related tags found
1 merge request!111Resolve "Unfinished appointments"
Pipeline #
Showing
with 649 additions and 171 deletions
......@@ -20,7 +20,9 @@ from web.api_views import worker, location, subject, appointment_type, appointme
urlpatterns = [
# appointments
url(r'^appointments/(?P<type>[A-z]+)$', appointment.appointments, name='web.api.appointments'),
url(r'^appointments/(?P<appointment_type>[A-z]+)$', appointment.appointments, name='web.api.appointments'),
url(r'^appointments:columns/(?P<appointment_list_type>[A-z]+)$', appointment.get_appointment_columns,
name='web.api.appointments.columns'),
# appointment types
url(r'^appointment_types$', appointment_type.appointment_types, name='web.api.appointment_types'),
......
......@@ -6,31 +6,55 @@ from django.http import JsonResponse
from django.urls import reverse
from django.utils import timezone
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
from web.api_views.serialization_utils import serialize_datetime, location_to_str, flying_team_to_str, add_column, \
bool_to_yes_no, get_filters_for_data_table_request
from web.models import Appointment, Study, SubjectColumns, AppointmentColumns, AppointmentList
from web.models.appointment_list import APPOINTMENT_LIST_GENERIC, APPOINTMENT_LIST_UNFINISHED, \
APPOINTMENT_LIST_APPROACHING
from web.models.constants import GLOBAL_STUDY_ID
from web.views.notifications import get_filter_locations, get_today_midnight_date, get_unfinished_appointments
logger = logging.getLogger(__name__)
@login_required
def get_appointments(request, type, min_date, max_date):
if type == APPOINTMENT_LIST_GENERIC:
result = Appointment.objects.filter(location__in=get_filter_locations(request.user),
)
elif type == APPOINTMENT_LIST_UNFINISHED:
def get_appointment_columns(request, appointment_list_type):
study = Study.objects.filter(id=GLOBAL_STUDY_ID)[0]
appointment_lists = AppointmentList.objects.filter(study=study, type=appointment_list_type)
if len(appointment_lists) > 0:
appointment_list = appointment_lists[0]
subject_columns = appointment_list.visible_subject_columns
appointment_columns = appointment_list.visible_appointment_columns
else:
subject_columns = SubjectColumns()
appointment_columns = AppointmentColumns()
result = []
add_column(result, "First name", "first_name", subject_columns, "string_filter")
add_column(result, "Last name", "last_name", subject_columns, "string_filter")
add_column(result, "Info sent", "post_mail_sent", appointment_columns, "yes_no_filter")
add_column(result, "Date", "datetime_when", appointment_columns, None)
add_column(result, "Appointment types", "appointment_types", appointment_columns, "appointment_type_filter",
sortable=False)
add_column(result, "Edit", "edit", None, None, sortable=False)
return JsonResponse({"columns": result})
@login_required
def get_appointments(request, appointment_type, min_date, max_date):
if appointment_type == APPOINTMENT_LIST_GENERIC:
result = Appointment.objects.filter(location__in=get_filter_locations(request.user))
elif appointment_type == APPOINTMENT_LIST_UNFINISHED:
result = get_unfinished_appointments(request.user)
elif type == APPOINTMENT_LIST_APPROACHING:
elif appointment_type == APPOINTMENT_LIST_APPROACHING:
result = 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)
raise TypeError("Unknown query type: " + appointment_type)
if min_date is not None:
min_date = datetime.strptime(min_date, "%Y-%m-%d").replace(tzinfo=timezone.now().tzinfo)
......@@ -42,74 +66,126 @@ def get_appointments(request, type, min_date, max_date):
return result.order_by("datetime_when")
def get_appointments_order(appointments_to_be_ordered, order_column, order_direction):
result = appointments_to_be_ordered
if order_direction == "asc":
order_direction = ""
else:
order_direction = "-"
if order_column == "first_name":
result = appointments_to_be_ordered.order_by(order_direction + 'visit__subject__subject__first_name')
elif order_column == "last_name":
result = appointments_to_be_ordered.order_by(order_direction + 'visit__subject__subject__last_name')
elif order_column == "location":
result = appointments_to_be_ordered.order_by(order_direction + 'location')
elif order_column == "flying_team":
result = appointments_to_be_ordered.order_by(order_direction + 'flying_team')
elif order_column == "post_mail_sent":
result = appointments_to_be_ordered.order_by(order_direction + 'post_mail_sent')
elif order_column == "datetime_when":
result = appointments_to_be_ordered.order_by(order_direction + 'datetime_when')
else:
logger.warn("Unknown sort column: " + str(order_column))
return result
def get_appointments_filtered(appointments_to_be_filtered, filters):
result = appointments_to_be_filtered
for row in filters:
column = row[0]
value = row[1]
if column == "first_name":
result = result.filter(visit__subject__subject__first_name__icontains=value)
elif column == "last_name":
result = result.filter(visit__subject__subject__last_name__icontains=value)
elif column == "location":
result = result.filter(location=value)
elif column == "flying_team":
result = result.filter(flying_team=value)
elif column == "appointment_types":
result = result.filter(appointment_types=value)
else:
message = "UNKNOWN filter: "
if column is None:
message += "[None]"
else:
message += str(column)
logger.warn(message)
return result
@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"))
def appointments(request, appointment_type):
# 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"))
start = int(request.GET.get("start", "0"))
length = int(request.GET.get("length", "10"))
min_date = request.GET.get("start_date", None)
max_date = request.GET.get("end_date", None)
order = int(request.GET.get("order[0][column]", "0"))
order_dir = request.GET.get("order[0][dir]", "asc")
order_column = request.GET.get("columns[" + str(order) + "][data]", "last_name")
if min_date is not None:
length = 1000000000
min_date = request.GET.get("start_date", None)
max_date = request.GET.get("end_date", None)
all_appointments = get_appointments(request, type, min_date, max_date)
filters = get_filters_for_data_table_request(request)
count = all_appointments.count()
if min_date is not None:
length = 1000000000
all_appointments = get_appointments(request, appointment_type, min_date, max_date)
count = all_appointments.count()
sliced_subjects = all_appointments[start:(start + length)]
sorted_appointments = get_appointments_order(all_appointments, order_column, order_dir)
filtered_appointments = get_appointments_filtered(sorted_appointments, filters)
sliced_appointments = filtered_appointments[start:(start + length)]
result_appointments = sliced_subjects
result_appointments = sliced_appointments
count_filtered = all_appointments.count()
count_filtered = all_appointments.count()
data = []
for appointment in result_appointments:
data.append(serialize_appointment(appointment))
data = []
for appointment in result_appointments:
data.append(serialize_appointment(appointment))
return JsonResponse({
"draw": draw,
"recordsTotal": count,
"recordsFiltered": count_filtered,
"data": data,
})
except:
logger.exception("Problem with getting appointments")
return e500_error(request)
return JsonResponse({
"draw": draw,
"recordsTotal": count,
"recordsFiltered": count_filtered,
"data": data,
})
def serialize_appointment(appointment):
subject_string = ""
nd_number = screening_number = phone_numbers = appointment_types = None
first_name = ""
last_name = ""
nd_number = screening_number = phone_numbers = appointment_type_names = None
if appointment.visit is not None:
title = "Visit " + str(appointment.visit.visit_number)
study_subject = appointment.visit.subject
subject_string = study_subject.subject.last_name + " " + study_subject.subject.first_name
first_name = study_subject.subject.first_name
last_name = study_subject.subject.last_name
nd_number = study_subject.nd_number
screening_number = study_subject.screening_number
phone_numbers = ", ".join(filter(None,
[study_subject.subject.phone_number, study_subject.subject.phone_number_2,
study_subject.subject.phone_number_3]))
appointment_types = ", ".join([unicode(type) for type in appointment.appointment_types.all()])
appointment_type_names = ", ".join(
[unicode(appointment_type_codes) for appointment_type_codes in appointment.appointment_types.all()])
else:
title = appointment.comment
type = ", ".join([type.code for type in appointment.appointment_types.all()])
time = ""
if appointment.datetime_when is not None:
time = appointment.datetime_when.strftime('%Y-%m-%d %H:%M')
until = ""
if appointment.datetime_when is not None:
until = appointment.datetime_until().strftime('%Y-%m-%d %H:%M')
appointment_type_codes = ", ".join(
[appointment_type_codes.code for appointment_type_codes in appointment.appointment_types.all()])
until = serialize_datetime(appointment.datetime_until())
if appointment.flying_team is None:
location = unicode(appointment.location)
else:
location = unicode(appointment.location) + " (" + unicode(appointment.flying_team) + ")"
location = location_to_str(appointment.location)
flying_team = flying_team_to_str(appointment.flying_team)
result = {
"subject": subject_string,
......@@ -117,14 +193,19 @@ def serialize_appointment(appointment):
"nd_number": nd_number,
"screening_number": screening_number,
"phone_number": phone_numbers,
"appointment_types": appointment_types,
"type": type,
"datetime_when": time,
"appointment_type_names": appointment_type_names,
"datetime_until": until,
"comment": appointment.comment,
"color": appointment.color(),
"id": appointment.id,
"first_name": first_name,
"last_name": last_name,
"location": location,
"flying_team": flying_team,
"post_mail_sent": bool_to_yes_no(appointment.post_mail_sent),
"datetime_when": serialize_datetime(appointment.datetime_when),
"appointment_types": appointment_type_codes,
"url": reverse('web.views.appointment_edit', kwargs={'id': str(appointment.id)})
}
return result
......@@ -59,3 +59,14 @@ def add_column(result, name, field_name, column_list, param, columns_used_in_stu
"visible": visible,
"sortable": sortable
})
def get_filters_for_data_table_request(request):
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
return filters
......@@ -6,7 +6,7 @@ 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, add_column, \
serialize_date, serialize_datetime
serialize_date, serialize_datetime, get_filters_for_data_table_request
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, \
......@@ -260,13 +260,7 @@ def subjects(request, type):
order_dir = request.GET.get("order[0][dir]", "asc")
order_column = request.GET.get("columns[" + str(order) + "][data]", "last_name")
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
filters = get_filters_for_data_table_request(request)
all_subjects = get_subjects(request, type)
......
......@@ -5,7 +5,7 @@ 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, add_column, \
serialize_date
serialize_date, get_filters_for_data_table_request
from web.models import AppointmentType, Appointment
from web.models import SubjectColumns
from web.models import Visit, Study, VisitColumns, StudyVisitList, StudyColumns
......@@ -182,13 +182,7 @@ def visits(request, visit_list_type):
order_dir = request.GET.get("order[0][dir]", "asc")
order_column = request.GET.get("columns[" + str(order) + "][data]", "last_name")
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
filters = get_filters_for_data_table_request(request)
all_visits = get_visits(request, visit_list_type)
......
# -*- coding: utf-8 -*-
# Generated by Django 1.10.7 on 2017-12-07 13:07
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('web', '0087_approaching_visit_wihout_appointment_list'),
]
operations = [
migrations.CreateModel(
name='AppointmentColumns',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('flying_team', models.BooleanField(default=False, verbose_name=b'Flying team')),
('worker_assigned', models.BooleanField(default=False, verbose_name=b'Worker conducting the assessment')),
('appointment_types', models.BooleanField(default=True, verbose_name=b'Appointment types')),
('room', models.BooleanField(default=False, verbose_name=b'Room')),
('location', models.BooleanField(default=False, verbose_name=b'Location')),
('comment', models.BooleanField(default=False, verbose_name=b'Comment')),
('datetime_when', models.BooleanField(default=True, verbose_name=b'Comment')),
('length', models.BooleanField(default=False, verbose_name=b'Appointment length')),
('status', models.BooleanField(default=False, verbose_name=b'Status')),
('post_mail_sent', models.BooleanField(default=False, verbose_name=b'Post mail sent')),
],
),
migrations.CreateModel(
name='AppointmentList',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('type', models.CharField(choices=[(b'GENERIC', b'Generic'), (b'UNFINISHED', b'Unfinished'), (b'APPROACHING', b'Approaching')], max_length=50, verbose_name=b'Type of list')),
('study', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='web.Study')),
('visible_appointment_columns', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='web.AppointmentColumns')),
('visible_study_subject_columns', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='web.StudyColumns')),
('visible_subject_columns', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='web.SubjectColumns')),
('visible_visit_columns', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='web.VisitColumns')),
],
),
]
# -*- coding: utf-8 -*-
# Generated by Django 1.10.7 on 2017-12-05 16:50
from __future__ import unicode_literals
from django.db import migrations
# noinspection PyUnusedLocal
# noinspection PyPep8Naming
def create_default_columns_for_APPOINTMENT_LIST_UNFINISHED(apps, schema_editor):
# We can't import the Study model directly as it may be a newer
# version than this migration expects. We use the historical version.
SubjectColumns = apps.get_model("web", "SubjectColumns")
subject_columns = SubjectColumns.objects.create()
subject_columns.sex = False
subject_columns.first_name = True
subject_columns.last_name = True
subject_columns.languages = False
subject_columns.default_written_communication_language = False
subject_columns.phone_number = False
subject_columns.phone_number_2 = False
subject_columns.phone_number_3 = False
subject_columns.email = False
subject_columns.date_born = False
subject_columns.address = False
subject_columns.postal_code = False
subject_columns.city = False
subject_columns.country = False
subject_columns.dead = False
subject_columns.save()
StudyColumns = apps.get_model("web", "StudyColumns")
study_columns = StudyColumns.objects.create()
study_columns.postponed = False
study_columns.datetime_contact_reminder = False
study_columns.type = False
study_columns.default_location = True
study_columns.flying_team = True
study_columns.screening_number = False
study_columns.nd_number = False
study_columns.mpower_id = False
study_columns.comments = False
study_columns.referral = False
study_columns.diagnosis = False
study_columns.year_of_diagnosis = False
study_columns.information_sent = False
study_columns.pd_in_family = False
study_columns.resigned = False
study_columns.resign_reason = False
study_columns.save()
VisitColumns = apps.get_model("web", "VisitColumns")
visit_columns = VisitColumns.objects.create()
visit_columns.datetime_begin = True
visit_columns.datetime_end = False
visit_columns.is_finished = False
visit_columns.post_mail_sent = False
visit_columns.visit_number = True
visit_columns.visible_appointment_types = False
visit_columns.save()
AppointmentColumns = apps.get_model("web", "AppointmentColumns")
appointment_columns = AppointmentColumns.objects.create()
appointment_columns.flying_team = False
appointment_columns.worker_assigned = False
appointment_columns.appointment_types = True
appointment_columns.room = False
appointment_columns.location = False
appointment_columns.comment = False
appointment_columns.datetime_when = True
appointment_columns.length = False
appointment_columns.status = False
appointment_columns.post_mail_sent = False
appointment_columns.save()
class Migration(migrations.Migration):
dependencies = [
('web', '0088_appointmentcolumns_appointmentlist'),
]
operations = [
migrations.RunPython(create_default_columns_for_APPOINTMENT_LIST_UNFINISHED),
migrations.RunSQL('INSERT INTO web_appointmentlist (' +
'study_id, ' +
'visible_visit_columns_id, ' +
'visible_subject_columns_id, ' +
'visible_study_subject_columns_id, ' +
'visible_appointment_columns_id, ' +
'type) ' +
"SELECT " +
"1, " +
"max(web_visitcolumns.id), " +
"max(web_subjectcolumns.id), " +
"max(web_studycolumns.id), " +
"max(web_appointmentcolumns.id), " +
"'UNFINISHED' FROM web_visitcolumns, web_studycolumns, web_subjectcolumns, web_appointmentcolumns;"),
]
......@@ -8,6 +8,7 @@ from flying_team import FlyingTeam
from location import Location
from appointment_type_link import AppointmentTypeLink
from country import Country
from appointment_columns import AppointmentColumns
from subject_columns import SubjectColumns
from study_columns import StudyColumns
from visit_columns import VisitColumns
......@@ -26,6 +27,7 @@ from subject import Subject
from study_subject import StudySubject
from study_subject_list import StudySubjectList
from study_visit_list import StudyVisitList
from appointment_list import AppointmentList
from contact_attempt import ContactAttempt
from mail_template import MailTemplate
from missing_subject import MissingSubject
......@@ -33,5 +35,6 @@ from inconsistent_subject import InconsistentSubject, InconsistentField
__all__ = [Study, FlyingTeam, Appointment, AppointmentType, Availability, Holiday, Item, Language, Location, Room,
Subject, StudySubject, StudySubjectList, SubjectColumns, StudyNotificationParameters,
Visit, Worker, ContactAttempt, ConfigurationItem, MailTemplate, AppointmentTypeLink, MissingSubject,
InconsistentSubject, InconsistentField, Country, StudyColumns, VisitColumns, StudyVisitList]
AppointmentList, AppointmentColumns, Visit, Worker, ContactAttempt, ConfigurationItem, MailTemplate,
AppointmentTypeLink,
MissingSubject, InconsistentSubject, InconsistentField, Country, StudyColumns, VisitColumns, StudyVisitList]
# coding=utf-8
from django.db import models
class AppointmentColumns(models.Model):
class Meta:
app_label = 'web'
flying_team = models.BooleanField(default=False,
verbose_name='Flying team',
)
worker_assigned = models.BooleanField(default=False,
verbose_name='Worker conducting the assessment',
)
appointment_types = models.BooleanField(default=True,
verbose_name='Appointment types',
)
room = models.BooleanField(default=False,
verbose_name='Room',
)
location = models.BooleanField(default=False,
verbose_name='Location',
)
comment = models.BooleanField(default=False,
verbose_name='Comment',
)
datetime_when = models.BooleanField(default=True,
verbose_name='Comment',
)
length = models.BooleanField(default=False,
verbose_name='Appointment length',
)
status = models.BooleanField(default=False,
verbose_name='Status',
)
post_mail_sent = models.BooleanField(default=False,
verbose_name='Post mail sent',
)
# coding=utf-8
from django.db import models
from web.models import Study, SubjectColumns, VisitColumns, AppointmentColumns, StudyColumns
APPOINTMENT_LIST_GENERIC = "GENERIC"
APPOINTMENT_LIST_UNFINISHED = "UNFINISHED"
APPOINTMENT_LIST_APPROACHING = "APPROACHING"
APPOINTMENT_LIST_CHOICES = {
APPOINTMENT_LIST_GENERIC: 'Generic',
APPOINTMENT_LIST_UNFINISHED: 'Unfinished',
APPOINTMENT_LIST_APPROACHING: 'Approaching',
}
class AppointmentList(models.Model):
class Meta:
app_label = 'web'
study = models.ForeignKey(
Study,
on_delete=models.CASCADE,
null=False,
)
visible_visit_columns = models.ForeignKey(
VisitColumns,
on_delete=models.CASCADE,
null=False,
)
visible_subject_columns = models.ForeignKey(
SubjectColumns,
on_delete=models.CASCADE,
null=False,
)
visible_study_subject_columns = models.ForeignKey(
StudyColumns,
on_delete=models.CASCADE,
null=False,
)
visible_appointment_columns = models.ForeignKey(
AppointmentColumns,
on_delete=models.CASCADE,
null=False,
)
type = models.CharField(max_length=50,
choices=APPOINTMENT_LIST_CHOICES.items(),
verbose_name='Type of list',
)
......@@ -208,7 +208,6 @@ function get_calendar_events_function(source, allow_url_redirection, day_headers
}
return function (start, end, timezone, callback) {
if (day_headers !== undefined) {
console.log("XXXX");
$.ajax({
data: {
start_date: start.format(),
......@@ -245,7 +244,7 @@ function get_calendar_events_function(source, allow_url_redirection, day_headers
const entry = doc.data[i];
var title = entry.subject;
if (title !== "") {
title += " (" + entry.nd_number + "); type: " + entry.type;
title += " (" + entry.nd_number + "); type: " + entry.appointment_types;
} else {
title = entry.title
}
......@@ -257,9 +256,11 @@ function get_calendar_events_function(source, allow_url_redirection, day_headers
color: entry.color,
nd_number: entry.nd_number,
location: entry.location,
flying_team: entry.flying_team,
screening_number: entry.screening_number,
phone_number: entry.phone_number,
appointment_types: entry.appointment_types
appointment_types: entry.appointment_types,
appointment_type_names: entry.appointment_type_names
};
if (allow_url_redirection) {
event["url"] = entry.url;
......@@ -271,3 +272,7 @@ function get_calendar_events_function(source, allow_url_redirection, day_headers
});
}
}
function createAppointmentsTable(params) {
return createTable(params);
}
......@@ -133,11 +133,15 @@
if (event.nd_number) {
content += '<li>ND number: ' + event.nd_number + '</li>'
}
if (event.appointment_types) {
content += '<li>Appointment types: ' + event.appointment_types + '</li>'
if (event.appointment_type_names) {
content += '<li>Appointment types: ' + event.appointment_type_names + '</li>'
}
if (event.location) {
content += '<li>Location: ' + event.location + '</li>'
var location = event.location;
if (event.flying_team) {
location += " (" + event.flying_team + ")";
}
content += '<li>Location: ' + location + '</li>'
}
content += "</ul>";
$(element).popover({
......
......@@ -19,25 +19,12 @@
{% endblock breadcrumb %}
{% block maincontent %}
<div class="row">
<div class="col-md-16">
<table id="table" class="table table-bordered table-striped">
<thead>
<tr>
<th>Subject</th>
<th>Visit</th>
<th>Type</th>
<th>Date</th>
<th>Details</th>
<th>Edit</th>
</tr>
</thead>
<tbody>
</tbody>
<tfoot style="display: table-header-group;"/>
</table>
</div>
<div class="box-body">
<table id="table" class="table table-bordered table-striped table-responsive">
</table>
</div>
<h3>Visible columns</h3>
<div id="visible-column-checkboxes" style="display:table; width:100%">
</div>
{% endblock maincontent %}
......@@ -46,53 +33,32 @@
<script src="{% static 'AdminLTE/plugins/datatables/jquery.dataTables.min.js' %}"></script>
<script src="{% static 'AdminLTE/plugins/datatables/dataTables.bootstrap.min.js' %}"></script>
<script src="{% static 'js/appointment.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>'
},
{# {#}
{# render: function (data, type, row) {#}
{# var date = new Date(data);#}
{# var mm = date.getMonth() + 1; // getMonth() is zero-based#}
{# var dd = date.getDate();#}
{# var hour = date.getHours();#}
{# var minute = date.getMinutes();#}
{##}
{# return [date.getFullYear(),#}
{# (mm > 9 ? '' : '0') + mm,#}
{# (dd > 9 ? '' : '0') + dd#}
{# ].join('-') + " " + hour + ":" + minute;#}
{# },#}
{# targets: 3#}
{# }#}
]
});
function getSubjectEditUrl(id) {
return "{% url 'web.views.appointment_edit' 1234567 %}".replace(/1234567/, id);
}
$('#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;
});
var worker_locations = [];
{% for location in worker.locations.all %}
worker_locations.push({id: location.id, name: location.name});
{% endfor %}
$('#table_filter').css("display", "none");
$.get("{% url 'web.api.appointments.columns' list_type %}", function (data) {
createAppointmentsTable({
worker_locations: worker_locations,
appointment_types_url: "{% url 'web.api.appointment_types' %}",
subject_types_url: "{% url 'web.api.subject_types' %}",
locations_url: "{% url 'web.api.locations' %}",
subjects_url: "{% url 'web.api.appointments' list_type %}",
flying_teams_url: "{% url 'web.api.flying_teams' %}",
tableElement: document.getElementById("table"),
columns: getColumns(data.columns, getSubjectEditUrl),
checkboxesElement: document.getElementById("visible-column-checkboxes")
})
});
</script>
{% endblock scripts %}
# coding=utf-8
import datetime
import json
from django.contrib.auth.models import User
from django.test import Client
from django.test import TestCase
from django.urls import reverse
from web.models import AppointmentTypeLink
from web.tests.functions import create_study_subject, create_worker, create_visit, create_appointment, \
create_appointment_type, create_get_suffix, create_flying_team
from web.api_views.appointment import get_appointments_filtered, get_appointments_order
from web.models import AppointmentTypeLink, AppointmentList, Appointment
from web.tests import LoggedInWithWorkerTestCase
from web.tests.functions import create_study_subject, create_visit, create_appointment, \
create_appointment_type, create_get_suffix, create_flying_team, create_location
from web.views.appointment import APPOINTMENT_LIST_GENERIC, APPOINTMENT_LIST_APPROACHING, APPOINTMENT_LIST_UNFINISHED
from web.views.notifications import get_today_midnight_date
class TestAppointmentApi(TestCase):
class TestAppointmentApi(LoggedInWithWorkerTestCase):
def setUp(self):
super(TestAppointmentApi, self).setUp()
self.study_subject = create_study_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):
place = "Some new flying team location"
......@@ -45,7 +35,7 @@ class TestAppointmentApi(TestCase):
appointment2.save()
AppointmentTypeLink.objects.create(appointment=appointment2, appointment_type=appointment_type)
url = reverse('web.api.appointments', kwargs={'type': APPOINTMENT_LIST_GENERIC})
url = reverse('web.api.appointments', kwargs={'appointment_type': APPOINTMENT_LIST_GENERIC})
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
......@@ -59,7 +49,7 @@ class TestAppointmentApi(TestCase):
appointment.flying_team = create_flying_team(place=place)
appointment.save()
url = reverse('web.api.appointments', kwargs={'type': APPOINTMENT_LIST_GENERIC})
url = reverse('web.api.appointments', kwargs={'appointment_type': APPOINTMENT_LIST_GENERIC})
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
......@@ -72,7 +62,7 @@ class TestAppointmentApi(TestCase):
visit = create_visit(self.study_subject)
create_appointment(visit, get_today_midnight_date() + datetime.timedelta(days=2))
url = reverse('web.api.appointments', kwargs={'type': APPOINTMENT_LIST_APPROACHING})
url = reverse('web.api.appointments', kwargs={'appointment_type': APPOINTMENT_LIST_APPROACHING})
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
......@@ -85,7 +75,7 @@ class TestAppointmentApi(TestCase):
visit = create_visit(self.study_subject)
create_appointment(visit, get_today_midnight_date() + datetime.timedelta(days=-12))
url = reverse('web.api.appointments', kwargs={'type': APPOINTMENT_LIST_UNFINISHED})
url = reverse('web.api.appointments', kwargs={'appointment_type': APPOINTMENT_LIST_UNFINISHED})
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
......@@ -106,7 +96,7 @@ class TestAppointmentApi(TestCase):
"end_date": (get_today_midnight_date() + datetime.timedelta(days=3)).strftime("%Y-%m-%d"),
}
url = ("%s" + create_get_suffix(params)) % reverse('web.api.appointments',
kwargs={'type': APPOINTMENT_LIST_GENERIC})
kwargs={'appointment_type': APPOINTMENT_LIST_GENERIC})
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
......@@ -114,8 +104,187 @@ class TestAppointmentApi(TestCase):
params["start_date"] = (get_today_midnight_date() + datetime.timedelta(days=-2)).strftime("%Y-%m-%d")
url = ("%s" + create_get_suffix(params)) % reverse('web.api.appointments',
kwargs={'type': APPOINTMENT_LIST_GENERIC})
kwargs={'appointment_type': APPOINTMENT_LIST_GENERIC})
response = self.client.get(url)
self.assertEqual(response.status_code, 200)
self.assertTrue(name in response.content)
def test_get_columns(self):
response = self.client.get(
reverse('web.api.appointments.columns', kwargs={'appointment_list_type': APPOINTMENT_LIST_GENERIC}))
self.assertEqual(response.status_code, 200)
columns = json.loads(response.content)['columns']
visible_columns = 0
for column in columns:
if column["visible"]:
visible_columns += 1
self.assertTrue(visible_columns > 0)
def test_get_columns_for_require_contact(self):
response = self.client.get(
reverse('web.api.appointments.columns', kwargs={'appointment_list_type': APPOINTMENT_LIST_UNFINISHED}))
self.assertEqual(response.status_code, 200)
columns = json.loads(response.content)['columns']
visible_columns = 0
for column in columns:
if column["visible"]:
visible_columns += 1
self.assertTrue(visible_columns > 0)
def test_get_columns_when_no_list_is_available(self):
AppointmentList.objects.all().delete()
response = self.client.get(
reverse('web.api.appointments.columns', kwargs={'appointment_list_type': APPOINTMENT_LIST_GENERIC}))
self.assertEqual(response.status_code, 200)
columns = json.loads(response.content)['columns']
self.assertTrue(len(columns) > 0)
def check_appointment_filtered(self, filters, result):
appointments = get_appointments_filtered(Appointment.objects.all(), filters)
self.assertEqual(len(result), appointments.count())
for index in range(len(result)):
self.assertEqual(result[index], appointments[index])
def check_appointment_ordered(self, order, result):
appointments = get_appointments_order(Appointment.objects.all(), order, "asc")
self.assertEqual(len(result), appointments.count())
for index in range(len(result)):
self.assertEqual(result[index], appointments[index])
appointments = get_appointments_order(Appointment.objects.all(), order, "desc")
length = len(result)
self.assertEqual(length, appointments.count())
for index in range(length):
self.assertEqual(result[length - index - 1], appointments[index])
def test_appointment_sort_first_name(self):
subject = self.study_subject
subject.subject.first_name = "PPP"
subject.subject.save()
subject2 = create_study_subject(2)
subject2.subject.first_name = "QQQ"
subject2.subject.save()
appointment = create_appointment(create_visit(subject))
appointment2 = create_appointment(create_visit(subject2))
self.check_appointment_ordered("first_name", [appointment, appointment2])
def test_appointment_sort_last_name(self):
subject = self.study_subject
subject.subject.last_name = "PPP"
subject.subject.save()
subject2 = create_study_subject(2)
subject2.subject.last_name = "QQQ"
subject2.subject.save()
appointment = create_appointment(create_visit(subject))
appointment2 = create_appointment(create_visit(subject2))
self.check_appointment_ordered("last_name", [appointment, appointment2])
def test_appointment_sort_default_location(self):
subject = self.study_subject
subject2 = create_study_subject(2)
appointment = create_appointment(create_visit(subject))
appointment.location = create_location(name="x")
appointment.save()
appointment2 = create_appointment(create_visit(subject2))
appointment2.location = create_location(name="y")
appointment2.save()
self.check_appointment_ordered("location", [appointment, appointment2])
def test_appointment_sort_flying_team(self):
subject = self.study_subject
subject2 = create_study_subject(2)
appointment = create_appointment(create_visit(subject))
appointment.flying_team = create_flying_team(place="x")
appointment.save()
appointment2 = create_appointment(create_visit(subject2))
appointment2.flying_team = create_flying_team(place="y")
appointment2.save()
self.check_appointment_ordered("flying_team", [appointment, appointment2])
def test_appointment_sort_post_mail_sent(self):
appointment = create_appointment(create_visit(self.study_subject))
self.check_appointment_ordered("post_mail_sent", [appointment])
def test_appointment_sort_datetime_when(self):
subject = self.study_subject
subject2 = create_study_subject(2)
appointment = create_appointment(create_visit(subject))
appointment.datetime_when = "2017-10-10"
appointment.save()
appointment2 = create_appointment(create_visit(subject2))
appointment2.datetime_when = "2017-10-12"
appointment2.save()
self.check_appointment_ordered("datetime_when", [appointment, appointment2])
def test_appointment_sort_unknown(self):
appointment = create_appointment(create_visit(self.study_subject))
self.check_appointment_ordered("some_unknown", [appointment])
self.check_appointment_ordered("", [appointment])
self.check_appointment_ordered(None, [appointment])
def test_subjects_filter_first_name(self):
subject = self.study_subject.subject
subject.first_name = "QQ"
subject.save()
appointment = create_appointment(create_visit(self.study_subject))
self.check_appointment_filtered([["first_name", "QQ"]], [appointment])
self.check_appointment_filtered([["first_name", "PP"]], [])
def test_subjects_filter_last_name(self):
subject = self.study_subject.subject
subject.last_name = "QQ"
subject.save()
appointment = create_appointment(create_visit(self.study_subject))
self.check_appointment_filtered([["last_name", "QQ"]], [appointment])
self.check_appointment_filtered([["last_name", "PP"]], [])
def test_subjects_filter_location(self):
appointment = create_appointment(create_visit(self.study_subject))
self.check_appointment_filtered([["location", str(appointment.location.id)]], [appointment])
self.check_appointment_filtered([["location", "-1"]], [])
def test_subjects_filter_flying_team(self):
appointment = create_appointment(create_visit(self.study_subject))
appointment.flying_team = create_flying_team()
appointment.save()
self.check_appointment_filtered([["flying_team", str(appointment.flying_team.id)]], [appointment])
self.check_appointment_filtered([["flying_team", "-1"]], [])
def test_subjects_filter_appointment_types(self):
appointment = create_appointment(create_visit(self.study_subject))
appointment_type = create_appointment_type()
AppointmentTypeLink.objects.create(appointment_type=appointment_type, appointment=appointment)
self.check_appointment_filtered([["appointment_types", str(appointment_type.id)]], [appointment])
self.check_appointment_filtered([["appointment_types", "-1"]], [])
def test_appointment_filter_unknown(self):
appointment = create_appointment(create_visit(self.study_subject))
self.check_appointment_filtered([["some_unknown", "unk"]], [appointment])
self.check_appointment_filtered([["", ""]], [appointment])
self.check_appointment_filtered([[None, None]], [appointment])
......@@ -286,6 +286,16 @@ class NotificationViewTests(LoggedInTestCase):
notification = get_unfinished_appointments_count(self.user)
self.assertEquals(original_notification.count + 1, notification.count)
def test_get_unfinished_appointments_count_for_general_appointments(self):
appointment = create_appointment()
appointment.visit = None
appointment.datetime_when = "2011-01-01"
appointment.status = Appointment.APPOINTMENT_STATUS_SCHEDULED
appointment.save()
notification = get_unfinished_appointments_count(self.user)
self.assertEquals(0, notification.count)
def test_get_unfinished_appointments_count_2(self):
original_notification = get_unfinished_appointments_count(self.user)
subject = create_study_subject()
......
......@@ -6,15 +6,13 @@ from django.contrib import messages
from django.core.exceptions import ValidationError
from django.shortcuts import get_object_or_404, redirect
from web.models.appointment_list import APPOINTMENT_LIST_APPROACHING, APPOINTMENT_LIST_GENERIC, \
APPOINTMENT_LIST_UNFINISHED
from . import wrap_response
from ..forms import AppointmentDetailForm, AppointmentAddForm, AppointmentEditForm, SubjectEditForm, \
StudySubjectEditForm
from ..models import Appointment, StudySubject, MailTemplate
APPOINTMENT_LIST_GENERIC = "GENERIC"
APPOINTMENT_LIST_UNFINISHED = "UNFINISHED"
APPOINTMENT_LIST_APPROACHING = "APPROACHING"
logger = logging.getLogger(__name__)
......
......@@ -235,7 +235,7 @@ def get_unfinished_appointments(user):
datetime_when__lt=get_today_midnight_date(),
status=Appointment.APPOINTMENT_STATUS_SCHEDULED,
location__in=get_filter_locations(user),
).order_by('datetime_when')
).exclude(visit__isnull=True).order_by('datetime_when')
def waiting_for_appointment(visit):
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment