Package: edc-visit-tracking
edc-visit-trackingTrack study participant visit reports.Declaring a visit modelAvisit_modelis declared using the model mixinVisitModelMixin. Normally, avisit_modelwill be declared with additional model mixins, butVisitModelMixinmust be there.classSubjectVisit(VisitModelMixin,BaseUuidModel):...Also, ensure theMetaclass attributes ofVisitModelMixinare inherited. These include required constraints and ordering.classSubjectVisit(VisitModelMixin,BaseUuidModel):...classMeta(VisitModelMixin.Meta):passAmong other features,VisitModelMixinadds aOneToOneFieldforeign key to thevisit_modelthat points toedc_appointment.Appointment.Important: Avisit modelis a special model in the EDC. A model declared with the model mixin,VisitModelMixin, is the definition of avisit model. CRFs and Requisitions have a foreign key pointing to avisit model. A number of methods on CRFs and Requisitions detect theirvisit modelforeign key name, model class and value by looking for the FK declared withVisitModelMixin.For a subject that requires ICF thevisit modelwould use theRequiresConsentModelMixin:classSubjectVisit(VisitModelMixin,RequiresConsentFieldsModelMixin,BaseUuidModel,):classMeta(VisitModelMixin.Meta,BaseUuidModel.Meta):passIf the subject does not require ICF, such as an infant, don’t include theRequiresConsentModelMixin:classInfantVisit(VisitModelMixin,BaseUuidModel):classMeta(VisitModelMixin.Meta,,BaseUuidModel.Meta):passA more complete declaration will include model mixins from other libraries. For example:fromedc_consent.model_mixinsimportRequiresConsentFieldsModelMixinfromedc_metadata.model_mixins.createsimportCreatesMetadataModelMixinfromedc_model.modelsimportBaseUuidModelfromedc_offstudy.model_mixinsimportOffstudyVisitModelMixinfromedc_sites.managersimportCurrentSiteManagerfromedc_sites.model_mixinsimportSiteModelMixinfromedc_visit_tracking.managersimportVisitModelManagerfromedc_visit_tracking.model_mixinsimportVisitModelMixinclassSubjectVisit(SiteModelMixin,VisitModelMixin,CreatesMetadataModelMixin,RequiresConsentFieldsModelMixin,OffstudyNonCrfModelMixin,BaseUuidModel,):objects=VisitModelManager()on_site=CurrentSiteManager()history=edc_models.HistoricalRecords()classMeta(VisitModelMixin.Meta,BaseUuidModel.Meta):passDeclaring a CRFTheCrfModelMixinis required for all CRF models. CRF models have aOneToOneFieldkey to avisit model.classCrfOne(CrfModelMixin,OffstudyCrfModelMixin,RequiresConsentModelMixin,UpdatesCrfMetadataModelMixin,BaseUuidModel):subject_visit=models.OneToOneField(SubjectVisit)f1=models.CharField(max_length=10,default='erik')vl=models.CharField(max_length=10,default=NO)rdb=models.CharField(max_length=10,default=NO)classMeta:consent_model='myapp.subjectconsent'# for RequiresConsentModelMixinDeclaring forms:TheVisitFormMixinincludes a number of common validations in thecleanmethod:classSubjectVisitForm(VisitFormMixin,FormValidatorMixin,forms.ModelForm):form_validator_cls=VisitFormValidatorclassMeta:model=SubjectVisitPreviousVisitModelMixinThePreviousVisitModelMixinensures that visits are entered in sequence. It is included with theVisitModelMixin.VisitTrackingModelFormMixinseeDEFAULT_REPORT_DATETIME_ALLOWANCEMissed Visit ReportA detail report should be submitted for scheduled visits that are missed.
By selecting the reasonmissed visitonSubjectVisit, only the missed visit CRF will be required
for the timepoint. All other CRFs and requisitions will be excluded.Unscheduled visits cannot be missed. (To change this behaviour seesettingsattrubuteEDC_VISIT_TRACKING_ALLOW_MISSED_UNSCHEDULED)The model mixinSubjectVisitMissedModelMixinprovides the basic features of aSubjectVisitMissedmodel.In your subject app declare:fromdjango.db.modelsimportPROTECTfromedc_crf.model_mixinsimportCrfWithActionModelMixinfromedc_modelimportmodelsasedc_modelsfromedc_visit_tracking.model_mixinsimportSubjectVisitMissedModelMixinclassSubjectVisitMissed(SubjectVisitMissedModelMixin,edc_models.BaseUuidModel):missed_reasons=models.ManyToManyField(SubjectVisitMissedReasons,blank=True,related_name="+")classMeta(CrfWithActionModelMixin.Meta,edc_models.BaseUuidModel.Meta):verbose_name="Missed Visit Report"verbose_name_plural="Missed Visit Report"In your list model app, e.g.meta_lists, declare the list model:classSubjectVisitMissedReasons(ListModelMixin):classMeta(ListModelMixin.Meta):verbose_name="Subject Missed Visit Reasons"verbose_name_plural="Subject Missed Visit Reasons"… and update thelist_datadictionary, for example:list_data={..."meta_lists.subjectvisitmissedreasons":[("forgot","Forgot / Can’t remember being told about appointment"),("family_emergency","Family emergency (e.g. funeral) and was away"),("travelling","Away travelling/visiting"),("working_schooling","Away working/schooling"),("too_sick","Too sick or weak to come to the centre"),("lack_of_transport","Transportation difficulty"),(OTHER,"Other reason (specify below)",),],...}Window periodBy default, the visitreport_datetimeis validated to stay within the same window period as the appointment.
This may be too restrictive in some cases.To bypass this override`validate_visit_datetime_in_window_period`in the`VisitFormValidator`fromedc_visit_tracking.form_validatorsimportVisitFormValidatorasBaseVisitFormValidatorclassVisitFormValidator(BaseVisitFormValidator):...defvalidate_visit_datetime_in_window_period():pass...Be sure that your appointment form validator is enforcing window periods before
bypassing this check.See alsoedc_appointment.