Newer
Older
from django.contrib.auth.decorators import login_required
Piotr Gawron
committed
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
Piotr Gawron
committed
from web.models import StudySubject, Visit, Appointment, Subject, SubjectColumns, StudyColumns, Study, ContactAttempt
Piotr Gawron
committed
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, \
StudySubjectList
from web.views.notifications import get_subjects_with_no_visit, get_subjects_with_reminder, get_today_midnight_date
result_subjects = Subject.objects.filter(city__isnull=False).values_list('city').distinct()
return JsonResponse({
"cities": [x[0] for x in result_subjects]
})
result_subjects = StudySubject.objects.filter(referral__isnull=False).values_list('referral').distinct()
def add_column(result, name, field_name, column_list, param, columns_used_in_study=None, visible_param=None):
Piotr Gawron
committed
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:
Piotr Gawron
committed
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]
Piotr Gawron
committed
study_subject_lists = StudySubjectList.objects.filter(study=study, type=subject_list_type)
if len(study_subject_lists) > 0:
study_subject_list = study_subject_lists[0]
subject_columns = study_subject_list.visible_subject_columns
study_subject_columns = study_subject_list.visible_subject_study_columns
Piotr Gawron
committed
else:
Piotr Gawron
committed
study_subject_list = StudySubjectList()
Piotr Gawron
committed
subject_columns = SubjectColumns()
study_subject_columns = StudyColumns()
result = []
add_column(result, "ND", "nd_number", study_subject_columns, "string_filter", study.columns)
add_column(result, "Screening", "screening_number", study_subject_columns, "string_filter", study.columns)
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, "Date of birth", "date_born", subject_columns, None)
add_column(result, "Contact on", "datetime_contact_reminder", study_subject_columns, None, study.columns)
Piotr Gawron
committed
add_column(result, "Last contact attempt", "last_contact_attempt", study_subject_list, None)
add_column(result, "Referred by", "referral", study_subject_columns, "string_filter", study.columns)
Piotr Gawron
committed
add_column(result, "Location", "default_location", study_subject_columns, "location_filter", study.columns)
add_column(result, "Flying team location", "flying_team", study_subject_columns, "flying_team_filter",
study.columns)
Piotr Gawron
committed
add_column(result, "Deceased", "dead", subject_columns, "yes_no_filter")
add_column(result, "Resigned", "resigned", study_subject_columns, "yes_no_filter", study.columns)
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)
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",
visible_param=study_subject_list.visits)
Piotr Gawron
committed
return JsonResponse({"columns": result})
@login_required
def get_subjects(request, type):
if type == SUBJECT_LIST_GENERIC:
elif type == SUBJECT_LIST_NO_VISIT:
return get_subjects_with_no_visit(request.user)
elif type == SUBJECT_LIST_REQUIRE_CONTACT:
return get_subjects_with_reminder(request.user)
else:
raise TypeError("Unknown query type: " + type)
def order_by_visit(subjects_to_be_ordered, order_direction, visit_number):
return subjects_to_be_ordered.annotate(
sort_visit_date=Min(Case(When(visit__visit_number=visit_number, then='visit__datetime_begin')))).order_by(
order_direction + 'sort_visit_date')
def get_subjects_order(subjects_to_be_ordered, order_column, order_direction):
result = subjects_to_be_ordered
if order_direction == "asc":
order_direction = ""
else:
order_direction = "-"
if order_column == "first_name":
Piotr Gawron
committed
result = subjects_to_be_ordered.order_by(order_direction + 'subject__first_name')
elif order_column == "last_name":
Piotr Gawron
committed
result = subjects_to_be_ordered.order_by(order_direction + 'subject__last_name')
elif order_column == "nd_number":
result = subjects_to_be_ordered.order_by(order_direction + 'nd_number')
elif order_column == "referral":
result = subjects_to_be_ordered.order_by(order_direction + 'referral')
elif order_column == "screening_number":
result = subjects_to_be_ordered.order_by(order_direction + 'screening_number')
elif order_column == "default_location":
result = subjects_to_be_ordered.order_by(order_direction + 'default_location')
elif order_column == "flying_team":
result = subjects_to_be_ordered.order_by(order_direction + 'flying_team')
elif order_column == "dead":
result = subjects_to_be_ordered.order_by(order_direction + 'subject__dead')
elif order_column == "resigned":
result = subjects_to_be_ordered.order_by(order_direction + 'resigned')
elif order_column == "information_sent":
result = subjects_to_be_ordered.order_by(order_direction + 'information_sent')
elif order_column == "postponed":
result = subjects_to_be_ordered.order_by(order_direction + 'postponed')
elif order_column == "type":
result = subjects_to_be_ordered.order_by(order_direction + 'type')
elif order_column == "id":
result = subjects_to_be_ordered.order_by(order_direction + 'id')
elif order_column == "date_born":
Piotr Gawron
committed
result = subjects_to_be_ordered.order_by(order_direction + 'subject__date_born')
elif order_column == "datetime_contact_reminder":
result = subjects_to_be_ordered.order_by(order_direction + 'datetime_contact_reminder')
Piotr Gawron
committed
elif order_column == "last_contact_attempt":
# noinspection SpellCheckingInspection
result = subjects_to_be_ordered.annotate(sort_contact_attempt=Max("contactattempt__datetime_when")).order_by(
order_direction + 'sort_contact_attempt')
Piotr Gawron
committed
elif str(order_column).startswith("visit_"):
visit_number = get_visit_number_from_visit_x_string(order_column)
result = order_by_visit(subjects_to_be_ordered, order_direction, visit_number)
logger.warn("Unknown sort column: " + str(order_column))
return result
Piotr Gawron
committed
def get_visit_number_from_visit_x_string(order_column):
return int(str(order_column).split("_")[1])
Piotr Gawron
committed
def filter_by_visit(result, visit_number, visit_type):
# we need to give custom names for filtering params that contain visit_number in it
# because we might want to filter by few visits and they shouldn't collide
Piotr Gawron
committed
datetime_begin_filter = 'visit_' + str(visit_number) + '_datetime_begin'
datetime_end_filter = 'visit_' + str(visit_number) + '_datetime_end'
is_finished_filter = 'visit_' + str(visit_number) + '_is_finished'
finished_appointments_filter = 'visit_' + str(visit_number) + '_finished_appointments'
scheduled_appointments_filter = 'visit_' + str(visit_number) + '_scheduled_appointments'
# this is hack... instead of providing True/False value this field contain 1/0 value, the problem is that we need
# to provide aggregate function for the interacting parameter
# If we try to assign it with pure Case(When...) (without Count/Min/Max/Avg) we obtain duplicates
# of the results which are later on messing up with the subject list
Piotr Gawron
committed
result = result.annotate(**{
is_finished_filter: Count(Case(When(Q(visit__is_finished=True) & Q(visit__visit_number=visit_number), then=1)))
Piotr Gawron
committed
})
# number of finished appointments
Piotr Gawron
committed
result = result.annotate(**{finished_appointments_filter: Count(Case(When(
Q(visit__appointment__status=Appointment.APPOINTMENT_STATUS_FINISHED) & Q(visit__visit_number=visit_number),
then=1)))})
# number of scheduled appointments
Piotr Gawron
committed
result = result.annotate(**{scheduled_appointments_filter: Count(Case(When(
Q(visit__appointment__status=Appointment.APPOINTMENT_STATUS_SCHEDULED) & Q(visit__visit_number=visit_number),
then=1)))})
# when visit starts
Piotr Gawron
committed
result = result.annotate(
**{datetime_begin_filter: Min(Case(When(visit__visit_number=visit_number, then='visit__datetime_begin')))})
# when visit finish
Piotr Gawron
committed
result = result.annotate(
**{datetime_end_filter: Min(Case(When(visit__visit_number=visit_number, then='visit__datetime_end')))})
Piotr Gawron
committed
if visit_type == "DONE":
result = result.filter(**{datetime_begin_filter + "__lt": get_today_midnight_date()}). \
filter(**{is_finished_filter + "__gt": 0}). \
Piotr Gawron
committed
filter(**{finished_appointments_filter + "__gt": 0})
elif visit_type == "MISSED":
result = result.filter(**{datetime_begin_filter + "__lt": get_today_midnight_date()}). \
filter(**{is_finished_filter + "__gt": 0}). \
Piotr Gawron
committed
filter(**{finished_appointments_filter: 0})
elif visit_type == "EXCEED":
result = result.filter(**{datetime_begin_filter + "__lt": get_today_midnight_date()}). \
filter(**{is_finished_filter: 0}). \
Piotr Gawron
committed
filter(**{datetime_end_filter + "__lt": get_today_midnight_date()})
elif visit_type == "IN_PROGRESS":
result = result.filter(**{datetime_begin_filter + "__lt": get_today_midnight_date()}). \
filter(**{is_finished_filter: 0}). \
Piotr Gawron
committed
filter(**{datetime_end_filter + "__gt": get_today_midnight_date()}). \
filter(**{scheduled_appointments_filter + "__gt": 0})
elif visit_type == "SHOULD_BE_IN_PROGRESS":
result = result.filter(**{datetime_begin_filter + "__lt": get_today_midnight_date()}). \
filter(**{is_finished_filter: 0}). \
Piotr Gawron
committed
filter(**{datetime_end_filter + "__gt": get_today_midnight_date()}). \
filter(**{scheduled_appointments_filter: 0})
elif visit_type == "UPCOMING":
result = result.filter(**{datetime_begin_filter + "__gt": get_today_midnight_date()})
return result
def get_subjects_filtered(subjects_to_be_filtered, filters):
result = subjects_to_be_filtered
for row in filters:
column = row[0]
value = row[1]
if column == "first_name":
Piotr Gawron
committed
result = result.filter(subject__first_name__icontains=value)
elif column == "last_name":
Piotr Gawron
committed
result = result.filter(subject__last_name__icontains=value)
elif column == "nd_number":
result = result.filter(nd_number__icontains=value)
elif column == "referral":
result = result.filter(referral__icontains=value)
elif column == "screening_number":
result = result.filter(screening_number__icontains=value)
result = result.filter(subject__dead=(value == "true"))
result = result.filter(resigned=(value == "true"))
result = result.filter(postponed=(value == "true"))
elif column == "information_sent":
result = result.filter(information_sent=(value == "true"))
elif column == "default_location":
result = result.filter(default_location=value)
elif column == "flying_team":
result = result.filter(flying_team=value)
elif column == "type":
result = result.filter(type=value)
Piotr Gawron
committed
elif str(column).startswith("visit_"):
visit_number = get_visit_number_from_visit_x_string(column)
result = filter_by_visit(result, visit_number, value)
elif column == "":
pass
message = "UNKNOWN filter: "
if column is None:
message += "[None]"
else:
message += str(column)
logger.warn(message)
return result
@login_required
def subjects(request, type):
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
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"))
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
all_subjects = get_subjects(request, type)
count = all_subjects.count()
ordered_subjects = get_subjects_order(all_subjects, order_column, order_dir)
filtered_subjects = get_subjects_filtered(ordered_subjects, filters)
sliced_subjects = filtered_subjects[start:(start + length)]
count_filtered = filtered_subjects.count()
data = []
data.append(serialize_subject(subject))
return JsonResponse({
"draw": draw,
"recordsTotal": count,
"recordsFiltered": count_filtered,
"data": data,
})
except Exception as e:
logger.error(e, exc_info=True)
data = [{"id": subject_type_id, "name": subject_type_name} for subject_type_id, subject_type_name in
SUBJECT_TYPE_CHOICES.items()]
return JsonResponse({
"types": data
})
Piotr Gawron
committed
def serialize_subject(study_subject):
location = location_to_str(study_subject.location)
flying_team = flying_team_to_str(study_subject.flying_team)
Piotr Gawron
committed
visits = Visit.objects.filter(subject=study_subject).order_by('visit_number')
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
serialized_visits = []
for visit in visits:
if visit.datetime_begin < get_today_midnight_date():
if visit.is_finished:
finished_appointments_count = visit.appointment_set.filter(
status=Appointment.APPOINTMENT_STATUS_FINISHED).count()
if finished_appointments_count > 0:
status = "DONE"
else:
status = "MISSED"
elif visit.datetime_end < get_today_midnight_date():
status = "EXCEEDED"
else:
scheduled_appointments_count = visit.appointment_set.filter(
status=Appointment.APPOINTMENT_STATUS_SCHEDULED).count()
if scheduled_appointments_count > 0:
status = "IN_PROGRESS"
else:
status = "SHOULD_BE_IN_PROGRESS"
else:
status = "UPCOMING"
serialized_visits.append({
"status": status,
"datetime_start": visit.datetime_begin.strftime('%Y-%m-%d'),
"datetime_end": visit.datetime_end.strftime('%Y-%m-%d'),
})
contact_reminder = study_subject.datetime_contact_reminder
if contact_reminder is not None:
contact_reminder = contact_reminder.strftime('%Y-%m-%d %H:%M')
Piotr Gawron
committed
contact_attempts = ContactAttempt.objects.filter(subject=study_subject).order_by("-datetime_when")
if len(contact_attempts) > 0:
last_contact_attempt = contact_attempts[0]
last_contact_attempt_string = last_contact_attempt.datetime_when.strftime(
'%Y-%m-%d %H:%M') + "<br/>" + str(last_contact_attempt.worker) + "<br/> Success: " + bool_to_yes_no(
Piotr Gawron
committed
last_contact_attempt.success) + "<br/>" + last_contact_attempt.comment
else:
last_contact_attempt_string = ""
result = {
Piotr Gawron
committed
"first_name": study_subject.subject.first_name,
"last_name": study_subject.subject.last_name,
"date_born": study_subject.subject.date_born,
"datetime_contact_reminder": contact_reminder,
Piotr Gawron
committed
"last_contact_attempt": last_contact_attempt_string,
Piotr Gawron
committed
"nd_number": study_subject.nd_number,
"screening_number": study_subject.screening_number,
"referral": study_subject.referral,
"default_location": location,
"flying_team": flying_team,
"dead": bool_to_yes_no(study_subject.subject.dead),
"resigned": bool_to_yes_no(study_subject.resigned),
"postponed": bool_to_yes_no(study_subject.postponed),
"information_sent": bool_to_yes_no(study_subject.information_sent),
Piotr Gawron
committed
"type": study_subject.get_type_display(),
"id": study_subject.id,
}
return result