class OnSchedulePreFlourish(OnScheduleModelMixin, BaseUuidModel): """A model used by the system. Auto-completed by enrollment model. """ subject_identifier = models.CharField( verbose_name="Subject Identifier", max_length=50) schedule_name = models.CharField(max_length=25, blank=True, null=True) on_site = CurrentSiteManager() objects = SubjectIdentifierManager() history = HistoricalRecords() def put_on_schedule(self): pass def save(self, *args, **kwargs): self.consent_version = '1' super().save(*args, **kwargs) class Meta: unique_together = ('subject_identifier', 'schedule_name')
class SubjectVisit(VisitModelMixin, ReferenceModelMixin, CreatesMetadataModelMixin, SiteModelMixin, RequiresConsentFieldsModelMixin, BaseUuidModel): """A model completed by the user that captures the covering information for the data collected for this timepoint/appointment, e.g.report_datetime. """ appointment = models.OneToOneField(Appointment, on_delete=models.PROTECT) reason = models.CharField( verbose_name='What is the reason for this visit report?', max_length=25, choices=VISIT_REASON) reason_unscheduled = models.CharField( verbose_name=( 'If \'Unscheduled\' above, provide reason for ' 'the unscheduled visit'), max_length=25, choices=VISIT_UNSCHEDULED_REASON, default=NOT_APPLICABLE) info_source = models.CharField( verbose_name='What is the main source of this information?', max_length=25, choices=INFO_SOURCE) on_site = CurrentSiteManager() objects = VisitModelManager() history = HistoricalRecords() class Meta(VisitModelMixin.Meta): pass
class Appointment(AppointmentModelMixin, SiteModelMixin, BaseUuidModel): appt_datetime = models.DateTimeField( verbose_name=('Appointment date and time'), validators=[datetime_not_before_study_start], db_index=True) site = models.ForeignKey(Site, on_delete=models.PROTECT, null=True, editable=False, related_name='appoimtment_site') on_site = CurrentSiteManager() objects = AppointmentManager() history = HistoricalRecords() def natural_key(self): return (self.subject_identifier, self.visit_schedule_name, self.schedule_name, self.visit_code, self.visit_code_sequence) natural_key.dependencies = ['sites.Site'] class Meta(AppointmentModelMixin.Meta): pass
class OutgoingTransaction(TransactionModelMixin, SiteModelMixin, BaseUuidModel): """ Transactions produced locally to be consumed/sent to a queue or consumer. """ site = models.ForeignKey(Site, on_delete=models.CASCADE, null=True, editable=False) is_consumed_middleman = models.BooleanField(default=False) is_consumed_server = models.BooleanField(default=False) using = models.CharField(max_length=25, null=True) on_site = CurrentSiteManager() objects = models.Manager() def save(self, *args, **kwargs): if not self.using: raise ValueError('Value for \'{}.using\' cannot be None.'.format( self._meta.model_name)) if self.is_consumed_server and not self.consumed_datetime: self.consumed_datetime = get_utcnow() super().save(*args, **kwargs) class Meta: ordering = ['timestamp']
class InfantOffSchedule(ConsentVersionModelModelMixin, OffScheduleModelMixin, BaseUuidModel): schedule_name = models.CharField(max_length=25, blank=True, null=True) on_site = CurrentSiteManager() objects = SubjectIdentifierManager() history = HistoricalRecords() def take_off_schedule(self): pass def get_consent_version(self): subject_consent_cls = django_apps.get_model( 'td_infant.infantdummysubjectconsent') subject_consent_objs = subject_consent_cls.objects.filter( subject_identifier=self.subject_identifier).order_by( '-consent_datetime') if subject_consent_objs: return subject_consent_objs.first().version else: raise ValidationError( 'Missing Infant Dummy Consent form. Cannot proceed.') class Meta: unique_together = ('subject_identifier', 'schedule_name')
class CaregiverOffSchedule(OffScheduleModelMixin, BaseUuidModel): schedule_name = models.CharField(max_length=25, blank=True, null=True) objects = SubjectIdentifierManager() on_site = CurrentSiteManager() history = HistoricalRecords() def take_off_schedule(self): pass @property def latest_consent_obj_version(self): caregiver_consent_cls = django_apps.get_model( 'flourish_caregiver.subjectconsent') subject_consents = caregiver_consent_cls.objects.filter( subject_identifier=self.subject_identifier, ) if subject_consents: latest_consent = subject_consents.latest('consent_datetime') return latest_consent.version else: raise forms.ValidationError( 'Missing Subject Consent form, cannot proceed.') def save(self, *args, **kwargs): self.consent_version = self.latest_consent_obj_version super().save(*args, **kwargs) class Meta: unique_together = ('subject_identifier', 'schedule_name')
class OnSchedule(OnScheduleModelMixin, BaseUuidModel): """A model used by the system. Auto-completed by subject_consent. """ on_site = CurrentSiteManager() objects = SubjectIdentifierManager() history = HistoricalRecords()
class FollowUp(ClinicalAssessmentModelMixin, CrfModelMixin): fluconazole_dose = models.CharField( verbose_name='Fluconazole dose (day prior to visit)', max_length=25, choices=FLUCONAZOLE_DOSE) fluconazole_dose_other = OtherCharField( verbose_name='If other, specify dose:', max_length=25) rifampicin_started = models.CharField( verbose_name='Rifampicin started since last visit?', max_length=25, choices=YES_NO_ALREADY_ND) rifampicin_start_date = models.DateField( verbose_name='Date Rifampicin started', validators=[date_not_future], null=True, blank=True, ) patient_help = models.CharField( verbose_name=('Does the patient require help from' ' anybody for everyday activities? '), max_length=10, choices=YES_NO_ND, help_text=('For example eating, drinking, washing,' ' brushing teeth, going to the toilet')) patient_problems = models.CharField( verbose_name= 'Has the illness left the patient with any other problems?', max_length=10, choices=YES_NO_ND) rankin_score = models.CharField(verbose_name='Modified Rankin score', choices=RANKIN_SCORE, max_length=10, null=True) other_significant_dx = models.CharField( verbose_name='Other significant diagnosis since last visit?', max_length=5, choices=YES_NO_NA) on_site = CurrentSiteManager() objects = CrfModelManager() history = HistoricalRecords() class Meta(CrfModelMixin.Meta): verbose_name = 'Follow-up' verbose_name_plural = 'Follow-up'
class EducationHoh(EducationModelMixin, CrfModelMixin): on_site = CurrentSiteManager() objects = CrfModelManager() history = HistoricalRecords() class Meta(CrfModelMixin.Meta): verbose_name = 'Health Economics: Education (Person who earns the highest income)' verbose_name_plural = 'Health Economics: Education (Person who earns the highest income)'
class MedicalExpensesTwo(CrfModelMixin): on_site = CurrentSiteManager() objects = CrfModelManager() history = HistoricalRecords() class Meta(CrfModelMixin.Meta): verbose_name = 'Health Economics: Medical Expenses Part 2' verbose_name_plural = 'Health Economics: Medical Expenses Part 2'
class ChildOffSchedule(OffScheduleModelMixin, BaseUuidModel): schedule_name = models.CharField(max_length=25, blank=True, null=True) objects = SubjectIdentifierManager() on_site = CurrentSiteManager() history = HistoricalRecords() def take_off_schedule(self): pass def get_consent_version(self): preg_subject_screening_cls = django_apps.get_model( 'flourish_caregiver.screeningpregwomen') prior_subject_screening_cls = django_apps.get_model( 'flourish_caregiver.screeningpriorbhpparticipants') consent_version_cls = django_apps.get_model( 'flourish_caregiver.flourishconsentversion') subject_screening_obj = None try: subject_screening_obj = preg_subject_screening_cls.objects.get( subject_identifier=self.subject_identifier[:-3]) except preg_subject_screening_cls.DoesNotExist: try: subject_screening_obj = prior_subject_screening_cls.objects.get( subject_identifier=self.subject_identifier[:-3]) except prior_subject_screening_cls.DoesNotExist: raise ValidationError( 'Missing Subject Screening form. Please complete ' 'it before proceeding.') if subject_screening_obj: try: consent_version_obj = consent_version_cls.objects.get( screening_identifier=subject_screening_obj. screening_identifier) except consent_version_cls.DoesNotExist: raise ValidationError( 'Missing Consent Version form. Please complete ' 'it before proceeding.') return consent_version_obj.version def save(self, *args, **kwargs): self.consent_version = self.get_consent_version() super().save(*args, **kwargs) class Meta: unique_together = ('subject_identifier', 'schedule_name')
class OnScheduleModelMixin(BaseOnScheduleModelMixin, BaseUuidModel): """A model used by the system. Auto-completed by enrollment model. """ subject_identifier = models.CharField( verbose_name="Subject Identifier", max_length=50) schedule_name = models.CharField(max_length=25, blank=True, null=True) on_site = CurrentSiteManager() objects = SubjectIdentifierManager() history = HistoricalRecords() def put_on_schedule(self): pass def save(self, *args, **kwargs): self.consent_version = self.latest_consent_obj_version super().save(*args, **kwargs) @property def consent_version_cls(self): return django_apps.get_model('flourish_caregiver.flourishconsentversion') @property def subject_consent_cls(self): return django_apps.get_model('flourish_caregiver.subjectconsent') @property def latest_consent_obj_version(self): child_consent_cls = django_apps.get_model( 'flourish_child.childdummysubjectconsent') subject_consents = child_consent_cls.objects.filter( subject_identifier=self.subject_identifier,) if subject_consents: latest_consent = subject_consents.latest('consent_datetime') return latest_consent.version else: raise forms.ValidationError('Missing dummy consent obj, cannot proceed.') class Meta: unique_together = ('subject_identifier', 'schedule_name') abstract = True
class CaregiverOffSchedule(OffScheduleModelMixin, BaseUuidModel): schedule_name = models.CharField(max_length=25, blank=True, null=True) objects = SubjectIdentifierManager() on_site = CurrentSiteManager() history = HistoricalRecords() def take_off_schedule(self): pass class Meta: unique_together = ('subject_identifier', 'schedule_name')
class SubjectRequisition( NonUniqueSubjectIdentifierFieldMixin, RequisitionModelMixin, RequisitionStatusMixin, RequisitionIdentifierMixin, VisitTrackingCrfModelMixin, SubjectScheduleCrfModelMixin, RequiresConsentFieldsModelMixin, PreviousVisitModelMixin, RequisitionReferenceModelMixin, UpdatesRequisitionMetadataModelMixin, SearchSlugModelMixin, BaseUuidModel): lab_profile_name = 'ambition_subject' subject_visit = models.ForeignKey(SubjectVisit, on_delete=PROTECT) reason_not_drawn = models.CharField( verbose_name='If not drawn, please explain', max_length=25, default=NOT_APPLICABLE, choices=REASON_NOT_DRAWN) on_site = CurrentSiteManager() objects = Manager() history = HistoricalRecords() def __str__(self): return (f'{self.requisition_identifier} ' f'{self.panel_object.verbose_name}') def save(self, *args, **kwargs): if not self.id: edc_protocol_app_config = django_apps.get_app_config( 'edc_protocol') self.protocol_number = edc_protocol_app_config.protocol_number self.subject_identifier = self.subject_visit.subject_identifier super().save(*args, **kwargs) def get_search_slug_fields(self): fields = super().get_search_slug_fields() fields.extend([ 'requisition_identifier', 'human_readable_identifier', 'identifier_prefix' ]) return fields class Meta: unique_together = ('panel', 'subject_visit')
class Week16(CrfModelMixin): patient_alive = models.CharField(verbose_name='Is the patient alive?', max_length=5, choices=YES_NO) death_datetime = models.DateTimeField( verbose_name='If dead, date and time of death', validators=[date_not_future], null=True, blank=True) activities_help = models.CharField( verbose_name= ('Does the patient require help from anybody for everyday activities?' ), max_length=5, choices=YES_NO_NA, help_text=('For example eating, drinking, washing, brushing teeth, ' 'going to the toilet.')) illness_problems = models.CharField( verbose_name= 'Has the illness left the patient with any other problems?', max_length=5, choices=YES_NO_NA) rankin_score = models.CharField(verbose_name='Modified Rankin score', max_length=10, choices=RANKIN_SCORE) week16_narrative = models.TextField(verbose_name='Narrative', max_length=1000, null=True, blank=True) on_site = CurrentSiteManager() objects = CrfModelManager() history = HistoricalRecords() class Meta(CrfModelMixin.Meta): verbose_name_plural = 'Week16'
class Education(EducationModelMixin, CrfModelMixin): household_head = models.CharField( verbose_name='Are you the person who earns the highest income?', max_length=5, choices=YES_NO, help_text=('If NO, please complete the form "Health Economics: ' 'Education (Person who earns the highest income)" on behalf of the ' 'Person who earns the highest income.')) on_site = CurrentSiteManager() objects = CrfModelManager() history = HistoricalRecords() class Meta(CrfModelMixin.Meta): verbose_name = 'Health Economics: Education' verbose_name_plural = 'Health Economics: Education'
class StudyTerminationConclusionW10(OffScheduleModelMixin, ActionItemModelMixin, TrackingIdentifierModelMixin, BaseUuidModel): action_cls = StudyTerminationConclusionW10Action tracking_identifier_prefix = 'ST' last_study_fu_date = models.DateField( verbose_name='Date of last research follow up (if different):', validators=[date_not_future], blank=True, null=True) termination_reason = models.CharField( verbose_name='Reason for study termination', max_length=75, choices=REASON_STUDY_TERMINATED_W10, help_text=( 'If included in error, be sure to fill in protocol deviation form.' )) death_date = models.DateField(verbose_name='Date of Death', validators=[date_not_future], blank=True, null=True) consent_withdrawal_reason = models.CharField( verbose_name='Reason for withdrawing consent', max_length=75, blank=True, null=True) on_site = CurrentSiteManager() objects = SubjectIdentifierManager() history = HistoricalRecords() class Meta: verbose_name = 'W10 Study Termination/Conclusion' verbose_name_plural = 'W10 Study Terminations/Conclusions'
class IncomingTransaction(TransactionModelMixin, SiteModelMixin, BaseUuidModel): """ Transactions received from a remote host. """ site = models.ForeignKey(Site, on_delete=models.CASCADE, null=True, editable=False) is_consumed = models.BooleanField(default=False) is_self = models.BooleanField(default=False) on_site = CurrentSiteManager() objects = models.Manager() class Meta: ordering = ['timestamp']
class OnScheduleMaternalLabourDel(ConsentVersionModelModelMixin, OnScheduleModelMixin, BaseUuidModel): """A model used by the system. Auto-completed by subject_consent. """ subject_identifier = models.CharField(verbose_name="Subject Identifier", max_length=50) schedule_name = models.CharField(max_length=25, blank=True, null=True) on_site = CurrentSiteManager() objects = SubjectIdentifierManager() history = HistoricalRecords() def put_on_schedule(self): pass class Meta: unique_together = ('subject_identifier', 'schedule_name')
class Week4(ClinicalAssessmentModelMixin, CrfModelMixin): fluconazole_dose = models.CharField( verbose_name='Fluconazole dose (day prior to visit)', max_length=25, choices=FLUCONAZOLE_DOSE) fluconazole_dose_other = OtherCharField(max_length=25) rifampicin_started = models.CharField( verbose_name='Rifampicin started since last visit?', max_length=25, choices=YES_NO_ALREADY_ND) rifampicin_start_date = models.DateField( verbose_name='Date Rifampicin started', validators=[date_not_future], null=True, blank=True) lp_done = models.CharField(verbose_name='LP done?', max_length=5, choices=YES_NO, help_text='If yes, ensure LP CRF completed.') other_significant_dx = models.CharField( verbose_name='Other significant diagnosis since last visit?', max_length=5, choices=YES_NO_NA) on_site = CurrentSiteManager() objects = CrfModelManager() history = HistoricalRecords() class Meta(CrfModelMixin.Meta): verbose_name = 'Week 4' verbose_name_plural = 'Week 4'
class Appointment(AppointmentModelMixin, SiteModelMixin, BaseUuidModel): site = models.ForeignKey(Site, on_delete=models.PROTECT, null=True, editable=False, related_name='appoimtment_site') on_site = CurrentSiteManager() objects = AppointmentManager() history = HistoricalRecords() def natural_key(self): return (self.subject_identifier, self.visit_schedule_name, self.schedule_name, self.visit_code, self.visit_code_sequence) natural_key.dependencies = ['sites.Site'] class Meta(AppointmentModelMixin.Meta): pass
class SubjectLocator(LocatorModelMixin, RequiresConsentFieldsModelMixin, ActionItemModelMixin, SiteModelMixin, TrackingIdentifierModelMixin, BaseUuidModel): """A model completed by the user to that captures participant locator information and permission to contact. """ action_cls = SubjectLocatorAction tracking_identifier_prefix = 'SL' on_site = CurrentSiteManager() objects = LocatorManager() history = HistoricalRecords() def natural_key(self): return (self.subject_identifier, ) natural_key.dependencies = ['sites.Site'] class Meta: verbose_name = 'Subject Locator'
class OnScheduleInfantBirth(OnScheduleModelMixin, BaseUuidModel): """A model used by the system. Auto-completed by infant birth. """ subject_identifier = models.CharField(verbose_name="Subject Identifier", max_length=50) schedule_name = models.CharField(max_length=25, blank=True, null=True) on_site = CurrentSiteManager() objects = SubjectIdentifierManager() history = HistoricalRecords() def put_on_schedule(self): pass def save(self, *args, **kwargs): self.consent_version = self.get_consent_version() super(OnScheduleInfantBirth, self).save(*args, **kwargs) def get_consent_version(self): subject_consent_cls = django_apps.get_model( 'td_infant.infantdummysubjectconsent') subject_consent_objs = subject_consent_cls.objects.filter( subject_identifier=self.subject_identifier).order_by( '-consent_datetime') if subject_consent_objs: return subject_consent_objs.first().version else: raise ValidationError( 'Missing Infant Dummy Consent form. Cannot proceed.') class Meta: unique_together = ('subject_identifier', 'schedule_name')
class RandomizationList(BaseUuidModel): subject_identifier = models.CharField( verbose_name="Subject Identifier", max_length=50, null=True, unique=True) sid = models.IntegerField(unique=True) drug_assignment = EncryptedTextField( choices=( (SINGLE_DOSE, SINGLE_DOSE_NAME), (CONTROL, CONTROL_NAME))) site_name = models.CharField(max_length=100) allocation = EncryptedTextField( verbose_name='Original integer allocation', null=True) allocated = models.BooleanField(default=False) allocated_datetime = models.DateTimeField(null=True) allocated_user = models.CharField(max_length=50, null=True) allocated_site = models.ForeignKey( Site, null=True, on_delete=models.CASCADE) verified = models.BooleanField(default=False) verified_datetime = models.DateTimeField(null=True) verified_user = models.CharField(max_length=50, null=True) objects = RandomizationListManager() history = HistoricalRecords() on_site = CurrentSiteManager('allocated_site') def __str__(self): return f'{self.site_name}.{self.sid} subject={self.subject_identifier}' def save(self, *args, **kwargs): try: self.treatment_description except RandomizationError as e: raise RandomizationListModelError(e) try: Site.objects.get(name=self.site_name) except ObjectDoesNotExist: site_names = [obj.name for obj in Site.objects.all()] raise RandomizationListModelError( f'Invalid site name. Got {self.site_name}. ' f'Expected one of {site_names}.') super().save(*args, **kwargs) @property def short_label(self): return (f'{self.drug_assignment} SID:{self.site_name}.{self.sid}') @property def treatment_description(self): if self.drug_assignment == CONTROL: return CONTROL_NAME elif self.drug_assignment == SINGLE_DOSE: return SINGLE_DOSE_NAME raise RandomizationError( f'Invalid drug assignment. Got {self.drug_assignment}') def natural_key(self): return (self.sid, ) class Meta: ordering = ('site_name', 'sid', ) unique_together = ('site_name', 'sid')
class RecurrenceSymptom(NonUniqueSubjectIdentifierFieldMixin, ActionItemModelMixin, TrackingIdentifierModelMixin, SiteModelMixin, BaseUuidModel): tracking_identifier_prefix = 'RS' action_cls = RecurrenceOfSymptomsAction report_datetime = models.DateTimeField( verbose_name="Report Date and Time", default=get_utcnow) meningitis_symptom = models.ManyToManyField( MeningitisSymptom, verbose_name='What are your current symptoms?') meningitis_symptom_other = models.CharField( verbose_name='If other symptom, please specify', max_length=50, null=True, blank=True) patient_readmitted = models.CharField( verbose_name=( 'Has the patient been re-admitted due to these recurrent symptoms?'), max_length=5, choices=YES_NO) glasgow_coma_score = models.IntegerField( verbose_name='Score:', validators=[MinValueValidator(3), MaxValueValidator(15)], help_text='/15') recent_seizure = models.CharField( verbose_name=( 'Recent seizure (<72 hrs):'), max_length=5, choices=YES_NO) behaviour_change = models.CharField( max_length=5, choices=YES_NO) confusion = models.CharField( max_length=5, choices=YES_NO) neurological = models.ManyToManyField( Neurological, verbose_name='neurologic:') focal_neurologic_deficit = models.CharField( verbose_name='If "Focal neurologic deficit" chosen, please specify', max_length=15, null=True, blank=True) cn_palsy_chosen_other = models.CharField( verbose_name='If other CN Palsy', max_length=15, null=True, blank=True) lp_completed = models.CharField( verbose_name='LP completed', max_length=5, choices=YES_NO, help_text='If YES, complete LP form') amb_administered = models.CharField( max_length=5, choices=YES_NO) amb_duration = models.IntegerField( verbose_name='If YES, specify length of course', validators=[MinValueValidator(1)], null=True, blank=True) tb_treatment = models.CharField( verbose_name='TB treatment:', max_length=5, choices=YES_NO) steroids_administered = models.CharField( max_length=5, choices=YES_NO) steroids_duration = models.IntegerField( verbose_name='If YES, specify the length of course in days:', validators=[MinValueValidator(1)], null=True, blank=True) steroids_choices = models.CharField( verbose_name='If YES', max_length=25, default=NOT_APPLICABLE, choices=STEROIDS_CHOICES) steroids_choices_other = models.CharField( blank=True, max_length=50, verbose_name='If other steroids, please specify') CD4_count = models.IntegerField( verbose_name='CD4 count (if available)', validators=[MinValueValidator(1)], null=True, blank=True) antibiotic_treatment = models.ManyToManyField( AntibioticTreatment, verbose_name='Antibiotics treatment') antibiotic_treatment_other = models.CharField( verbose_name='If other antibiotic treatment, please specify', max_length=50, blank=True, null=True) on_arvs = models.CharField( verbose_name='On ARVs:', max_length=26, choices=YES_NO_ALREADY_ARV) arv_date = models.DateField( verbose_name='Study date ARVs started.', validators=[date_not_future], null=True, blank=True) arvs_stopped = models.CharField( verbose_name='ARVs stopped this clinical episode?', max_length=5, choices=YES_NO_NA) narrative_summary = models.TextField( verbose_name='Narrative summary of recurrence of symptoms:', help_text=( 'Please ensure the following forms have been completed: ' 'LP, Bloods, Microbiology, Radiology')) dr_opinion = models.CharField( verbose_name='Study doctor\'s opinion:', max_length=10, choices=DR_OPINION) dr_opinion_other = models.CharField( verbose_name='If other doctor\'s opinion, please specify', blank=True, max_length=50, null=True) on_site = CurrentSiteManager() objects = TrackingIdentifierManager() history = HistoricalRecords() def natural_key(self): return (self.tracking_identifier, ) class Meta: verbose_name = 'Recurrence of Symptoms' verbose_name_plural = 'Recurrence of Symptoms'
class ProtocolDeviationViolation(NonUniqueSubjectIdentifierFieldMixin, SiteModelMixin, ActionItemModelMixin, TrackingIdentifierModelMixin, BaseUuidModel): tracking_identifier_prefix = 'PD' action_cls = ProtocolDeviationViolationAction report_datetime = models.DateTimeField(verbose_name="Report Date and Time", default=get_utcnow) short_description = models.CharField( verbose_name='Provide a short description of this occurrence', max_length=35, null=True, blank=False, help_text=( 'Max 35 characters. Note: If this occurrence is a "violation" ' 'there is additional space below for a more detailed ' 'description')) report_type = models.CharField(verbose_name='Type of occurrence', max_length=25, choices=DEVIATION_VIOLATION) safety_impact = models.CharField( verbose_name='Could this occurrence have an impact on safety of the ' 'participant?', max_length=25, choices=YES_NO) safety_impact_details = models.TextField( verbose_name='If "Yes", provide details', null=True, blank=True) study_outcomes_impact = models.CharField( verbose_name='Could this occurrence have an impact on study outcomes?', max_length=25, choices=YES_NO) study_outcomes_impact_details = models.TextField( verbose_name='If "Yes", provide details', null=True, blank=True) violation_datetime = models.DateTimeField( verbose_name='Date violation occurred', validators=[datetime_not_future], null=True, blank=True) violation_type = models.CharField(verbose_name='Type of violation', max_length=75, choices=PROTOCOL_VIOLATION, default=NOT_APPLICABLE) violation_type_other = models.CharField( null=True, blank=True, verbose_name='If other, please specify', max_length=75) violation_description = models.TextField( verbose_name='Describe the violation', null=True, blank=True, help_text=('Describe in full. Explain how the violation ' 'happened, what occurred, etc.')) violation_reason = models.TextField( verbose_name='Explain the reason why the violation occurred', null=True, blank=True) corrective_action_datetime = models.DateTimeField( verbose_name='Corrective action date and time', validators=[datetime_not_future], null=True, blank=True) corrective_action = models.TextField( verbose_name='Corrective action taken', null=True, blank=True) preventative_action_datetime = models.DateTimeField( verbose_name='Preventative action date and time', validators=[datetime_not_future], null=True, blank=True) preventative_action = models.TextField( verbose_name='Preventative action taken', null=True, blank=True) action_required = models.CharField(max_length=45, choices=ACTION_REQUIRED) report_status = models.CharField( verbose_name='What is the status of this report?', max_length=25, choices=REPORT_STATUS) report_closed_datetime = models.DateTimeField( blank=True, null=True, validators=[datetime_not_future], verbose_name=('Date and time report closed.')) on_site = CurrentSiteManager() objects = TrackingIdentifierManager() history = HistoricalRecords() def natural_key(self): return (self.tracking_identifier, ) natural_key.dependencies = ['sites.Site'] class Meta: verbose_name = 'Protocol Deviation/Violation' verbose_name_plural = 'Protocol Deviations/Violations'
class ActionItem(NonUniqueSubjectIdentifierFieldMixin, SiteModelMixin, BaseUuidModel): subject_identifier_model = 'edc_registration.registeredsubject' action_identifier = models.CharField(max_length=25, unique=True) report_datetime = models.DateTimeField(default=get_utcnow) action_type = models.ForeignKey(ActionType, on_delete=PROTECT, related_name='action_type', verbose_name='Action') reference_model = models.CharField(max_length=50, null=True) reference_identifier = models.CharField( max_length=50, null=True, help_text='e.g. tracking identifier updated from the reference model') related_reference_model = models.CharField(max_length=100, null=True, editable=False) related_reference_identifier = models.CharField( max_length=50, null=True, blank=True, help_text=('May be left blank. e.g. tracking identifier from ' 'source model that opened the item.')) parent_reference_model = models.CharField(max_length=100, null=True, editable=False) parent_reference_identifier = models.CharField( max_length=50, null=True, blank=True, help_text=('May be left blank. e.g. tracking identifier from ' 'source model that opened the item.')) priority = models.CharField( max_length=25, choices=PRIORITY, null=True, blank=True, help_text='Leave blank to use default for this action type.') parent_action_item = models.ForeignKey('self', on_delete=PROTECT, null=True, blank=True) status = models.CharField(max_length=25, default=NEW, choices=ACTION_STATUS) instructions = models.TextField(null=True, blank=True, help_text='populated by action class') auto_created = models.BooleanField(default=False) auto_created_comment = models.CharField(max_length=25, null=True, blank=True) on_site = CurrentSiteManager() objects = ActionItemManager() history = HistoricalRecords() def __str__(self): return (f'{self.action_identifier[-9:]} {self.action_type.name} ' f'({self.get_status_display()})') def save(self, *args, **kwargs): """See also signals.""" if not self.id: # a new action item always has a unique action identifier self.action_identifier = ActionIdentifier().identifier self.check_registered_subject() self.priority = self.priority or self.action_type.priority self.reference_model = self.action_type.reference_model self.related_reference_model = self.action_type.related_reference_model self.instructions = self.action_type.instructions super().save(*args, **kwargs) def check_registered_subject(self): # subject_identifier if self.subject_identifier: subject_identifier_model_cls = django_apps.get_model( self.subject_identifier_model) try: subject_identifier_model_cls.objects.get( subject_identifier=self.subject_identifier) except ObjectDoesNotExist: raise SubjectDoesNotExist( f'Invalid subject identifier. Subject does not exist ' f'in \'{self.subject_identifier_model}\'. ' f'Got \'{self.subject_identifier}\'.') def natural_key(self): return (self.action_identifier, ) natural_key.dependencies = ['sites.Site'] @property def last_updated(self): return None if self.status == NEW else self.modified @property def user_last_updated(self): return None if self.status == NEW else self.user_modified or self.user_created @property def action_cls(self): """Returns the action_cls. """ return site_action_items.get(self.action_type.name) @property def parent_reference_model_obj(self): """Returns the parent reference model instance or None. """ try: reference_model = self.parent_action_item.reference_model except AttributeError: pass else: return django_apps.get_model(reference_model).objects.get( tracking_identifier=self.parent_action_item. reference_identifier) return None @property def related_reference_model_obj(self): """Returns the related reference model instance or None. """ try: reference_model = self.related_reference_model except AttributeError: pass else: return django_apps.get_model(reference_model).objects.get( tracking_identifier=self.related_reference_identifier) return None @property def identifier(self): """Returns a shortened action identifier. """ return self.action_identifier[-9:] @property def parent(self): """Returns a url to the parent action item for display in admin. """ if self.parent_action_item: url_name = '_'.join(self._meta.label_lower.split('.')) namespace = edc_action_item_admin.name url = reverse(f'{namespace}:{url_name}_changelist') return mark_safe( f'<a data-toggle="tooltip" title="go to parent action item" ' f'href="{url}?q={self.parent_action_item.action_identifier}">' f'{self.parent_action_item.identifier}</a>') return None @property def reference(self): """Returns a shortened reference_identifier which in most cases is the reference model's tracking identifier. """ if self.reference_identifier: return self.reference_identifier[-9:] return None @property def parent_reference(self): """Returns a shortened reference_identifier of the parent model reference which in most cases is the parent reference model's tracking identifier. """ try: reference_identifier = self.parent_action_item.reference_identifier except AttributeError: reference_identifier = None if reference_identifier: return reference_identifier[-9:] return None @property def related_reference(self): """Returns a shortened reference_identifier of the parent model reference which in most cases is the parent reference model's tracking identifier. """ try: reference_identifier = self._related_reference_identifier except AttributeError: reference_identifier = None if reference_identifier: return reference_identifier[-9:] return None class Meta: verbose_name = 'Action Item' verbose_name_plural = 'Action Items' unique_together = ('subject_identifier', 'action_type', 'reference_identifier')
class Microbiology(CrfModelMixin): urine_culture_performed = models.CharField( max_length=5, choices=YES_NO, help_text='only for patients with >50 white cells in urine') urine_taken_date = models.DateField( validators=[date_not_before_study_start, date_not_future], null=True, blank=True) urine_culture_results = models.CharField( verbose_name='Urine culture results, if completed', max_length=10, choices=CULTURE_RESULTS, default=NOT_APPLICABLE) urine_culture_organism = models.CharField( verbose_name='If positive, organism', max_length=25, choices=URINE_CULTURE_RESULTS_ORGANISM, default=NOT_APPLICABLE) urine_culture_organism_other = OtherCharField( max_length=50, null=True, blank=True) blood_culture_performed = models.CharField( max_length=5, choices=YES_NO) blood_culture_results = models.CharField( verbose_name='Blood culture results, if completed', max_length=10, choices=CULTURE_RESULTS, default=NOT_APPLICABLE) blood_taken_date = models.DateField( validators=[date_not_before_study_start, date_not_future], null=True, blank=True) day_blood_taken = models.IntegerField( verbose_name='If positive, study day positive culture sample taken', validators=[MinValueValidator(1)], null=True, blank=True) blood_culture_organism = models.CharField( verbose_name='If growth positive, organism', max_length=50, choices=BLOOD_CULTURE_RESULTS_ORGANISM, default=NOT_APPLICABLE) blood_culture_organism_other = OtherCharField( max_length=50, null=True, blank=True) bacteria_identified = models.CharField( verbose_name='If bacteria, type', max_length=50, choices=BACTERIA_TYPE, default=NOT_APPLICABLE) bacteria_identified_other = OtherCharField( max_length=100, null=True, blank=True) sputum_afb_performed = models.CharField( verbose_name='AFB microscopy performed?', max_length=5, choices=YES_NO, help_text='Was sputum AFB done?') sputum_afb_date = models.DateField( validators=[date_not_before_study_start, date_not_future], null=True, blank=True) sputum_results_afb = models.CharField( verbose_name='AFB results', max_length=10, choices=POS_NEG_NA, default=NOT_APPLICABLE) sputum_performed = models.CharField( verbose_name='Culture performed?', max_length=15, choices=YES_NO_NA, default=NOT_APPLICABLE) sputum_taken_date = models.DateField( validators=[date_not_before_study_start, date_not_future], null=True, blank=True) sputum_results_culture = models.CharField( verbose_name='Culture results', max_length=10, choices=POS_NEG_NA, default=NOT_APPLICABLE) sputum_results_positive = models.CharField( verbose_name='If culture is positive, please specify:', max_length=50, null=True, blank=True) sputum_genexpert_performed = models.CharField( verbose_name='Sputum Gene-Xpert performed?', max_length=15, choices=YES_NO_NA, default=NOT_APPLICABLE) sputum_genexpert_date = models.DateField( verbose_name='Date sputum Gene-Xpert taken', validators=[date_not_before_study_start, date_not_future], null=True, blank=True) sputum_result_genexpert = models.CharField( verbose_name='Gene-Xpert results', max_length=45, choices=SPUTUM_GENEXPERT, default=NOT_APPLICABLE) tissue_biopsy_taken = models.CharField( max_length=5, choices=YES_NO) tissue_biopsy_results = models.CharField( verbose_name='If YES, results', max_length=10, choices=CULTURE_RESULTS, default=NOT_APPLICABLE) biopsy_date = models.DateField( validators=[date_not_before_study_start, date_not_future], null=True, blank=True) day_biopsy_taken = models.IntegerField( verbose_name='If positive, Study day positive culture sample taken', validators=[MinValueValidator(1)], null=True, blank=True) tissue_biopsy_organism = models.CharField( verbose_name='If growth positive, organism', max_length=50, choices=BIOPSY_RESULTS_ORGANISM, default=NOT_APPLICABLE) tissue_biopsy_organism_other = OtherCharField( max_length=50, null=True, blank=True) histopathology_report = models.TextField( null=True, blank=True) on_site = CurrentSiteManager() objects = CrfModelManager() history = HistoricalRecords() class Meta(CrfModelMixin.Meta): verbose_name = 'Microbiology' verbose_name_plural = 'Microbiology'
class PkPdCrf(CrfModelMixin): albumin = models.IntegerField(verbose_name='Albumin', null=True, blank=True, help_text='Units in g/L') ambisome_dose = models.IntegerField(verbose_name='Ambisome dose given', null=True, blank=True, help_text='Units in mg') ambisome_started_datetime = models.DateTimeField( verbose_name='Date and time Ambisome infusion started?', null=True, blank=True) ambisome_ended_datetime = models.DateTimeField( verbose_name='Date and time Ambisome infusion stopped', null=True, blank=True) full_ambisome_dose_given = models.CharField( verbose_name='Was the entire Ambisome dose given?', choices=YES_NO, max_length=5, null=True, blank=True) flucytosine_dose = models.IntegerField(verbose_name='Flucytosine dose?', null=True, blank=True, help_text='Units in mg') flucytosine_dose_one_given = models.CharField( verbose_name='Flucytosine <u>DOSE 1</u> given? ', choices=YES_NO, max_length=5, null=True, blank=True) flucytosine_dose_one_datetime = models.DateTimeField( verbose_name=mark_safe( 'Date and time Flucytosine <u>DOSE 1</u> was swallowed?'), null=True, blank=True) flucytosine_dose_two_given = models.CharField( verbose_name='Flucytosine <u>DOSE 2</u> given? ', choices=YES_NO, max_length=5, null=True, blank=True) flucytosine_dose_two_datetime = models.DateTimeField( verbose_name=mark_safe( 'Date and time Flucytosine <u>DOSE 2</u> was swallowed?'), null=True, blank=True) flucytosine_dose_three_given = models.CharField( verbose_name='Flucytosine <u>DOSE 3</u> given? ', choices=YES_NO, max_length=5, null=True, blank=True) flucytosine_dose_three_datetime = models.DateTimeField( verbose_name=mark_safe( 'Date and time Flucytosine <u>DOSE 3</u> was swallowed?'), null=True, blank=True) flucytosine_dose_four_given = models.CharField( verbose_name='Flucytosine <u>DOSE 4</u> given? ', choices=YES_NO, max_length=5, null=True, blank=True) flucytosine_dose_four_datetime = models.DateTimeField( verbose_name=mark_safe( 'Date and time Flucytosine <u>DOSE 4</u> was swallowed?'), null=True, blank=True) flucytosine_dose_reason_missed = models.TextField( verbose_name='If any Flucytosine doses not given, provide reason', max_length=75, null=True, blank=True) fluconazole_dose = models.IntegerField(verbose_name='Fluconazole dose?', null=True, blank=True, help_text='Units in mg') fluconazole_dose_given = models.CharField( verbose_name='Was the Fluconazole dose given?', choices=YES_NO, max_length=5, null=True, blank=True) fluconazole_dose_datetime = models.DateTimeField( verbose_name='Date and time Fluconazole was swallowed?', null=True, blank=True) fluconazole_dose_reason_missed = models.TextField( verbose_name='If Fluconazole dose not given, provide reason', max_length=75, null=True, blank=True) blood_sample_one_datetime = models.DateTimeField(verbose_name=mark_safe( 'Date and time blood <u>SAMPLE 1</u> taken?'), null=True, blank=True) blood_sample_two_datetime = models.DateTimeField(verbose_name=mark_safe( 'Date and time blood <u>SAMPLE 2</u> taken?'), null=True, blank=True) blood_sample_three_datetime = models.DateTimeField(verbose_name=mark_safe( 'Date and time blood <u>SAMPLE 3</u> taken?'), null=True, blank=True) blood_sample_four_datetime = models.DateTimeField(verbose_name=mark_safe( 'Date and time blood <u>SAMPLE 4</u> taken?'), null=True, blank=True) blood_sample_five_datetime = models.DateTimeField(verbose_name=mark_safe( 'Date and time blood <u>SAMPLE 5</u> taken?'), null=True, blank=True) blood_sample_six_datetime = models.DateTimeField(verbose_name=mark_safe( 'Date and time blood <u>SAMPLE 6</u> taken?'), null=True, blank=True) blood_sample_missed = models.CharField( verbose_name='Were any blood samples missed?', choices=YES_NO, max_length=5, null=True, blank=True) blood_sample_reason_missed = models.TextField( verbose_name='If any blood samples missed, provide reason', max_length=75, null=True, blank=True) pre_dose_lp = models.CharField(verbose_name='Is this a pre-dose LP?', choices=YES_NO, max_length=5, null=True, blank=True) post_dose_lp = models.CharField(verbose_name='Is this a post-dose LP?', choices=YES_NO, max_length=5, null=True, blank=True) time_csf_sample_taken = models.DateTimeField( verbose_name='What date and time was the CSF sample taken?', null=True, blank=True) on_site = CurrentSiteManager() objects = CrfModelManager() history = HistoricalRecords() class Meta(CrfModelMixin.Meta): verbose_name = 'PK/PD' verbose_name_plural = 'PK/PD'
class Week2(ClinicalAssessmentModelMixin, CrfModelMixin): discharged = models.CharField(verbose_name='Discharged?', max_length=25, choices=YES_NO) discharge_date = models.DateField(validators=[date_not_future], null=True, blank=True) died = models.CharField(verbose_name='Died?', max_length=25, choices=YES_NO) death_date_time = models.DateTimeField(validators=[datetime_not_future], null=True, blank=True) ampho_start_date = models.DateField( verbose_name='Amphotericin B start date: ', validators=[date_not_future], null=True, blank=True) ampho_end_date = models.DateField(verbose_name='Amphotericin B end date: ', validators=[date_not_future], null=True, blank=True) ampho_duration = models.IntegerField( verbose_name='Amphotericin B treatment duration', null=True, blank=True) flucon_start_date = models.DateField( verbose_name='Fluconazole start date:', validators=[date_not_future], null=True, blank=True) flucon_stop_date = models.DateField(verbose_name='Fluconazole end date:', validators=[date_not_future], null=True, blank=True) flucon_duration = models.IntegerField( verbose_name='Fluconazole treatment duration:', null=True, blank=True) flucy_start_date = models.DateField(verbose_name='Flucytosine start date:', validators=[date_not_future], null=True, blank=True) flucy_stop_date = models.DateField(verbose_name='Flucytosine end date:', validators=[date_not_future], null=True, blank=True) flucy_duration = models.IntegerField( verbose_name='Flucytosine treatment duration:', null=True, blank=True) ambi_start_date = models.DateField(verbose_name='Ambisome start date:', validators=[date_not_future], null=True, blank=True) ambi_stop_date = models.DateField(verbose_name='Ambisome end date:', validators=[date_not_future], null=True, blank=True) ambi_duration = models.IntegerField( verbose_name='Ambisome treatment duration:', null=True, blank=True) drug_intervention = models.ManyToManyField( OtherDrug, verbose_name="Other drugs/interventions given during first 14 days", ) drug_intervention_other = models.CharField( verbose_name='If other, please specify:', blank=True, max_length=50, null=True) antibiotic = models.ManyToManyField( Antibiotic, blank=True, verbose_name="Were any of the following antibiotics given?", ) antibiotic_other = models.CharField( verbose_name='If other antibiotics, please specify:', max_length=50, null=True, blank=True) blood_received = models.CharField( verbose_name='Blood transfusion received?', max_length=25, choices=YES_NO) units = models.IntegerField(verbose_name='If YES, no. of units', validators=[MinValueValidator(1)], null=True, blank=True) temperature = models.FloatField(verbose_name='Temperature', null=True, blank=True, default=None) weight = models.DecimalField( verbose_name='Weight:', validators=[MinValueValidator(20), MaxValueValidator(150)], decimal_places=1, max_digits=4, help_text='kg') medicines = models.ManyToManyField(Day14Medication, verbose_name='Medicine day 14:') medicine_other = models.CharField(verbose_name='If other, please specify:', max_length=50, null=True, blank=True) significant_dx = models.CharField( verbose_name='Other significant diagnoses since enrolment?', max_length=25, choices=YES_NO) significant_dx_datetime = models.DateTimeField( validators=[date_not_future], null=True, blank=True) flucon_missed_doses = models.CharField( verbose_name='Were any Fluconazole drug doses missed?', max_length=25, choices=YES_NO) amphotericin_missed_doses = models.CharField( verbose_name='Were any Amphotericin B drug doses missed?', max_length=25, choices=YES_NO) other_significant_dx = models.CharField( verbose_name='Other significant diagnosis since enrollment?', max_length=5, choices=YES_NO) on_site = CurrentSiteManager() objects = CrfModelManager() history = HistoricalRecords() class Meta(CrfModelMixin.Meta): verbose_name = 'Week 2' verbose_name_plural = 'Week 2'