diff --git a/smash/web/api_views/serialization_utils.py b/smash/web/api_views/serialization_utils.py
index 89e2c9c19c2eb5aa958a17165fe12d440b842c58..5f6cd27120fd039257378a8b3b83cedcc55b1d8e 100644
--- a/smash/web/api_views/serialization_utils.py
+++ b/smash/web/api_views/serialization_utils.py
@@ -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:
diff --git a/smash/web/api_views/subject.py b/smash/web/api_views/subject.py
index c31d13903ab43b26fa7db0fd1f9c570d28e5cb5e..cd7b5ac5dd59429c1457ea3fd30d5f90a4b87c0a 100644
--- a/smash/web/api_views/subject.py
+++ b/smash/web/api_views/subject.py
@@ -1,12 +1,12 @@
 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
diff --git a/smash/web/forms/study_subject_forms.py b/smash/web/forms/study_subject_forms.py
index 2bc880780226734cfa1653501960eb47bc8138b0..1f75992f6793598ef7d2c760af61af98ab90ab09 100644
--- a/smash/web/forms/study_subject_forms.py
+++ b/smash/web/forms/study_subject_forms.py
@@ -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:
diff --git a/smash/web/migrations/0159_configurationitem_email_items_for_redcap.py b/smash/web/migrations/0159_configurationitem_email_items_for_redcap.py
new file mode 100644
index 0000000000000000000000000000000000000000..f496a5a3f92489be49b66ac9dbac156eb07e46db
--- /dev/null
+++ b/smash/web/migrations/0159_configurationitem_email_items_for_redcap.py
@@ -0,0 +1,53 @@
+# -*- 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),
+    ]
diff --git a/smash/web/migrations/0160_auto_20200415_1101.py b/smash/web/migrations/0160_auto_20200415_1101.py
new file mode 100644
index 0000000000000000000000000000000000000000..f00f970dcc844171d9c43b7743823b5e4b2772bb
--- /dev/null
+++ b/smash/web/migrations/0160_auto_20200415_1101.py
@@ -0,0 +1,115 @@
+# -*- 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'),
+        ),
+    ]
diff --git a/smash/web/models/constants.py b/smash/web/models/constants.py
index e83ddc83fa6dc4ea020850952d9fb151f97ab3d4..b512e714efac9136e8c4d9d9bc048f77d065458f 100644
--- a/smash/web/models/constants.py
+++ b/smash/web/models/constants.py
@@ -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'
diff --git a/smash/web/models/study_columns.py b/smash/web/models/study_columns.py
index 7d20b3913d40a71f72fc0b70327a20084e83a158..d2e769ca52216cf9560933ec89d9902f3092220e 100644
--- a/smash/web/models/study_columns.py
+++ b/smash/web/models/study_columns.py
@@ -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',
+    )
diff --git a/smash/web/models/study_subject.py b/smash/web/models/study_subject.py
index 22a5b0b8fe77518abb0092587531f0b715413633..d802d8debb0e5d87dc0a54756bc9152349855cd9 100644
--- a/smash/web/models/study_subject.py
+++ b/smash/web/models/study_subject.py
@@ -1,23 +1,25 @@
 # 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()
diff --git a/smash/web/redcap_connector.py b/smash/web/redcap_connector.py
index 4e1da4fa683708eb0a6c28dc46c7ae214622f143..793e0fa221ee88c8daffcd67d08c95d069bf7498 100644
--- a/smash/web/redcap_connector.py
+++ b/smash/web/redcap_connector.py
@@ -1,43 +1,24 @@
 # 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):
diff --git a/smash/web/static/js/smash.js b/smash/web/static/js/smash.js
index 2f2846523db2447089e98c1c7f08de6accf1aff3..fecda37d4b7560d1b16fd9ee768119189ea9138c 100644
--- a/smash/web/static/js/smash.js
+++ b/smash/web/static/js/smash.js
@@ -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++) {