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

information from redcap about finished appointment is automatically imported

parent c5b9cd10
No related branches found
No related tags found
1 merge request!240Redcap sync
Pipeline #25285 failed
......@@ -10,6 +10,15 @@ def bool_to_yes_no(val):
return "NO"
def bool_to_yes_no_null(val):
if val is None:
return "N/A"
if val:
return "YES"
else:
return "NO"
def flying_team_to_str(flying_team):
result = ""
if flying_team is not None:
......
import logging
from django.urls import reverse
from django.db.models import Count, Case, When, Min, Max
from django.db.models import Q
from django.http import JsonResponse
from django.urls import reverse
from web.api_views.serialization_utils import bool_to_yes_no, flying_team_to_str, location_to_str, add_column, \
serialize_date, serialize_datetime, get_filters_for_data_table_request
serialize_date, serialize_datetime, get_filters_for_data_table_request, bool_to_yes_no_null
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, \
......@@ -73,12 +73,25 @@ def get_subject_columns(request, subject_list_type):
add_column(result, "Next of keen", "next_of_keen_name", subject_columns, "string_filter")
add_column(result, "Next of keen phone", "next_of_keen_phone", subject_columns, "string_filter")
add_column(result, "Next of keen address", "next_of_keen_address", subject_columns, "string_filter")
add_column(result, "Brain donation agreement", "brain_donation_agreement", study_subject_columns, "yes_no_filter", study.columns)
add_column(result, "Brain donation agreement", "brain_donation_agreement", 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, "Visit 1 virus", "virus_test_1", study_subject_columns, "yes_no_null_filter", study.columns)
add_column(result, "Visit 1 virus date", "virus_test_1_updated", study_subject_columns, None, study.columns)
add_column(result, "Visit 2 virus", "virus_test_2", study_subject_columns, "yes_no_null_filter", study.columns)
add_column(result, "Visit 2 virus date", "virus_test_2_updated", study_subject_columns, None, study.columns)
add_column(result, "Visit 3 virus", "virus_test_3", study_subject_columns, "yes_no_null_filter", study.columns)
add_column(result, "Visit 3 virus date", "virus_test_3_updated", study_subject_columns, None, study.columns)
add_column(result, "Visit 4 virus", "virus_test_4", study_subject_columns, "yes_no_null_filter", study.columns)
add_column(result, "Visit 4 virus date", "virus_test_4_updated", study_subject_columns, None, study.columns)
add_column(result, "Visit 5 virus", "virus_test_5", study_subject_columns, "yes_no_null_filter", study.columns)
add_column(result, "Visit 5 virus date", "virus_test_5_updated", study_subject_columns, None, 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, study.visits_to_show_in_subject_list+1):
for visit_number in range(1, study.visits_to_show_in_subject_list + 1):
visit_key = "visit_" + str(visit_number)
add_column(result, "Visit " + str(visit_number), visit_key, None, "visit_filter",
visible_param=study_subject_list.visits)
......@@ -133,7 +146,8 @@ 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')
elif order_column == "flying_team":
......@@ -173,6 +187,26 @@ def get_subjects_order(subjects_to_be_ordered, order_column, order_direction, co
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)
elif order_column == "virus_test_1":
result = subjects_to_be_ordered.order_by(order_direction + 'virus_test_1')
elif order_column == "virus_test_2":
result = subjects_to_be_ordered.order_by(order_direction + 'virus_test_2')
elif order_column == "virus_test_3":
result = subjects_to_be_ordered.order_by(order_direction + 'virus_test_3')
elif order_column == "virus_test_4":
result = subjects_to_be_ordered.order_by(order_direction + 'virus_test_4')
elif order_column == "virus_test_5":
result = subjects_to_be_ordered.order_by(order_direction + 'virus_test_5')
elif order_column == "virus_test_1_updated":
result = subjects_to_be_ordered.order_by(order_direction + 'virus_test_1_updated')
elif order_column == "virus_test_2_updated":
result = subjects_to_be_ordered.order_by(order_direction + 'virus_test_2_updated')
elif order_column == "virus_test_3_updated":
result = subjects_to_be_ordered.order_by(order_direction + 'virus_test_3_updated')
elif order_column == "virus_test_4_updated":
result = subjects_to_be_ordered.order_by(order_direction + 'virus_test_4_updated')
elif order_column == "virus_test_5_updated":
result = subjects_to_be_ordered.order_by(order_direction + 'virus_test_5_updated')
else:
logger.warn("Unknown sort column: " + str(order_column))
return result
......@@ -272,6 +306,31 @@ def get_subjects_filtered(subjects_to_be_filtered, filters):
result = result.filter(resigned=(value == "true"))
elif column == "endpoint_reached":
result = result.filter(endpoint_reached=(value == "true"))
elif column == "virus_test_1":
if value == "null":
result = result.filter(virus_test_1__isnull=True)
else:
result = result.filter(virus_test_1=(value == "true"))
elif column == "virus_test_2":
if value == "null":
result = result.filter(virus_test_2__isnull=True)
else:
result = result.filter(virus_test_2=(value == "true"))
elif column == "virus_test_3":
if value == "null":
result = result.filter(virus_test_3__isnull=True)
else:
result = result.filter(virus_test_3=(value == "true"))
elif column == "virus_test_4":
if value == "null":
result = result.filter(virus_test_4__isnull=True)
else:
result = result.filter(virus_test_4=(value == "true"))
elif column == "virus_test_5":
if value == "null":
result = result.filter(virus_test_5__isnull=True)
else:
result = result.filter(virus_test_5=(value == "true"))
elif column == "brain_donation_agreement":
result = result.filter(brain_donation_agreement=(value == "true"))
elif column == "postponed":
......@@ -358,6 +417,7 @@ def types(request):
"types": data
})
def serialize_subject(study_subject):
location = location_to_str(study_subject.default_location)
flying_team = flying_team_to_str(study_subject.flying_team)
......@@ -388,7 +448,7 @@ def serialize_subject(study_subject):
status = "SHOULD_BE_IN_PROGRESS"
else:
status = "UPCOMING"
appointment_types = ['{} ({})'.format(at.code, at.description) for at in visit.appointment_types.all()]
if len(appointment_types) == 0:
appointment_types = ['No appointment types set.']
......@@ -436,6 +496,16 @@ def serialize_subject(study_subject):
"dead": bool_to_yes_no(study_subject.subject.dead),
"resigned": bool_to_yes_no(study_subject.resigned),
"endpoint_reached": bool_to_yes_no(study_subject.endpoint_reached),
"virus_test_1": bool_to_yes_no_null(study_subject.virus_test_1),
"virus_test_2": bool_to_yes_no_null(study_subject.virus_test_2),
"virus_test_3": bool_to_yes_no_null(study_subject.virus_test_3),
"virus_test_4": bool_to_yes_no_null(study_subject.virus_test_4),
"virus_test_5": bool_to_yes_no_null(study_subject.virus_test_5),
"virus_test_1_updated": study_subject.virus_test_1_updated,
"virus_test_2_updated": study_subject.virus_test_2_updated,
"virus_test_3_updated": study_subject.virus_test_3_updated,
"virus_test_4_updated": study_subject.virus_test_4_updated,
"virus_test_5_updated": study_subject.virus_test_5_updated,
"postponed": bool_to_yes_no(study_subject.postponed),
"brain_donation_agreement": bool_to_yes_no(study_subject.brain_donation_agreement),
"excluded": bool_to_yes_no(study_subject.excluded),
......@@ -447,4 +517,4 @@ def serialize_subject(study_subject):
"id": study_subject.id,
"visits": serialized_visits,
}
return result
\ No newline at end of file
return result
......@@ -36,6 +36,17 @@ class StudySubjectAddForm(StudySubjectForm):
self.study = get_study_from_args(kwargs)
super(StudySubjectAddForm, self).__init__(*args, **kwargs)
self.fields['virus_test_1'].widget.attrs['readonly'] = True
self.fields['virus_test_2'].widget.attrs['readonly'] = True
self.fields['virus_test_3'].widget.attrs['readonly'] = True
self.fields['virus_test_4'].widget.attrs['readonly'] = True
self.fields['virus_test_5'].widget.attrs['readonly'] = True
self.fields['virus_test_1_updated'].widget.attrs['readonly'] = True
self.fields['virus_test_2_updated'].widget.attrs['readonly'] = True
self.fields['virus_test_3_updated'].widget.attrs['readonly'] = True
self.fields['virus_test_4_updated'].widget.attrs['readonly'] = True
self.fields['virus_test_5_updated'].widget.attrs['readonly'] = True
prepare_study_subject_fields(fields=self.fields, study=self.study)
def save(self, commit=True):
......@@ -128,6 +139,18 @@ class StudySubjectEditForm(StudySubjectForm):
self.fields['resigned'].disabled = was_resigned
self.fields['endpoint_reached'].disabled = endpoint_was_reached
self.fields['virus_test_1'].widget.attrs['readonly'] = True
self.fields['virus_test_2'].widget.attrs['readonly'] = True
self.fields['virus_test_3'].widget.attrs['readonly'] = True
self.fields['virus_test_4'].widget.attrs['readonly'] = True
self.fields['virus_test_5'].widget.attrs['readonly'] = True
self.fields['virus_test_1_updated'].widget.attrs['readonly'] = True
self.fields['virus_test_2_updated'].widget.attrs['readonly'] = True
self.fields['virus_test_3_updated'].widget.attrs['readonly'] = True
self.fields['virus_test_4_updated'].widget.attrs['readonly'] = True
self.fields['virus_test_5_updated'].widget.attrs['readonly'] = True
prepare_study_subject_fields(fields=self.fields, study=self.study)
def clean(self):
......@@ -181,6 +204,17 @@ def prepare_study_subject_fields(fields, study):
prepare_field(fields, study.columns, 'previously_in_study')
prepare_field(fields, study.columns, 'voucher_types')
prepare_field(fields, study.columns, 'virus_test_1')
prepare_field(fields, study.columns, 'virus_test_2')
prepare_field(fields, study.columns, 'virus_test_3')
prepare_field(fields, study.columns, 'virus_test_4')
prepare_field(fields, study.columns, 'virus_test_5')
prepare_field(fields, study.columns, 'virus_test_1_updated')
prepare_field(fields, study.columns, 'virus_test_2_updated')
prepare_field(fields, study.columns, 'virus_test_3_updated')
prepare_field(fields, study.columns, 'virus_test_4_updated')
prepare_field(fields, study.columns, 'virus_test_5_updated')
def validate_subject_screening_number(self, cleaned_data):
if self.study.columns.resign_reason:
......
# -*- coding: utf-8 -*-
# Generated by Django 1.10.3 on 2017-04-04 09:43
from __future__ import unicode_literals
from django.db import migrations
from web.models.constants import RED_CAP_LANGUAGE_4_FIELD_TYPE, RED_CAP_LANGUAGE_3_FIELD_TYPE, \
RED_CAP_LANGUAGE_2_FIELD_TYPE, RED_CAP_LANGUAGE_1_FIELD_TYPE, RED_CAP_MPOWER_ID_FIELD_TYPE, RED_CAP_DEAD_FIELD_TYPE, \
RED_CAP_SEX_FIELD_TYPE, RED_CAP_DATE_BORN_FIELD_TYPE, RED_CAP_ND_NUMBER_FIELD_TYPE, RED_CAP_VIRUS_FIELD_TYPE
def create_item(apps, type, value, name):
# We can't import the ConfigurationItem model directly as it may be a newer
# version than this migration expects. We use the historical version.
ConfigurationItem = apps.get_model("web", "ConfigurationItem")
item = ConfigurationItem.objects.create()
item.type = type
item.value = value
item.name = name
item.save()
def configuration_items(apps, schema_editor):
create_item(apps, RED_CAP_LANGUAGE_4_FIELD_TYPE, "dm_language_4",
"Redcap field for language 4")
create_item(apps, RED_CAP_LANGUAGE_3_FIELD_TYPE, "dm_language_3",
"Redcap field for language 3")
create_item(apps, RED_CAP_LANGUAGE_2_FIELD_TYPE, "dm_language_2",
"Redcap field for language 2")
create_item(apps, RED_CAP_LANGUAGE_1_FIELD_TYPE, "dm_language_1",
"Redcap field for language 1")
create_item(apps, RED_CAP_MPOWER_ID_FIELD_TYPE, "dm_mpowerid",
"Redcap field for mPowerId")
create_item(apps, RED_CAP_DEAD_FIELD_TYPE, "dm_death",
"Redcap field for deceased")
create_item(apps, RED_CAP_SEX_FIELD_TYPE, "cdisc_dm_sex",
"Redcap field for sex")
create_item(apps, RED_CAP_DATE_BORN_FIELD_TYPE, "cdisc_dm_brthdtc",
"Redcap field for birth date")
create_item(apps, RED_CAP_ND_NUMBER_FIELD_TYPE, "cdisc_dm_usubjd",
"Redcap field for subject id")
create_item(apps, RED_CAP_VIRUS_FIELD_TYPE, "",
"Redcap field for virus test result")
class Migration(migrations.Migration):
dependencies = [
('web', '0158_configurationitem_email_items'),
]
operations = [
migrations.RunPython(configuration_items),
]
# -*- coding: utf-8 -*-
# Generated by Django 1.11.5 on 2020-04-15 11:01
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('web', '0159_configurationitem_email_items_for_redcap'),
]
operations = [
migrations.AddField(
model_name='studycolumns',
name='virus_test_1',
field=models.BooleanField(default=False, verbose_name=b'Visit 1 virus results'),
),
migrations.AddField(
model_name='studycolumns',
name='virus_test_1_updated',
field=models.BooleanField(default=False, verbose_name=b'Visit 1 virus results date'),
),
migrations.AddField(
model_name='studycolumns',
name='virus_test_2',
field=models.BooleanField(default=False, verbose_name=b'Visit 2 virus results'),
),
migrations.AddField(
model_name='studycolumns',
name='virus_test_2_updated',
field=models.BooleanField(default=False, verbose_name=b'Visit 2 virus results date'),
),
migrations.AddField(
model_name='studycolumns',
name='virus_test_3',
field=models.BooleanField(default=False, verbose_name=b'Visit 3 virus results'),
),
migrations.AddField(
model_name='studycolumns',
name='virus_test_3_updated',
field=models.BooleanField(default=False, verbose_name=b'Visit 3 virus results date'),
),
migrations.AddField(
model_name='studycolumns',
name='virus_test_4',
field=models.BooleanField(default=False, verbose_name=b'Visit 4 virus results'),
),
migrations.AddField(
model_name='studycolumns',
name='virus_test_4_updated',
field=models.BooleanField(default=False, verbose_name=b'Visit 4 virus results date'),
),
migrations.AddField(
model_name='studycolumns',
name='virus_test_5',
field=models.BooleanField(default=False, verbose_name=b'Visit 5 virus results'),
),
migrations.AddField(
model_name='studycolumns',
name='virus_test_5_updated',
field=models.BooleanField(default=False, verbose_name=b'Visit 5 virus results date'),
),
migrations.AddField(
model_name='studysubject',
name='virus_test_1',
field=models.NullBooleanField(choices=[(True, b'Yes'), (False, b'No'), (None, b'N/A')], default=None, editable=False, verbose_name=b'Visit 1 virus result'),
),
migrations.AddField(
model_name='studysubject',
name='virus_test_1_updated',
field=models.DateField(editable=False, null=True, verbose_name=b'Visit 1 virus result date'),
),
migrations.AddField(
model_name='studysubject',
name='virus_test_2',
field=models.NullBooleanField(choices=[(True, b'Yes'), (False, b'No'), (None, b'N/A')], default=None, editable=False, verbose_name=b'Visit 2 virus result'),
),
migrations.AddField(
model_name='studysubject',
name='virus_test_2_updated',
field=models.DateField(editable=False, null=True, verbose_name=b'Visit 2 virus result date'),
),
migrations.AddField(
model_name='studysubject',
name='virus_test_3',
field=models.NullBooleanField(choices=[(True, b'Yes'), (False, b'No'), (None, b'N/A')], default=None, editable=False, verbose_name=b'Visit 3 virus result'),
),
migrations.AddField(
model_name='studysubject',
name='virus_test_3_updated',
field=models.DateField(editable=False, null=True, verbose_name=b'Visit 3 virus result date'),
),
migrations.AddField(
model_name='studysubject',
name='virus_test_4',
field=models.NullBooleanField(choices=[(True, b'Yes'), (False, b'No'), (None, b'N/A')], default=None, editable=False, verbose_name=b'Visit 4 virus result'),
),
migrations.AddField(
model_name='studysubject',
name='virus_test_4_updated',
field=models.DateField(editable=False, null=True, verbose_name=b'Visit 4 virus result date'),
),
migrations.AddField(
model_name='studysubject',
name='virus_test_5',
field=models.NullBooleanField(choices=[(True, b'Yes'), (False, b'No'), (None, b'N/A')], default=None, editable=False, verbose_name=b'Visit 5 virus result'),
),
migrations.AddField(
model_name='studysubject',
name='virus_test_5_updated',
field=models.DateField(editable=False, null=True, verbose_name=b'Visit 5 virus result date'),
),
]
......@@ -10,6 +10,11 @@ SEX_CHOICES = (
(SEX_CHOICES_MALE, 'Male'),
(SEX_CHOICES_FEMALE, 'Female'),
)
BOOL_CHOICES_WITH_NONE = (
(True, 'Yes'),
(False, 'No'),
(None, 'N/A'),
)
SUBJECT_TYPE_CHOICES_CONTROL = 'C'
SUBJECT_TYPE_CHOICES_PATIENT = 'P'
SUBJECT_TYPE_CHOICES = {
......@@ -47,6 +52,17 @@ KIT_EMAIL_DAY_OF_WEEK_CONFIGURATION_TYPE = "KIT_EMAIL_DAY_OF_WEEK_CONFIGURATION_
KIT_DAILY_EMAIL_DAYS_PERIOD_TYPE = "KIT_DAILY_EMAIL_DAYS_PERIOD_TYPE"
KIT_DAILY_EMAIL_TIME_FORMAT_TYPE = "KIT_DAILY_EMAIL_TIME_FORMAT_TYPE"
RED_CAP_LANGUAGE_4_FIELD_TYPE = 'RED_CAP_LANGUAGE_4_FIELD_TYPE'
RED_CAP_LANGUAGE_3_FIELD_TYPE = 'RED_CAP_LANGUAGE_3_FIELD_TYPE'
RED_CAP_LANGUAGE_2_FIELD_TYPE = 'RED_CAP_LANGUAGE_2_FIELD_TYPE'
RED_CAP_LANGUAGE_1_FIELD_TYPE = 'RED_CAP_LANGUAGE_1_FIELD_TYPE'
RED_CAP_MPOWER_ID_FIELD_TYPE = 'RED_CAP_MPOWER_ID_FIELD_TYPE'
RED_CAP_DEAD_FIELD_TYPE = 'RED_CAP_DEAD_FIELD_TYPE'
RED_CAP_SEX_FIELD_TYPE = 'RED_CAP_SEX_FIELD_TYPE'
RED_CAP_DATE_BORN_FIELD_TYPE = 'RED_CAP_DATE_BORN_FIELD_TYPE'
RED_CAP_ND_NUMBER_FIELD_TYPE = 'RED_CAP_ND_NUMBER_FIELD_TYPE'
RED_CAP_VIRUS_FIELD_TYPE = 'RED_CAP_VIRUS_FIELD_TYPE'
MAIL_TEMPLATE_CONTEXT_SUBJECT = 'S'
MAIL_TEMPLATE_CONTEXT_APPOINTMENT = 'A'
MAIL_TEMPLATE_CONTEXT_VISIT = 'V'
......
......@@ -12,69 +12,69 @@ 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')
......@@ -83,41 +83,82 @@ class StudyColumns(models.Model):
resign_reason = models.BooleanField(default=True, verbose_name='Endpoint reached comments')
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',
)
brain_donation_agreement = models.BooleanField(
default=False,
verbose_name='Brain donation agreement',
)
default=False,
verbose_name='Brain donation agreement',
)
virus_test_1 = models.BooleanField(
default=False,
verbose_name='Visit 1 virus results',
)
virus_test_2 = models.BooleanField(
default=False,
verbose_name='Visit 2 virus results',
)
virus_test_3 = models.BooleanField(
default=False,
verbose_name='Visit 3 virus results',
)
virus_test_4 = models.BooleanField(
default=False,
verbose_name='Visit 4 virus results',
)
virus_test_5 = models.BooleanField(
default=False,
verbose_name='Visit 5 virus results',
)
virus_test_1_updated = models.BooleanField(
default=False,
verbose_name='Visit 1 virus results date',
)
virus_test_2_updated = models.BooleanField(
default=False,
verbose_name='Visit 2 virus results date',
)
virus_test_3_updated = models.BooleanField(
default=False,
verbose_name='Visit 3 virus results date',
)
virus_test_4_updated = models.BooleanField(
default=False,
verbose_name='Visit 4 virus results date',
)
virus_test_5_updated = models.BooleanField(
default=False,
verbose_name='Visit 5 virus results date',
)
# coding=utf-8
import logging
import re
from django.db import models
from web.models import VoucherType, Appointment, Location, Visit, Provenance
from web.models.constants import BOOL_CHOICES, SUBJECT_TYPE_CHOICES, FILE_STORAGE
from django.db import models
from django.db.models.signals import post_save
from django.dispatch import receiver
from web.models import VoucherType, Appointment, Location, Visit, Provenance
from web.models.constants import BOOL_CHOICES, SUBJECT_TYPE_CHOICES, FILE_STORAGE, BOOL_CHOICES_WITH_NONE
logger = logging.getLogger(__name__)
class StudySubject(models.Model):
class StudySubject(models.Model):
class Meta:
app_label = 'web'
@property
def provenances(self):
return Provenance.objects.filter(modified_table=StudySubject._meta.db_table, modified_table_id=self.id).order_by('-modification_date')
return Provenance.objects.filter(modified_table=StudySubject._meta.db_table,
modified_table_id=self.id).order_by('-modification_date')
def finish_all_visits(self):
visits = Visit.objects.filter(subject=self, is_finished=False)
......@@ -168,9 +170,9 @@ class StudySubject(models.Model):
default=None,
)
brain_donation_agreement = models.BooleanField(
default=False,
verbose_name='Brain donation agreement',
)
default=False,
verbose_name='Brain donation agreement',
)
resigned = models.BooleanField(
verbose_name='Resigned',
......@@ -196,42 +198,84 @@ class StudySubject(models.Model):
editable=True
)
endpoint_reached_reason = models.TextField(max_length=2000,
blank=True,
verbose_name='Endpoint reached comments'
)
blank=True,
verbose_name='Endpoint reached comments'
)
virus_test_1 = models.NullBooleanField(choices=BOOL_CHOICES_WITH_NONE,
verbose_name='Visit 1 virus result',
default=None
)
virus_test_2 = models.NullBooleanField(choices=BOOL_CHOICES_WITH_NONE,
verbose_name='Visit 2 virus result',
default=None
)
virus_test_3 = models.NullBooleanField(choices=BOOL_CHOICES_WITH_NONE,
verbose_name='Visit 3 virus result',
default=None
)
virus_test_4 = models.NullBooleanField(choices=BOOL_CHOICES_WITH_NONE,
verbose_name='Visit 4 virus result',
default=None
)
virus_test_5 = models.NullBooleanField(choices=BOOL_CHOICES_WITH_NONE,
verbose_name='Visit 5 virus result',
default=None
)
virus_test_1_updated = models.DateField(verbose_name='Visit 1 virus result date',
blank=True,
null=True,
)
virus_test_2_updated = models.DateField(verbose_name='Visit 2 virus result date',
blank=True,
null=True,
)
virus_test_3_updated = models.DateField(verbose_name='Visit 3 virus result date',
blank=True,
null=True,
)
virus_test_4_updated = models.DateField(verbose_name='Visit 4 virus result date',
blank=True,
null=True,
)
virus_test_5_updated = models.DateField(verbose_name='Visit 5 virus result date',
blank=True,
null=True,
)
def sort_matched_screening_first(self, pattern, reverse=False):
if self.screening_number is None:
return None
parts = self.screening_number.split(';')
matches, reminder = [], []
try:
for part in parts:
chunks = part.strip().split('-')
if len(chunks) == 2:
letter, number = chunks
try:
tupl = (letter, int(number))
except ValueError: #better than isdigit because isdigit fails with negative numbers and others
tupl = (letter, number)
else:
logger.warn('There are {} chunks in some parts of this screening_number: |{}|.'.format(
len(chunks), self.screening_number))
tupl = (part.strip(), None)
if pattern is not None and pattern in part:
matches.append(tupl)
else:
reminder.append(tupl)
except: #if the format is not the expected format
matches = parts
return matches + sorted(reminder, reverse=reverse)
if self.screening_number is None:
return None
parts = self.screening_number.split(';')
matches, reminder = [], []
try:
for part in parts:
chunks = part.strip().split('-')
if len(chunks) == 2:
letter, number = chunks
try:
tupl = (letter, int(number))
except ValueError: # better than isdigit because isdigit fails with negative numbers and others
tupl = (letter, number)
else:
logger.warn('There are {} chunks in some parts of this screening_number: |{}|.'.format(
len(chunks), self.screening_number))
tupl = (part.strip(), None)
if pattern is not None and pattern in part:
matches.append(tupl)
else:
reminder.append(tupl)
except: # if the format is not the expected format
matches = parts
return matches + sorted(reminder, reverse=reverse)
@staticmethod
def check_nd_number_regex(regex_str, study):
nd_numbers = StudySubject.objects.filter(study=study).exclude(nd_number__isnull=True).exclude(nd_number__exact='').all().values_list('nd_number', flat=True)
nd_numbers = StudySubject.objects.filter(study=study).exclude(nd_number__isnull=True).exclude(
nd_number__exact='').all().values_list('nd_number', flat=True)
regex = re.compile(regex_str)
for nd_number in nd_numbers:
if regex.match(nd_number) is None:
......@@ -239,21 +283,20 @@ class StudySubject(models.Model):
return True
def can_schedule(self):
return not any([self.resigned, self.excluded, self.endpoint_reached, self.subject.dead])
return not any([self.resigned, self.excluded, self.endpoint_reached, self.subject.dead])
@property
def status(self):
if self.subject.dead:
return 'Deceased'
elif self.resigned:
return 'Resigned'
elif self.excluded:
return 'Excluded'
elif self.endpoint_reached:
return 'Endpoint Reached'
else:
return 'Normal'
if self.subject.dead:
return 'Deceased'
elif self.resigned:
return 'Resigned'
elif self.excluded:
return 'Excluded'
elif self.endpoint_reached:
return 'Endpoint Reached'
else:
return 'Normal'
def __str__(self):
return "%s %s" % (self.subject.first_name, self.subject.last_name)
......@@ -261,12 +304,13 @@ class StudySubject(models.Model):
def __unicode__(self):
return "%s %s" % (self.subject.first_name, self.subject.last_name)
#SIGNALS
# SIGNALS
@receiver(post_save, sender=StudySubject)
def set_as_resigned_or_excluded_or_endpoint_reached(sender, instance, **kwargs):
if instance.excluded:
instance.mark_as_excluded()
if instance.resigned:
instance.mark_as_resigned()
if instance.endpoint_reached:
instance.mark_as_endpoint_reached()
\ No newline at end of file
if instance.excluded:
instance.mark_as_excluded()
if instance.resigned:
instance.mark_as_resigned()
if instance.endpoint_reached:
instance.mark_as_endpoint_reached()
# coding=utf-8
import cStringIO
import datetime
import json
import logging
import pycurl
import certifi
import pycurl
import timeout_decorator
from django.conf import settings
from django.forms.models import model_to_dict
from django_cron import CronJobBase, Schedule
from django.forms.models import model_to_dict
from web.models.constants import GLOBAL_STUDY_ID
from web.models import ConfigurationItem, StudySubject, Language, Study, StudyRedCapColumns
from web.models import ConfigurationItem, StudySubject, Language, AppointmentType, Appointment, Visit
from web.models.constants import REDCAP_TOKEN_CONFIGURATION_TYPE, \
REDCAP_BASE_URL_CONFIGURATION_TYPE, CRON_JOB_TIMEOUT
REDCAP_BASE_URL_CONFIGURATION_TYPE, CRON_JOB_TIMEOUT, RED_CAP_LANGUAGE_4_FIELD_TYPE, RED_CAP_LANGUAGE_3_FIELD_TYPE, \
RED_CAP_LANGUAGE_2_FIELD_TYPE, RED_CAP_LANGUAGE_1_FIELD_TYPE, RED_CAP_MPOWER_ID_FIELD_TYPE, RED_CAP_DEAD_FIELD_TYPE, \
RED_CAP_SEX_FIELD_TYPE, RED_CAP_DATE_BORN_FIELD_TYPE, RED_CAP_ND_NUMBER_FIELD_TYPE, RED_CAP_VIRUS_FIELD_TYPE
from web.models.inconsistent_subject import InconsistentField, InconsistentSubject
from web.models.missing_subject import MissingSubject
RED_CAP_LANGUAGE_4_FIELD = 'dm_language_4'
RED_CAP_LANGUAGE_3_FIELD = 'dm_language_3'
RED_CAP_LANGUAGE_2_FIELD = 'dm_language_2'
RED_CAP_LANGUAGE_1_FIELD = 'dm_language_1'
# noinspection SpellCheckingInspection
RED_CAP_MPOWER_ID_FIELD = 'dm_mpowerid'
RED_CAP_DEAD_FIELD = 'dm_death'
# noinspection SpellCheckingInspection
RED_CAP_SEX_FIELD = 'cdisc_dm_sex'
# noinspection SpellCheckingInspection
RED_CAP_DATE_BORN_FIELD = 'cdisc_dm_brthdtc'
# noinspection SpellCheckingInspection
RED_CAP_ND_NUMBER_FIELD = 'cdisc_dm_usubjd'
logger = logging.getLogger(__name__)
......@@ -52,12 +33,18 @@ class RedcapSubject(object):
def __init__(self):
self.languages = []
self.visits = []
def add_language(self, language):
if language is not None:
self.languages.append(language)
class RedcapVisit(object):
virus = None
visit_number = 0
def different_string(string1, string2):
if string1 is None:
string1 = ""
......@@ -83,6 +70,28 @@ class RedcapConnector(object):
for language in languages:
self.language_by_name[language.name.lower()] = language
self.date_born_field = ConfigurationItem.objects.get(type=RED_CAP_DATE_BORN_FIELD_TYPE).value
self.sex_field = ConfigurationItem.objects.get(type=RED_CAP_SEX_FIELD_TYPE).value
self.nd_number_field = ConfigurationItem.objects.get(type=RED_CAP_ND_NUMBER_FIELD_TYPE).value
self.dead_field = ConfigurationItem.objects.get(type=RED_CAP_DEAD_FIELD_TYPE).value
self.language_1_field = ConfigurationItem.objects.get(type=RED_CAP_LANGUAGE_1_FIELD_TYPE).value
self.language_2_field = ConfigurationItem.objects.get(type=RED_CAP_LANGUAGE_2_FIELD_TYPE).value
self.language_3_field = ConfigurationItem.objects.get(type=RED_CAP_LANGUAGE_3_FIELD_TYPE).value
self.language_4_field = ConfigurationItem.objects.get(type=RED_CAP_LANGUAGE_4_FIELD_TYPE).value
self.m_power_id_field = ConfigurationItem.objects.get(type=RED_CAP_MPOWER_ID_FIELD_TYPE).value
self.virus_field = ConfigurationItem.objects.get(type=RED_CAP_VIRUS_FIELD_TYPE).value
self.date_born_field = ""
self.sex_field = ""
self.nd_number_field = "donor_id"
self.dead_field = ""
self.language_1_field = ""
self.language_2_field = ""
self.language_3_field = ""
self.language_4_field = ""
self.m_power_id_field = ""
self.virus_field = "sarscov2_status"
def find_missing(self):
pid = self.get_project_id()
redcap_version = self.get_redcap_version()
......@@ -157,6 +166,13 @@ class RedcapConnector(object):
self.add_inconsistent(inconsistent)
def find_inconsistent(self):
appointment_type_code_to_finish = getattr(settings, "IMPORT_APPOINTMENT_TYPE", None)
appointment_type_to_finish = None
if appointment_type_code_to_finish is not None:
appointment_types = AppointmentType.objects.filter(code=appointment_type_code_to_finish)
if len(appointment_types) > 0:
appointment_type_to_finish = appointment_types[0]
pid = self.get_project_id()
redcap_version = self.get_redcap_version()
......@@ -173,9 +189,41 @@ class RedcapConnector(object):
if red_cap_subject is not None:
url = self.create_redcap_link(pid, redcap_version, subject)
subject = self.create_inconsistency_subject(red_cap_subject, subject, url)
if subject is not None:
result.append(subject)
inconsistent_subject = self.create_inconsistency_subject(red_cap_subject, subject, url)
if inconsistent_subject is not None:
result.append(inconsistent_subject)
if appointment_type_to_finish is not None:
for visit in red_cap_subject.visits:
smasch_visits = Visit.objects.filter(visit_number=visit.visit_number, subject=subject)
smasch_appointments = Appointment.objects.filter(visit__in=smasch_visits,
appointment_types=appointment_type_to_finish,
status=Appointment.APPOINTMENT_STATUS_SCHEDULED)
for smasch_appointment in smasch_appointments:
smasch_appointment.mark_as_finished()
smasch_appointment.visit.is_finished = True
smasch_appointment.visit.save()
if visit.virus is not None:
if visit.visit_number == 1 and subject.virus_test_1 != visit.virus:
subject.virus_test_1 = visit.virus
subject.virus_test_1_updated = datetime.datetime.now()
subject.save()
if visit.visit_number == 2 and subject.virus_test_2 != visit.virus:
subject.virus_test_2 = visit.virus
subject.virus_test_2_updated = datetime.datetime.now()
subject.save()
if visit.visit_number == 3 and subject.virus_test_3 != visit.virus:
subject.virus_test_3 = visit.virus
subject.virus_test_3_updated = datetime.datetime.now()
subject.save()
if visit.visit_number == 4 and subject.virus_test_4 != visit.virus:
subject.virus_test_4 = visit.virus
subject.virus_test_4_updated = datetime.datetime.now()
subject.save()
if visit.visit_number == 5 and subject.virus_test_5 != visit.virus:
subject.virus_test_5 = visit.virus
subject.virus_test_5_updated = datetime.datetime.now()
subject.save()
return result
......@@ -228,7 +276,7 @@ class RedcapConnector(object):
@staticmethod
def create_inconsistency_subject(red_cap_subject, study_subject, url):
#func dict
# func dict
field_checks = {
'sex': RedcapConnector.check_sex_consistency,
'date_born': RedcapConnector.check_birth_date_consistency,
......@@ -239,14 +287,14 @@ class RedcapConnector(object):
fields = []
#get fields which are true from redcap columns
fields_to_check = [k for k,v in model_to_dict(study_subject.study.redcap_columns).iteritems() if v is True]
# get fields which are true from redcap columns
fields_to_check = [k for k, v in model_to_dict(study_subject.study.redcap_columns).iteritems() if v is True]
for field_to_check in fields_to_check:
field = field_checks[field_to_check](red_cap_subject, study_subject)
if field is not None:
fields.append(field)
result = None
if len(fields) > 0:
result = InconsistentSubject.create(smash_subject=study_subject, url=url, fields=fields)
......@@ -257,20 +305,66 @@ class RedcapConnector(object):
subject.nd_number + "&page=demographics"
def get_red_cap_subjects(self):
query_data = {
query_data = self.get_subject_query_data()
data = self.execute_query(query_data)
result = []
for row in data:
if isinstance(row, dict):
redcap_subject = RedcapSubject()
redcap_subject.nd_number = row.get(self.nd_number_field)
if self.date_born_field != "":
redcap_subject.date_born = row.get(self.date_born_field)
if self.sex_field != "":
redcap_subject.sex = row.get(self.sex_field)
if self.dead_field != "":
redcap_subject.dead = (row.get(self.dead_field).lower() == "yes")
if self.m_power_id_field != "":
redcap_subject.mpower_id = row.get(self.m_power_id_field)
if self.language_1_field != "" and row.get(self.language_1_field):
redcap_subject.add_language(self.get_language(row.get(self.language_1_field)))
if self.language_2_field != "" and row[self.language_2_field]:
redcap_subject.add_language(self.get_language(row.get(self.language_2_field)))
if self.language_3_field != "" and row[self.language_3_field]:
redcap_subject.add_language(self.get_language(row.get(self.language_3_field)))
if self.language_4_field != "" and row[self.language_4_field]:
redcap_subject.add_language(self.get_language(row.get(self.language_4_field)))
visit = RedcapVisit()
visit.visit_number = 1
if self.virus_field != "":
if row.get(self.virus_field) == "Negative":
visit.virus = False
elif row.get(self.virus_field) == "Positive":
visit.virus = True
redcap_subject.visits.append(visit)
result.append(redcap_subject)
for i in range(2, 10):
query_data = self.get_subject_query_data()
query_data["events[0]"] = "visit_" + str(i) + "_arm_1"
data = self.execute_query(query_data)
if isinstance(data, dict):
break
for row in data:
if isinstance(row, dict):
nd_number = row.get(self.nd_number_field)
for redcap_subject in result:
if redcap_subject.nd_number == nd_number:
visit = RedcapVisit()
visit.visit_number = i
if self.virus_field != "":
if row.get(self.virus_field) == "Negative":
visit.virus = False
elif row.get(self.virus_field) == "Positive":
visit.virus = True
redcap_subject.visits.append(visit)
return result
def get_subject_query_data(self):
result = {
'token': self.token,
'content': 'record',
'format': 'json',
'type': 'flat',
'fields[0]': RED_CAP_DATE_BORN_FIELD,
'fields[1]': RED_CAP_SEX_FIELD,
'fields[2]': RED_CAP_ND_NUMBER_FIELD,
'fields[3]': RED_CAP_DEAD_FIELD,
'fields[4]': RED_CAP_LANGUAGE_1_FIELD,
'fields[5]': RED_CAP_LANGUAGE_2_FIELD,
'fields[6]': RED_CAP_LANGUAGE_3_FIELD,
'fields[7]': RED_CAP_LANGUAGE_4_FIELD,
'fields[8]': RED_CAP_MPOWER_ID_FIELD,
'events[0]': 'visit_1_arm_1',
'rawOrLabel': 'label',
'rawOrLabelHeaders': 'raw',
......@@ -279,24 +373,37 @@ class RedcapConnector(object):
'exportDataAccessGroups': 'false',
'returnFormat': 'json'
}
data = self.execute_query(query_data)
result = []
for row in data:
redcap_subject = RedcapSubject()
redcap_subject.nd_number = row[RED_CAP_ND_NUMBER_FIELD]
redcap_subject.date_born = row[RED_CAP_DATE_BORN_FIELD]
redcap_subject.sex = row[RED_CAP_SEX_FIELD]
redcap_subject.dead = (row[RED_CAP_DEAD_FIELD].lower() == "yes")
redcap_subject.mpower_id = row[RED_CAP_MPOWER_ID_FIELD]
if row[RED_CAP_LANGUAGE_1_FIELD]:
redcap_subject.add_language(self.get_language(row[RED_CAP_LANGUAGE_1_FIELD]))
if row[RED_CAP_LANGUAGE_2_FIELD]:
redcap_subject.add_language(self.get_language(row[RED_CAP_LANGUAGE_2_FIELD]))
if row[RED_CAP_LANGUAGE_3_FIELD]:
redcap_subject.add_language(self.get_language(row[RED_CAP_LANGUAGE_3_FIELD]))
if row[RED_CAP_LANGUAGE_4_FIELD]:
redcap_subject.add_language(self.get_language(row[RED_CAP_LANGUAGE_4_FIELD]))
result.append(redcap_subject)
field_number = 0
if self.date_born_field != "":
result['fields[' + str(field_number) + ']'] = self.date_born_field
field_number += 1
if self.sex_field != "":
result['fields[' + str(field_number) + ']'] = self.sex_field
field_number += 1
if self.nd_number_field != "":
result['fields[' + str(field_number) + ']'] = self.nd_number_field
field_number += 1
if self.dead_field != "":
result['fields[' + str(field_number) + ']'] = self.dead_field
field_number += 1
if self.language_1_field != "":
result['fields[' + str(field_number) + ']'] = self.language_1_field
field_number += 1
if self.language_2_field != "":
result['fields[' + str(field_number) + ']'] = self.language_2_field
field_number += 1
if self.language_3_field != "":
result['fields[' + str(field_number) + ']'] = self.language_3_field
field_number += 1
if self.language_4_field != "":
result['fields[' + str(field_number) + ']'] = self.language_4_field
field_number += 1
if self.m_power_id_field != "":
result['fields[' + str(field_number) + ']'] = self.m_power_id_field
field_number += 1
if self.virus_field != "":
result['fields[' + str(field_number) + ']'] = self.virus_field
field_number += 1
return result
def get_language(self, name):
......
......@@ -300,6 +300,9 @@ function createTable(params) {
$(tableElement).find('tfoot div[name="yes_no_filter"]').each(function () {
$(this).html('<select style="width:60px" ><option value selected="selected">---</option><option value="true">YES</option><option value="false">NO</option></select>');
});
$(tableElement).find('tfoot div[name="yes_no_null_filter"]').each(function () {
$(this).html('<select style="width:60px" ><option value selected="selected">---</option><option value="true">YES</option><option value="false">NO</option><option value="null">N/A</option></select>');
});
$(tableElement).find('tfoot div[name="integer_filter"]').each(function () {
var options = '<option value selected="selected">---</option>';
for (var i = 1; i <= 8; i++) {
......
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