diff --git a/smash/smash/local_settings.py.template b/smash/smash/local_settings.py.template
index 652a544a7cbe0200a79bf08391d99e85743fc7fd..6a6d9320b67791836ca102e1869c0c1189b1e366 100644
--- a/smash/smash/local_settings.py.template
+++ b/smash/smash/local_settings.py.template
@@ -6,6 +6,8 @@ DEBUG = True
 
 WSGI_APPLICATION = 'smash.wsgi.application'
 
+IMPORTER_USER = 'admin' #username
+
 EMAIL_USE_TLS = False
 EMAIL_USE_SSL = False
 EMAIL_HOST = 'smtp.uni.lu'
diff --git a/smash/web/importer/csv_tns_visit_import_reader.py b/smash/web/importer/csv_tns_visit_import_reader.py
index ae9501c06abab72ef192b80056a2fb23e3aa1d11..4ba9eef37c2421d48809ee469bb39c379d0307f4 100644
--- a/smash/web/importer/csv_tns_visit_import_reader.py
+++ b/smash/web/importer/csv_tns_visit_import_reader.py
@@ -10,7 +10,7 @@ import pytz
 from django.conf import settings
 
 from warning_counter import MsgCounterHandler
-from web.models import StudySubject, Study, Visit, Appointment, AppointmentType, Location, AppointmentTypeLink, Subject
+from web.models import StudySubject, Study, Visit, Appointment, AppointmentType, Location, AppointmentTypeLink, Subject, User, Worker, Provenance
 from web.models.constants import GLOBAL_STUDY_ID
 
 CSV_DATE_FORMAT = "%d/%m/%Y"
@@ -33,6 +33,16 @@ class TnsCsvVisitImportReader:
         self.processed_count = 0
         self.warning_count = 0
 
+        self.importer_user = None
+
+        importer_user_name = getattr(settings, "IMPORTER_USER", None)
+        if importer_user_name is not None:
+            user = User.objects.filter(username=importer_user_name)
+            if user is None:
+                logger.warn("User does not exist: " + importer_user_name)
+            else:
+                self.importer_user = Worker.objects.filter(user=user)
+
     def load_data(self, filename):
         warning_counter = MsgCounterHandler()
         logging.getLogger('').addHandler(warning_counter)
@@ -80,22 +90,74 @@ class TnsCsvVisitImportReader:
                     if len(visits) > 0:
                         logger.debug("Visit for subject " + nd_number + " already exists. Updating")
                         visit = visits[0]
-                        visit.datetime_begin = date
-                        visit.datetime_end = date + datetime.timedelta(days=14)
+
+                        changes = [('datetime_begin', date),
+                                   ('datetime_end', date + datetime.timedelta(days=14))]
+
+                        for field, new_value in changes:
+                            old_value = getattr(visit, field)
+                            if old_value == new_value:
+                                continue
+                            description = u'{} changed from "{}" to "{}"'.format(field, old_value, new_value)
+                            p = Provenance(modified_table=Visit._meta.db_table,
+                                        modified_table_id=visit.id,
+                                        modification_author=self.importer_user,
+                                        previous_value=old_value,
+                                        new_value=new_value,
+                                        modification_description=description,
+                                        modified_field=field,
+                                        )
+                            setattr(visit, field, new_value)
+                            p.save()
+                        visit.save()
                     else:
                         visit = Visit.objects.create(subject=study_subject, visit_number=visit_number,
                                                      datetime_begin=date,
                                                      datetime_end=date + datetime.timedelta(days=14))
-                    visit.save()
+                        visit.save()
+                        #visit does not have id until .save() is done
+                        for field in Visit._meta.get_fields():
+                            if field.get_internal_type() == "CharField" or field.get_internal_type() == "DateField" or field.get_internal_type() is "BooleanField":
+                                new_value = getattr(visit, field.name)
+                                if new_value is not None and new_value != "":
+                                    description = u'{} changed from "{}" to "{}"'.format(field, '', new_value)
+                                    p = Provenance(modified_table=Visit._meta.db_table,
+                                                modified_table_id=visit.id,
+                                                modification_author=self.importer_user,
+                                                previous_value='',
+                                                new_value=new_value,
+                                                modification_description=description,
+                                                modified_field=field,
+                                                )
+                                    p.save()
+                        
                     result.append(visit)
 
                     appointments = Appointment.objects.filter(visit=visit, appointment_types=self.appointment_type)
                     if len(appointments) > 0:
                         logger.debug("Appointment for subject " + nd_number + " already set. Updating")
                         appointment = appointments[0]
-                        appointment.length = 60
-                        appointment.datetime_when = date
-                        appointment.location = location
+                        
+                        #(field, new_value)
+                        changes = [('length', 60),
+                                   ('datetime_when', date),
+                                   ('location', location)]
+
+                        for field, new_value in changes:
+                            old_value = getattr(appointment, field)
+                            if old_value == new_value:
+                                continue
+                            description = u'{} changed from "{}" to "{}"'.format(field, old_value, new_value)
+                            p = Provenance(modified_table=Appointment._meta.db_table,
+                                        modified_table_id=appointment.id,
+                                        modification_author=self.importer_user,
+                                        previous_value=old_value,
+                                        new_value=new_value,
+                                        modification_description=description,
+                                        modified_field=field,
+                                        )
+                            setattr(appointment, field, new_value)
+                            p.save()
                         appointment.save()
                     else:
                         appointment = Appointment.objects.create(visit=visit, length=60, datetime_when=date,
@@ -103,6 +165,23 @@ class TnsCsvVisitImportReader:
                         if self.appointment_type is not None:
                             AppointmentTypeLink.objects.create(appointment_id=appointment.id,
                                                                appointment_type=self.appointment_type)
+                        
+                        appointment.save()
+                        #appointment does not have id until .save() is done
+                        for field in Appointment._meta.get_fields():
+                            if field.get_internal_type() == "CharField" or field.get_internal_type() == "DateField" or field.get_internal_type() is "BooleanField":
+                                new_value = getattr(appointment, field.name)
+                                if new_value is not None and new_value != "":
+                                    description = u'{} changed from "{}" to "{}"'.format(field, '', new_value)
+                                    p = Provenance(modified_table=Appointment._meta.db_table,
+                                                modified_table_id=appointment.id,
+                                                modification_author=self.importer_user,
+                                                previous_value='',
+                                                new_value=new_value,
+                                                modification_description=description,
+                                                modified_field=field,
+                                                )
+                                    p.save()
                     self.processed_count += 1
                 except:
                     self.problematic_count += 1
diff --git a/smash/web/redcap_connector.py b/smash/web/redcap_connector.py
index a2af5169ca07e21bf5bce8098b152dbd53122891..01ada9e9c12d07b945679485dbcd6aa6dc870d7e 100644
--- a/smash/web/redcap_connector.py
+++ b/smash/web/redcap_connector.py
@@ -11,7 +11,7 @@ from django.conf import settings
 from django.forms.models import model_to_dict
 from django_cron import CronJobBase, Schedule
 
-from web.models import ConfigurationItem, StudySubject, Language, AppointmentType, Appointment, Visit, Study
+from web.models import ConfigurationItem, StudySubject, Language, AppointmentType, Appointment, Visit, Study, Provenance, Worker, User
 from web.models.constants import REDCAP_TOKEN_CONFIGURATION_TYPE, \
     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, \
@@ -86,6 +86,16 @@ class RedcapConnector(object):
 
         self.study = Study.objects.get(id=GLOBAL_STUDY_ID)
 
+        self.importer_user = None
+
+        importer_user_name = getattr(settings, "IMPORTER_USER", None)
+        if importer_user_name is not None:
+            user = User.objects.filter(username=importer_user_name)
+            if user is None:
+                logger.warn("User does not exist: " + importer_user_name)
+            else:
+                self.importer_user = Worker.objects.filter(user=user)
+
     def find_missing(self):
         pid = self.get_project_id()
         redcap_version = self.get_redcap_version()
@@ -196,43 +206,53 @@ class RedcapConnector(object):
                                                                          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 smasch_appointment.visit.is_finished != True:
+                                description = u'{} changed from "{}" to "{}"'.format('is_finished', smasch_appointment.visit.is_finished, True)
+                                p = Provenance(modified_table=Visit._meta.db_table,
+                                            modified_table_id=smasch_appointment.visit.id,
+                                            modification_author=self.importer_user,
+                                            previous_value=smasch_appointment.visit.is_finished,
+                                            new_value=True,
+                                            modification_description=description,
+                                            modified_field='is_finished')
+                                smasch_appointment.visit.is_finished = True
+                                smasch_appointment.visit.save()
                         if visit.virus is not None or visit.virus_inconclusive:
+                            changes = 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()
+                                changes = [('virus_test_1', visit.virus), ('virus_test_1_updated', datetime.datetime.now())]
                             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()
+                                changes = [('virus_test_2', visit.virus), ('virus_test_2_updated', datetime.datetime.now())]
                             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()
+                                changes = [('virus_test_3', visit.virus), ('virus_test_3_updated', datetime.datetime.now())]
                             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()
+                                changes = [('virus_test_4', visit.virus), ('virus_test_4_updated', datetime.datetime.now())]
                             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()
+                                changes = [('virus_test_5', visit.virus), ('virus_test_5_updated', datetime.datetime.now())]
                             if visit.visit_number == 1 and subject.virus_test_1_updated is None and visit.virus_inconclusive:
-                                subject.virus_test_1_updated = datetime.datetime.now()
-                                subject.save()
+                                changes = [('virus_test_1_updated', datetime.datetime.now())]
                             if visit.visit_number == 2 and subject.virus_test_2_updated is None and visit.virus_inconclusive:
-                                subject.virus_test_2_updated = datetime.datetime.now()
-                                subject.save()
+                                changes = [('virus_test_2_updated', datetime.datetime.now())]
                             if visit.visit_number == 3 and subject.virus_test_3_updated is None and visit.virus_inconclusive:
-                                subject.virus_test_3_updated = datetime.datetime.now()
-                                subject.save()
+                                changes = [('virus_test_3_updated', datetime.datetime.now())]
                             if visit.visit_number == 4 and subject.virus_test_4_updated is None and visit.virus_inconclusive:
-                                subject.virus_test_4_updated = datetime.datetime.now()
-                                subject.save()
+                                changes = [('virus_test_4_updated', datetime.datetime.now())]
                             if visit.visit_number == 5 and subject.virus_test_5_updated is None and visit.virus_inconclusive:
-                                subject.virus_test_5_updated = datetime.datetime.now()
+                                changes = [('virus_test_5_updated', datetime.datetime.now())]
+                            #
+                            if changes is not None:
+                                for field, new_value in changes:
+                                    old_value = getattr(subject, field)
+                                    description = u'{} changed from "{}" to "{}"'.format(field, old_value, new_value)
+                                    p = Provenance(modified_table=StudySubject._meta.db_table,
+                                                modified_table_id=subject.id,
+                                                modification_author=self.importer_user,
+                                                previous_value=old_value,
+                                                new_value=new_value,
+                                                modification_description=description,
+                                                modified_field=field)
+                                    setattr(subject, field, new_value)
+                                    p.save()
                                 subject.save()
 
         return result