From b4330dabd89f4184ea041c43c9b8ff45be104cc3 Mon Sep 17 00:00:00 2001 From: Carlos Vega <carlos.vega@uni.lu> Date: Wed, 31 Oct 2018 11:40:11 +0100 Subject: [PATCH] added excluded and excluded reason fields to studysubject --- smash/web/api_views/subject.py | 130 ++++++++++++------ smash/web/models/study_columns.py | 135 ++++++++++--------- smash/web/models/study_subject.py | 14 ++ smash/web/tests/models/test_study_subject.py | 15 +++ 4 files changed, 187 insertions(+), 107 deletions(-) diff --git a/smash/web/api_views/subject.py b/smash/web/api_views/subject.py index 65039448..881fb347 100644 --- a/smash/web/api_views/subject.py +++ b/smash/web/api_views/subject.py @@ -19,7 +19,8 @@ logger = logging.getLogger(__name__) # noinspection PyUnusedLocal def cities(request): - result_subjects = Subject.objects.filter(city__isnull=False).values_list('city').distinct() + result_subjects = Subject.objects.filter( + city__isnull=False).values_list('city').distinct() return JsonResponse({ "cities": [x[0] for x in result_subjects] }) @@ -27,7 +28,8 @@ def cities(request): # noinspection PyUnusedLocal def referrals(request): - result_subjects = StudySubject.objects.filter(referral__isnull=False).values_list('referral').distinct() + result_subjects = StudySubject.objects.filter( + referral__isnull=False).values_list('referral').distinct() return JsonResponse({ "referrals": [x[0] for x in result_subjects] }) @@ -35,7 +37,8 @@ def referrals(request): def get_subject_columns(request, subject_list_type): study = Study.objects.filter(id=GLOBAL_STUDY_ID)[0] - study_subject_lists = StudySubjectList.objects.filter(study=study, type=subject_list_type) + 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 @@ -46,29 +49,44 @@ def get_subject_columns(request, subject_list_type): 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, "Social Security Number", "social_security_number", subject_columns, "string_filter") + 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, "Social Security Number", + "social_security_number", 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) - 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) + add_column(result, "Contact on", "datetime_contact_reminder", + study_subject_columns, None, study.columns) + 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) add_column(result, "Health partner name", "health_partner_first_name", None, "string_filter", add_param=study.columns.health_partner, visible_param=study_subject_columns.health_partner) add_column(result, "Health partner last name", "health_partner_last_name", None, "string_filter", add_param=study.columns.health_partner, visible_param=study_subject_columns.health_partner) - add_column(result, "Location", "default_location", study_subject_columns, "location_filter", study.columns) + 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) 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, "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, "Excluded", "excluded", + 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, sortable=False) for visit_number in range(1, 9): visit_key = "visit_" + str(visit_number) @@ -104,9 +122,11 @@ def get_subjects_order(subjects_to_be_ordered, order_column, order_direction, co else: order_direction = "-" if order_column == "first_name": - result = subjects_to_be_ordered.order_by(order_direction + 'subject__first_name') + result = subjects_to_be_ordered.order_by( + order_direction + 'subject__first_name') elif order_column == "last_name": - result = subjects_to_be_ordered.order_by(order_direction + 'subject__last_name') + 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": @@ -117,40 +137,53 @@ def get_subjects_order(subjects_to_be_ordered, order_column, order_direction, co else: pattern = column_filters[u'screening_number'] result = subjects_to_be_ordered.all() - result = sorted(result, key=lambda t: t.sort_matched_screening_first(pattern, reverse=order_direction == '-'), reverse = order_direction == '-' ) + result = sorted(result, key=lambda t: t.sort_matched_screening_first( + pattern, reverse=order_direction == '-'), reverse=order_direction == '-') elif order_column == "default_location": - result = subjects_to_be_ordered.order_by(order_direction + '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') + 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') + 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') + result = subjects_to_be_ordered.order_by( + order_direction + 'information_sent') elif order_column == "health_partner_first_name": - result = subjects_to_be_ordered.order_by(order_direction + 'health_partner__first_name') + result = subjects_to_be_ordered.order_by( + order_direction + 'health_partner__first_name') elif order_column == "health_partner_last_name": - result = subjects_to_be_ordered.order_by(order_direction + 'health_partner__last_name') + result = subjects_to_be_ordered.order_by( + order_direction + 'health_partner__last_name') elif order_column == "social_security_number": - result = subjects_to_be_ordered.order_by(order_direction + 'subject__social_security_number') + result = subjects_to_be_ordered.order_by( + order_direction + 'subject__social_security_number') elif order_column == "postponed": result = subjects_to_be_ordered.order_by(order_direction + 'postponed') + elif order_column == "excluded": + result = subjects_to_be_ordered.order_by(order_direction + 'excluded') 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": - result = subjects_to_be_ordered.order_by(order_direction + 'subject__date_born') + 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') + result = subjects_to_be_ordered.order_by( + order_direction + 'datetime_contact_reminder') 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') 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) + result = order_by_visit(subjects_to_be_ordered, + order_direction, visit_number) else: logger.warn("Unknown sort column: " + str(order_column)) return result @@ -166,8 +199,10 @@ def filter_by_visit(result, visit_number, visit_type): 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' + 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 @@ -179,11 +214,13 @@ def filter_by_visit(result, visit_number, visit_type): # number of finished appointments result = result.annotate(**{finished_appointments_filter: Count(Case(When( - Q(visit__appointment__status=Appointment.APPOINTMENT_STATUS_FINISHED) & Q(visit__visit_number=visit_number), + Q(visit__appointment__status=Appointment.APPOINTMENT_STATUS_FINISHED) & Q( + visit__visit_number=visit_number), then=1)))}) # number of scheduled appointments result = result.annotate(**{scheduled_appointments_filter: Count(Case(When( - Q(visit__appointment__status=Appointment.APPOINTMENT_STATUS_SCHEDULED) & Q(visit__visit_number=visit_number), + Q(visit__appointment__status=Appointment.APPOINTMENT_STATUS_SCHEDULED) & Q( + visit__visit_number=visit_number), then=1)))}) # when visit starts @@ -216,7 +253,8 @@ def filter_by_visit(result, visit_number, visit_type): 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()}) + result = result.filter( + **{datetime_begin_filter + "__gt": get_today_midnight_date()}) return result @@ -242,6 +280,8 @@ def get_subjects_filtered(subjects_to_be_filtered, filters): result = result.filter(resigned=(value == "true")) elif column == "postponed": result = result.filter(postponed=(value == "true")) + elif column == "excluded": + result = result.filter(excluded=(value == "true")) elif column == "information_sent": result = result.filter(information_sent=(value == "true")) elif column == "health_partner_first_name": @@ -249,7 +289,8 @@ def get_subjects_filtered(subjects_to_be_filtered, filters): elif column == "health_partner_last_name": result = result.filter(health_partner__last_name__icontains=value) elif column == "social_security_number": - result = result.filter(subject__social_security_number__icontains=value) + result = result.filter( + subject__social_security_number__icontains=value) elif column == "default_location": result = result.filter(default_location=value) elif column == "flying_team": @@ -273,14 +314,16 @@ def get_subjects_filtered(subjects_to_be_filtered, filters): def subjects(request, type): try: - # id of the query from dataTable: https://datatables.net/manual/server-side + # 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") + order_column = request.GET.get( + "columns[" + str(order) + "][data]", "last_name") filters = get_filters_for_data_table_request(request) @@ -289,7 +332,8 @@ def subjects(request, type): count = all_subjects.count() filtered_subjects = get_subjects_filtered(all_subjects, filters) - ordered_subjects = get_subjects_order(filtered_subjects, order_column, order_dir, column_filters=dict(filters)) + ordered_subjects = get_subjects_order( + filtered_subjects, order_column, order_dir, column_filters=dict(filters)) sliced_subjects = ordered_subjects[start:(start + length)] result_subjects = sliced_subjects @@ -323,7 +367,8 @@ def types(request): def serialize_subject(study_subject): location = location_to_str(study_subject.default_location) flying_team = flying_team_to_str(study_subject.flying_team) - visits = Visit.objects.filter(subject=study_subject).order_by('visit_number') + visits = Visit.objects.filter( + subject=study_subject).order_by('visit_number') serialized_visits = [] for visit in visits: if visit.datetime_begin < get_today_midnight_date(): @@ -355,8 +400,10 @@ def serialize_subject(study_subject): "datetime_start": serialize_date(visit.datetime_begin), "datetime_end": serialize_date(visit.datetime_end), }) - contact_reminder = serialize_datetime(study_subject.datetime_contact_reminder) - contact_attempts = ContactAttempt.objects.filter(subject=study_subject).order_by("-datetime_when") + contact_reminder = serialize_datetime( + study_subject.datetime_contact_reminder) + 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 = serialize_datetime(last_contact_attempt.datetime_when) + "<br/>" + unicode( @@ -385,6 +432,7 @@ def serialize_subject(study_subject): "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), + "excluded": bool_to_yes_no(study_subject.excluded), "information_sent": bool_to_yes_no(study_subject.information_sent), "health_partner_first_name": health_partner_first_name, "health_partner_last_name": health_partner_last_name, diff --git a/smash/web/models/study_columns.py b/smash/web/models/study_columns.py index 565ad55d..aae985a0 100644 --- a/smash/web/models/study_columns.py +++ b/smash/web/models/study_columns.py @@ -5,6 +5,7 @@ from web.models.constants import BOOL_CHOICES class StudyColumns(models.Model): + class Meta: app_label = 'web' @@ -14,101 +15,103 @@ class StudyColumns(models.Model): ) datetime_contact_reminder = models.BooleanField( - default=True, - verbose_name='Please make a contact on' - ) + default=True, + verbose_name='Please make a contact on' + ) type = models.BooleanField( - default=True, - verbose_name='Type' - ) + default=True, + verbose_name='Type' + ) default_location = models.BooleanField( - default=True, - verbose_name='Default appointment location', - ) + default=True, + verbose_name='Default appointment location', + ) flying_team = models.BooleanField( - default=True, - verbose_name='Default flying team location (if applicable)', - ) + default=True, + verbose_name='Default flying team location (if applicable)', + ) screening_number = models.BooleanField( - default=True, - verbose_name='Screening number', - ) + default=True, + verbose_name='Screening number', + ) nd_number = models.BooleanField( - default=True, - verbose_name='ND number', - ) + default=True, + verbose_name='ND number', + ) mpower_id = models.BooleanField( - default=True, - verbose_name='MPower ID' - ) + default=True, + verbose_name='MPower ID' + ) comments = models.BooleanField( - default=True, - verbose_name='Comments' - ) + default=True, + verbose_name='Comments' + ) referral = models.BooleanField( - default=True, - verbose_name='Referred by' - ) + default=True, + verbose_name='Referred by' + ) diagnosis = models.BooleanField( - default=True, - verbose_name='Diagnosis' - ) + default=True, + verbose_name='Diagnosis' + ) year_of_diagnosis = models.BooleanField( - default=True, - verbose_name='Year of diagnosis (YYYY)' - ) + default=True, + verbose_name='Year of diagnosis (YYYY)' + ) information_sent = models.BooleanField( - default=True, - verbose_name='Information sent', - ) + default=True, + verbose_name='Information sent', + ) pd_in_family = models.BooleanField( - default=True, - verbose_name='PD in family', - ) + default=True, + verbose_name='PD in family', + ) resigned = models.BooleanField( - default=True, - verbose_name='Resigned', - ) + default=True, + verbose_name='Resigned', + ) resign_reason = models.BooleanField( - default=True, - verbose_name='Resign reason' - ) + default=True, + verbose_name='Resign reason' + ) + + excluded = models.BooleanField(default=False, verbose_name='Excluded') referral_letter = models.BooleanField( - default=False, - verbose_name='Referral letter' - ) + default=False, + verbose_name='Referral letter' + ) health_partner = models.BooleanField( - default=False, - verbose_name='Health partner' - ) + default=False, + verbose_name='Health partner' + ) health_partner_feedback_agreement = models.BooleanField( - default=False, - verbose_name='Agrees to give information to referral' - ) + default=False, + verbose_name='Agrees to give information to referral' + ) screening = models.BooleanField( - default=False, - verbose_name='Screening' - ) + default=False, + verbose_name='Screening' + ) previously_in_study = models.BooleanField( - default=False, - verbose_name='Previously in PDP study', - ) + default=False, + verbose_name='Previously in PDP study', + ) voucher_types = models.BooleanField( - default=False, - verbose_name='Voucher types', - ) + default=False, + verbose_name='Voucher types', + ) vouchers = models.BooleanField( - default=False, - verbose_name='Vouchers', - ) + default=False, + verbose_name='Vouchers', + ) diff --git a/smash/web/models/study_subject.py b/smash/web/models/study_subject.py index e6242553..dc7cedb0 100644 --- a/smash/web/models/study_subject.py +++ b/smash/web/models/study_subject.py @@ -32,6 +32,11 @@ class StudySubject(models.Model): self.finish_all_visits() self.finish_all_appointments() + def mark_as_excluded(self): + self.excluded = True + self.finish_all_visits() + self.finish_all_appointments() + subject = models.ForeignKey("web.Subject", verbose_name='Subject', editable=False, @@ -161,6 +166,15 @@ class StudySubject(models.Model): blank=True, verbose_name='Resign reason' ) + excluded = models.BooleanField( + verbose_name='Excluded', + default=False, + editable=True + ) + exclude_reason = models.TextField(max_length=2000, + blank=True, + verbose_name='Exclude reason' + ) def sort_matched_screening_first(self, pattern, reverse=False): parts = self.screening_number.split(';') diff --git a/smash/web/tests/models/test_study_subject.py b/smash/web/tests/models/test_study_subject.py index f91762f3..50a850a5 100644 --- a/smash/web/tests/models/test_study_subject.py +++ b/smash/web/tests/models/test_study_subject.py @@ -24,6 +24,21 @@ class SubjectModelTests(TestCase): self.assertEquals( Appointment.APPOINTMENT_STATUS_CANCELLED, appointment_status) + def test_mark_as_excluded(self): + subject = create_study_subject() + visit = create_visit(subject) + appointment = create_appointment(visit) + + subject.mark_as_excluded() + appointment_status = Appointment.objects.filter(id=appointment.id)[ + 0].status + visit_finished = Visit.objects.filter(id=visit.id)[0].is_finished + + self.assertTrue(subject.excluded) + self.assertTrue(visit_finished) + self.assertEquals( + Appointment.APPOINTMENT_STATUS_CANCELLED, appointment_status) + def test_check_nd_number_regex(self): # delete everything StudySubject.objects.all().delete() -- GitLab