class MaternalLocator(LocatorMixin, ExportTrackingFieldsMixin, BaseUuidModel):
    """ A model completed by the user to capture locator information and
    the details of the infant caretaker. """

    registered_subject = models.OneToOneField(RegisteredSubject, null=True)

    # appointment = models.ForeignKey(Appointment, null=True)
    report_datetime = models.DateTimeField(
        verbose_name="Report Date",
        validators=[
            datetime_not_before_study_start,
            datetime_not_future,
        ],
        default=timezone.now,
        help_text=('If reporting today, use today\'s date/time, otherwise use '
                   'the date/time this information was reported.'))

    care_clinic = OtherCharField(
        verbose_name=
        "Health clinic where your infant will receive their routine care ",
        max_length=35,
    )

    has_caretaker = models.CharField(verbose_name=(
        "Has the participant identified someone who will be "
        "responsible for the care of the baby in case of her death, to whom the "
        "study team could share information about her baby's health?"),
                                     max_length=25,
                                     choices=YES_NO,
                                     help_text="")

    caretaker_name = EncryptedCharField(
        verbose_name="Full Name of the responsible person",
        max_length=35,
        help_text="include firstname and surname",
        blank=True,
        null=True)

    caretaker_cell = EncryptedCharField(verbose_name="Cell number",
                                        max_length=8,
                                        validators=[
                                            CellNumber,
                                        ],
                                        blank=True,
                                        null=True)

    caretaker_tel = EncryptedCharField(verbose_name="Telephone number",
                                       max_length=8,
                                       validators=[
                                           TelephoneNumber,
                                       ],
                                       blank=True,
                                       null=True)

    entry_meta_data_manager = LocalCrfMetaDataManager(MaternalVisit)

    class Meta:
        app_label = 'td_maternal'
        verbose_name = 'Maternal Locator'
        verbose_name_plural = 'Maternal Locator'
class SubjectWorkFieldsMixin(models.Model):

    may_call_work = models.CharField(
        max_length=25,
        choices=YES_NO,
        verbose_name=mark_safe(
            "Has the participant given permission to contacted <b>at work</b> by telephone "
            "or cell by study staff for follow-up purposes during the study?"),
    )

    subject_work_place = EncryptedTextField(
        verbose_name="Name and location of work place",
        max_length=250,
        blank=True,
        null=True,
    )

    subject_work_phone = EncryptedCharField(
        verbose_name="Work contact telephone",
        validators=[telephone_number],
        blank=True,
        null=True,
    )

    subject_work_cell = EncryptedCharField(
        verbose_name="Work contact cell number",
        validators=[cell_number],
        blank=True,
        null=True,
    )

    class Meta:
        abstract = True
Пример #3
0
class SubjectContactFieldsMixin(models.Model):

    may_call = models.CharField(
        max_length=25,
        choices=YES_NO,
        verbose_name=mark_safe(
            'Has the participant given permission <b>to contacted by telephone '
            'or cell</b> by study staff for follow-up purposes during the study?'
        ))

    may_visit_home = models.CharField(
        max_length=25,
        choices=YES_NO,
        verbose_name=mark_safe(
            'Has the participant given permission for study '
            'staff <b>to make home visits</b> for follow-up purposes?'))

    may_sms = models.CharField(
        max_length=25,
        choices=YES_NO,
        null=True,
        blank=False,
        verbose_name=mark_safe(
            'Has the participant given permission <b>to be contacted by SMS</b> '
            'by study staff for follow-up purposes during the study?'))

    mail_address = EncryptedTextField(verbose_name='Mailing address ',
                                      max_length=500,
                                      null=True,
                                      blank=True)

    physical_address = EncryptedTextField(
        verbose_name='Physical address with detailed description',
        max_length=500,
        blank=True,
        null=True,
        help_text='')

    subject_cell = EncryptedCharField(verbose_name='Cell number',
                                      blank=True,
                                      null=True,
                                      help_text='')

    subject_cell_alt = EncryptedCharField(
        verbose_name='Cell number (alternate)', blank=True, null=True)

    subject_phone = EncryptedCharField(verbose_name='Telephone',
                                       blank=True,
                                       null=True)

    subject_phone_alt = EncryptedCharField(
        verbose_name='Telephone (alternate)', blank=True, null=True)

    class Meta:
        abstract = True
class SubjectIndirectContactFieldsMixin(models.Model):

    may_contact_indirectly = models.CharField(
        max_length=25,
        choices=YES_NO,
        verbose_name=mark_safe(
            'Has the participant given permission for study staff '
            '<b>to contact anyone else</b> for follow-up purposes during the study?'
        ),
        help_text='For example a partner, spouse, family member, neighbour ...'
    )

    indirect_contact_name = EncryptedCharField(
        verbose_name='Full names of the contact person', blank=True, null=True)

    indirect_contact_relation = EncryptedCharField(
        verbose_name='Relationship to participant', blank=True, null=True)

    indirect_contact_physical_address = EncryptedTextField(
        verbose_name='Full physical address ',
        max_length=500,
        blank=True,
        null=True)

    indirect_contact_cell = EncryptedCharField(verbose_name='Cell number',
                                               validators=[
                                                   CellNumber,
                                               ],
                                               blank=True,
                                               null=True)

    indirect_contact_cell_alt = EncryptedCharField(
        verbose_name='Cell number (alternative)',
        validators=[
            CellNumber,
        ],
        blank=True,
        null=True)

    indirect_contact_phone = EncryptedCharField(
        verbose_name='Telephone number',
        validators=[
            TelephoneNumber,
        ],
        blank=True,
        null=True)

    class Meta:
        abstract = True
class HouseholdComposition(CrfModelMixin):

    physical_add = EncryptedCharField(
        verbose_name="Description of physical address: ",
        max_length=150,
        blank=True,
        null=True,
    )

    coordinates = models.DecimalField(
        verbose_name="GPS coordinates",
        max_digits=10,
        decimal_places=4,
        help_text=" Record coordinates of the main gate to the household",
    )

    contact = models.CharField(
        verbose_name="[To the respondent] Can we contact you by telephone?",
        max_length=3,
        choices=YES_NO,
    )

    phone_number = models.IntegerField(
        verbose_name=
        "[To the respondent] What phone numbers can we use to reach you?",
        help_text="",
    )

    history = HistoricalRecords()

    class Meta(CrfModelMixin.Meta):
        app_label = 'bcpp_subject'
        verbose_name = "Household Composition"
        verbose_name_plural = "Household Composition"
Пример #6
0
class CrfEncrypted(CrfModelMixin, ExportTrackingFieldsModelMixin,
                   BaseUuidModel):

    subject_visit = models.ForeignKey(SubjectVisit, on_delete=PROTECT)

    encrypted1 = EncryptedCharField(null=True)

    export_history = ExportHistoryManager()
Пример #7
0
class ChildBirth(UniqueSubjectIdentifierFieldMixin, SiteModelMixin,
                 CryptoMixin, BaseUuidModel):
    """ A model completed by the user on the infant's birth. """

    report_datetime = models.DateTimeField(
        verbose_name="Date and Time infant enrolled",
        validators=[
            datetime_not_future,
        ])

    first_name = FirstnameField(
        max_length=25,
        verbose_name="Infant's first name",
        help_text="If infant name is unknown or not yet determined, "
        "use Baby + birth order + mother's last name, e.g. 'Baby1Malane'")

    last_name = LastnameField(
        max_length=25,
        verbose_name="Infant's last name",
        help_text="If infant name is unknown or not yet determined, "
        "use Baby + birth order + mother's last name, e.g. 'Baby1Malane'")

    initials = EncryptedCharField(validators=[
        RegexValidator(regex=r'^[A-Z]{2,3}$',
                       message=('Ensure initials consist of letters '
                                'only in upper case, no spaces.'))
    ], )

    dob = models.DateField(verbose_name='Date of Birth',
                           help_text="Must match labour and delivery report.",
                           validators=[
                               date_not_future,
                           ])

    gender = models.CharField(max_length=10, choices=GENDER_UNDETERMINED)

    def __str__(self):
        return f'{self.first_name}, {self.initials}, {self.gender}'

    @property
    def registered_subject(self):
        """Return infant registered subject.
        """
        registered_subject_cls = django_apps.get_model(
            'edc_registration.registeredsubject')
        try:
            registered_subject = registered_subject_cls.objects.get(
                subject_identifier=self.subject_identifier)
        except registered_subject_cls.DoesNotExist:
            raise ValidationError(
                f'Registered Subject is missing for {self.subject_identifier}')
        else:
            return registered_subject

    class Meta:
        app_label = 'flourish_child'
        verbose_name = "Child Birth"
class MemberEntryMixin(SurveyScheduleModelMixin,
                       RequiresHouseholdLogEntryMixin, models.Model):
    """For absentee and undecided log models.
    """

    household_member = models.ForeignKey(HouseholdMember,
                                         on_delete=models.PROTECT)

    report_date = models.DateField(editable=False)

    report_datetime = models.DateTimeField(verbose_name='Report date',
                                           validators=[datetime_not_future],
                                           default=get_utcnow)

    reason_other = OtherCharField()

    next_appt_datetime = models.DateTimeField(
        verbose_name='Follow-up appointment',
        help_text='The date and time to meet with the subject')

    next_appt_datetime_source = models.CharField(
        verbose_name='Appointment date suggested by?',
        max_length=25,
        choices=NEXT_APPOINTMENT_SOURCE,
        help_text='')

    contact_details = EncryptedCharField(
        null=True,
        blank=True,
        help_text='Information that can be used to contact someone, '
        'preferrably the subject, to confirm the appointment')

    comment = EncryptedTextField(
        verbose_name='Comments',
        max_length=250,
        blank=True,
        null=True,
        help_text=('IMPORTANT: Do not include any names or other personally '
                   'identifying information in this comment'))

    def save(self, *args, **kwargs):
        self.survey_schedule = (
            self.household_member.survey_schedule_object.field_value)
        if not self.id and self.report_date:
            raise ValueError(
                'Expected report_date to be None. Got {}. Set report datetime, '
                'not report_date.'.format(self.report_date))
        self.report_date = arrow.Arrow.fromdatetime(
            self.report_datetime,
            tzinfo=self.report_datetime.tzinfo).to('UTC').date()
        super().save(*args, **kwargs)

    class Meta:
        abstract = True
        unique_together = ('household_member', 'report_date')
Пример #9
0
class VaccinationDetails(NonUniqueSubjectIdentifierFieldMixin,
                         SiteModelMixin, BaseUuidModel):
    report_datetime = models.DateTimeField(
        verbose_name='Report Date and Time',
        default=get_utcnow,
        help_text='Date and time of report.')

    date_of_vaccination = models.DateField(
        verbose_name="When did the participant receive their vaccination dose?",
        validators=[date_not_future, ])

    vaccination_place = models.CharField(
        verbose_name="Where was the vaccination administered?",
        max_length=35,
        blank=True,
        null=True, )

    vaccine_name = models.CharField(
        verbose_name="Name of the vaccination",
        max_length=35,
        blank=True,
        null=True, )

    dosage_administered = models.IntegerField(
        verbose_name="Dosage administered",
        blank=True,
        null=True, )

    batch_number = models.CharField(
        verbose_name="Vaccination batch number",
        max_length=35,
        blank=True,
        null=True, )

    expiry_date = models.DateField(
        verbose_name="Vaccination expiry date",
        validators=[date_is_future, ])

    provider_name = EncryptedCharField(
        verbose_name="Name of the provider",
        max_length=35,
        blank=True,
        null=True, )

    next_vaccination = models.DateField(
        verbose_name="When is the participant scheduled for "
                     "their next vaccination dose?",
        validators=[date_is_future, ])

    class Meta:
        verbose_name = "Vaccine Details"
        verbose_name_plural = "Vaccine Details"
class SenaiteUser(BaseUuidModel, models.Model):

    username = models.CharField(verbose_name="Senaitte Username",
                                null=True,
                                max_length=200)

    contact = models.CharField(verbose_name="Contact",
                               null=True,
                               max_length=300)

    password = EncryptedCharField(verbose_name='Senaite LIMS Password',
                                  blank=False,
                                  null=True,
                                  help_text='Senaite LIMS password')
Пример #11
0
class MapitioAdditionalIdentifiersModelMixin(models.Model):

    hospital_identifier = EncryptedCharField(
        verbose_name="HMS Identifier",
        help_text="Hindu Mandal Hospital Identifier",
        unique=True,
    )

    ctc_identifier = EncryptedCharField(
        verbose_name="CTC Identifier",
        null=True,
        blank=True,
        unique=True,
    )

    file_number = EncryptedCharField(
        verbose_name="Patient File number",
        help_text=mark_safe("Patient file number from Hindu Mandal Hospital"),
        unique=True,
    )

    class Meta:
        abstract = True
Пример #12
0
class AccountHolder(BaseUuidModel):

    first_name = EncryptedCharField(verbose_name=_("First name"))

    last_name = EncryptedCharField(verbose_name=_("Last name"))

    initials = models.CharField(max_length=3)

    comment = models.TextField(
        max_length=100,
        blank=True,
    )

    def __unicode__(self):
        return '%s, %s' % (self.last_name, self.first_name)

    def get_absolute_url(self):
        return "/lab_account/accountholder/%s/" % self.id

    class Meta:
        ordering = ['last_name', 'first_name']
        unique_together = ['last_name', 'first_name']
        app_label = 'lab_account'
        db_table = 'bhp_lab_registration_accountholder'
Пример #13
0
class PersonalFieldsMixin(CryptoMixin, models.Model):

    first_name = FirstnameField(
        null=True, blank=False)

    last_name = LastnameField(
        verbose_name="Last name",
        null=True, blank=False)

    initials = EncryptedCharField(
        validators=[RegexValidator(
            regex=r'^[A-Z]{2,3}$',
            message=('Ensure initials consist of letters '
                     'only in upper case, no spaces.'))],
        null=True, blank=False)

    dob = models.DateField(
        verbose_name="Date of birth",
        null=True,
        blank=False)

    is_dob_estimated = IsDateEstimatedField(
        verbose_name="Is date of birth estimated?",
        null=True,
        blank=False)

    gender = models.CharField(
        verbose_name="Gender",
        choices=GENDER_UNDETERMINED,
        max_length=1,
        null=True,
        blank=False)

    guardian_name = LastnameField(
        verbose_name=('Guardian\'s last and first name'),
        validators=[FullNameValidator()],
        blank=True,
        null=True,
        help_text=mark_safe(
            'Required only if participant is a minor.<BR>'
            'Format is \'LASTNAME, FIRSTNAME\'. '
            'All uppercase separated by a comma.'))

    subject_type = models.CharField(
        max_length=25)

    class Meta:
        abstract = True
class HouseholdRefusalMixin(models.Model):

    household_structure = models.OneToOneField(HouseholdStructure,
                                               on_delete=models.PROTECT)

    report_datetime = models.DateTimeField(verbose_name="Report date",
                                           default=get_utcnow,
                                           validators=[datetime_not_future])

    reason = models.CharField(
        verbose_name=
        'Please indicate the reason the household cannot be enumerated',
        max_length=25,
        choices=HOUSEHOLD_REFUSAL)

    reason_other = EncryptedCharField(verbose_name='If Other, specify',
                                      max_length=100,
                                      blank=True,
                                      null=True)

    comment = EncryptedTextField(
        max_length=250,
        help_text="You may provide a comment here or leave BLANK.",
        blank=True,
        null=True)

    def common_clean(self):
        if self.household_structure.enrolled:
            raise HouseholdAlreadyEnrolledError(
                'Household is already enrolled. Blocking attempt to '
                'add \'{}\'.'.format(self._meta.verbose_name))
        super().common_clean()

    def save(self, *args, **kwargs):
        if self.household_structure.enrolled:
            raise ValidationError('Household is enrolled.')
        super(HouseholdRefusalMixin, self).save(*args, **kwargs)

    def __str__(self):
        return '{} ({})'.format(self.household_structure,
                                self.report_datetime.strftime('%Y-%m-%d'))

    class Meta:
        abstract = True
class Respondent(CrfModelMixin):

    household_composition = models.ForeignKey(HouseholdComposition,
                                              on_delete=PROTECT)

    first_name = EncryptedCharField(
        verbose_name="First name or initials ",
        max_length=25,
    )
    relation = models.CharField(
        verbose_name="Relation",
        choices=RELATION,
        max_length=25,
    )
    relation_other = OtherCharField()

    gender = models.CharField(
        verbose_name="Gender",
        max_length=6,
        choices=GENDER,
    )
    age = models.IntegerField(verbose_name="Age", )
    present = models.CharField(
        verbose_name="Present Today",
        max_length=3,
        choices=YES_NO,
    )
    nights_outside = models.IntegerField(
        verbose_name="Nights spent outside of this Community", )

    history = HistoricalRecords()

    class Meta(CrfModelMixin.Meta):
        app_label = 'bcpp_subject'
        verbose_name = "Respondent Details"
        verbose_name_plural = "Respondent Details"
Пример #16
0
class PersonalFieldsMixin(CryptoMixin, models.Model):

    first_name = FirstnameField(
        null=True,
        blank=False,
        validators=[
            RegexValidator(
                regex=r"^([A-Z]+$|[A-Z]+\ [A-Z]+)$",
                message="Ensure name consist of letters only in upper case",
            )
        ],
        help_text="Use UPPERCASE letters only.",
    )

    last_name = LastnameField(
        verbose_name="Surname",
        null=True,
        blank=False,
        validators=[
            RegexValidator(
                regex=r"^([A-Z]+$|[A-Z]+\ [A-Z]+)$",
                message="Ensure name consist of letters only in upper case",
            )
        ],
        help_text="Use UPPERCASE letters only.",
    )

    initials = EncryptedCharField(
        validators=[
            RegexValidator(
                regex=r"^[A-Z]{2,3}$",
                message=
                "Ensure initials consist of letters only in upper case, no spaces.",
            )
        ],
        null=True,
        blank=False,
    )

    dob = models.DateField(verbose_name="Date of birth",
                           null=True,
                           blank=False)

    is_dob_estimated = IsDateEstimatedField(
        verbose_name="Is date of birth estimated?", null=True, blank=False)

    gender = models.CharField(
        verbose_name="Gender",
        choices=GENDER_UNDETERMINED,
        max_length=1,
        null=True,
        blank=False,
    )

    guardian_name = LastnameField(
        verbose_name="Guardian's last and first name",
        validators=[FullNameValidator()],
        blank=True,
        null=True,
        help_text=mark_safe("Required only if participant is a minor.<BR>"
                            "Format is 'LASTNAME, FIRSTNAME'. "
                            "All uppercase separated by a comma."),
    )

    subject_type = models.CharField(max_length=25)

    class Meta:
        abstract = True
Пример #17
0
class RandomizationListModelMixin(models.Model):
    """
    A model mixin for the randomization list.

    The default expects and ACTIVE vs PLACEBO randomization. If
    yours differs, you need to re-declare field "assignment"
    and model method "treatment_description". The default
    `Randomizer` class MAY also need to be customized.
    """

    assignment = EncryptedCharField()

    randomizer_name = models.CharField(max_length=50, default="default")

    subject_identifier = models.CharField(verbose_name="Subject Identifier",
                                          max_length=50,
                                          null=True,
                                          unique=True)

    sid = models.IntegerField(unique=True)

    site_name = models.CharField(max_length=100)

    allocation = EncryptedCharField(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.PROTECT,
                                       related_name="+")

    verified = models.BooleanField(default=False)

    verified_datetime = models.DateTimeField(null=True)

    verified_user = models.CharField(max_length=50, null=True)

    objects = RandomizationListManager()

    history = HistoricalRecords(inherit=True)

    on_site = CurrentSiteManager("allocated_site")

    def __str__(self):
        return f"{self.site_name}.{self.sid} subject={self.subject_identifier}"

    def save(self, *args, **kwargs):
        self.randomizer_name = self.randomizer_cls.name
        try:
            getattr(self, "assignment_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.assignment} SID:{self.site_name}.{self.sid}"

    @property
    def randomizer_cls(self):
        return site_randomizers.get(self.randomizer_name)

    # customize if approriate
    @property
    def assignment_description(self):
        """May be overridden."""
        if self.assignment not in self.randomizer_cls.assignment_map:
            raise RandomizationError(
                f"Invalid assignment. Expected one of "
                f"{list(self.randomizer_cls.assignment_map.keys())}. "
                f"Got `{self.assignment}`. See ")
        return self.assignment

    def natural_key(self):
        return (self.sid, )

    class Meta:
        abstract = True
        ordering = ("site_name", "sid")
        unique_together = ("site_name", "sid")
        permissions = (("display_assignment", "Can display assignment"), )
class SubjectContactFieldsMixin(models.Model):

    may_call = models.CharField(
        max_length=25,
        choices=YES_NO,
        verbose_name=mark_safe(
            "Has the participant given permission <b>to contacted by telephone "
            "or cell</b> by study staff for follow-up purposes during the study?"
        ),
    )

    may_visit_home = models.CharField(
        max_length=25,
        choices=YES_NO,
        verbose_name=mark_safe(
            "Has the participant given permission for study "
            "staff <b>to make home visits</b> for follow-up purposes?"
        ),
    )

    may_sms = models.CharField(
        max_length=25,
        choices=YES_NO,
        null=True,
        blank=False,
        verbose_name=mark_safe(
            "Has the participant given permission <b>to be contacted by SMS</b> "
            "by study staff for follow-up purposes during the study?"
        ),
    )

    mail_address = EncryptedTextField(
        verbose_name="Mailing address ", max_length=500, null=True, blank=True
    )

    physical_address = EncryptedTextField(
        verbose_name="Physical address with detailed description",
        max_length=500,
        blank=True,
        null=True,
        help_text="",
    )

    subject_cell = EncryptedCharField(
        verbose_name="Cell number",
        validators=[cell_number],
        blank=True,
        null=True,
        help_text="",
    )

    subject_cell_alt = EncryptedCharField(
        verbose_name="Cell number (alternate)",
        validators=[cell_number],
        blank=True,
        null=True,
    )

    subject_phone = EncryptedCharField(
        verbose_name="Telephone", validators=[telephone_number], blank=True, null=True
    )

    subject_phone_alt = EncryptedCharField(
        verbose_name="Telephone (alternate)",
        validators=[telephone_number],
        blank=True,
        null=True,
    )

    class Meta:
        abstract = True
Пример #19
0
class MaternalLocator(LocatorModelMixin, ActionModelMixin,
                      RequiresConsentFieldsModelMixin, SiteModelMixin,
                      NonUniqueSubjectIdentifierModelMixin, BaseUuidModel):

    action_name = MATERNAL_LOCATOR_ACTION

    tracking_identifier_prefix = 'SL'

    on_site = CurrentSiteManager()

    locator_date = models.DateField(
        verbose_name='Date Locator Form signed',
        validators=[date_not_future])

    health_care_infant = models.CharField(
        verbose_name=('Health clinic where your infant will'
                      ' receive their routine care'),
        max_length=35,
        blank=True,
        null=True)

    may_call = models.CharField(
        max_length=25,
        choices=YES_NO,
        verbose_name=mark_safe(
            'Has the participant given his/her permission for study '
            'staff to call her for follow-up purposes during the study?'))

    may_visit_home = models.CharField(
        max_length=25,
        choices=YES_NO,
        verbose_name=mark_safe(
            'Has the participant given his/her permission for study staff <b>to '
            'make home visits</b> for follow-up purposes during the study??'))

    has_caretaker = models.CharField(
        verbose_name=(
            "Has the participant identified someone who will be "
            "responsible for the care of the baby in case of her death, to "
            "whom the study team could share information about her baby's "
            "health?"),
        max_length=25,
        choices=YES_NO,
        help_text="")

    caretaker_name = EncryptedCharField(
        verbose_name="Full Name of the responsible person",
        max_length=35,
        help_text="include firstname and surname",
        blank=True,
        null=True)

    may_call_work = models.CharField(
        max_length=25,
        choices=YES_NO_DOESNT_WORK,
        verbose_name=mark_safe(
            'Has the participant given his/her permission for study staff '
            'to contact her at work for follow up purposes during the study?'))

    subject_work_phone = EncryptedCharField(
        verbose_name='Work contact number',
        blank=True,
        null=True)

    caretaker_cell = EncryptedCharField(
        verbose_name="Cell number",
        max_length=8,
        validators=[CellNumber, ],
        blank=True,
        null=True)

    caretaker_tel = EncryptedCharField(
        verbose_name="Telephone number",
        max_length=8,
        validators=[TelephoneNumber, ],
        blank=True,
        null=True)

    history = HistoricalRecords()

    class Meta:
        verbose_name = 'Maternal Locator'
class RegisteredSubject(UniqueSubjectIdentifierModelMixin, SiteModelMixin,
                        edc_models.BaseUuidModel):
    """A model mixin for the RegisteredSubject model (only)."""

    # may not be available when instance created (e.g. infants prior to birth
    # report)
    first_name = FirstnameField(null=True)

    # may not be available when instance created (e.g. infants or household
    # subject before consent)
    last_name = LastnameField(verbose_name="Last name", null=True)

    # may not be available when instance created (e.g. infants)
    initials = EncryptedCharField(
        validators=[
            RegexValidator(
                regex=r"^[A-Z]{2,3}$",
                message=("Ensure initials consist of letters "
                         "only in upper case, no spaces."),
            )
        ],
        null=True,
    )

    dob = models.DateField(
        verbose_name=_("Date of birth"),
        null=True,
        blank=False,
        help_text=_("Format is YYYY-MM-DD"),
    )

    is_dob_estimated = IsDateEstimatedField(
        verbose_name=_("Is date of birth estimated?"), null=True, blank=False)

    gender = models.CharField(verbose_name="Gender",
                              max_length=1,
                              choices=GENDER,
                              null=True,
                              blank=False)

    subject_consent_id = models.CharField(max_length=100,
                                          null=True,
                                          blank=True)

    registration_identifier = models.CharField(max_length=36,
                                               null=True,
                                               blank=True)

    sid = models.CharField(verbose_name="SID",
                           max_length=15,
                           null=True,
                           blank=True)

    subject_type = models.CharField(max_length=25, null=True, blank=True)

    relative_identifier = models.CharField(
        verbose_name="Identifier of immediate relation",
        max_length=36,
        null=True,
        blank=True,
        help_text=
        "For example, mother's identifier, if available / appropriate",
    )

    identity = IdentityField(null=True, blank=True)

    identity_type = IdentityTypeField(null=True, blank=True)

    screening_identifier = models.CharField(max_length=36,
                                            null=True,
                                            blank=True)

    screening_datetime = models.DateTimeField(null=True, blank=True)

    screening_age_in_years = models.IntegerField(null=True, blank=True)

    registration_datetime = models.DateTimeField(null=True, blank=True)

    # For simplicity, if going straight from screen to rando,
    # update both registration date and randomization date
    randomization_datetime = models.DateTimeField(null=True, blank=True)

    registration_status = models.CharField(verbose_name="Registration status",
                                           max_length=25,
                                           null=True,
                                           blank=True)

    consent_datetime = models.DateTimeField(null=True, blank=True)

    comment = models.TextField(verbose_name="Comment",
                               max_length=250,
                               null=True,
                               blank=True)

    additional_key = models.CharField(
        max_length=36,
        verbose_name="-",
        editable=False,
        default=None,
        null=True,
        help_text=(
            "A uuid (or some other text value) to be added to bypass the "
            "unique constraint of just firstname, initials, and dob."
            "The default constraint proves limiting since the source "
            "model usually has some other attribute in additional to "
            "first_name, initials and dob which is not captured in "
            "this model"),
    )

    dm_comment = models.CharField(
        verbose_name="Data Management comment",
        max_length=150,
        null=True,
        editable=False,
    )

    randomization_list_model = models.CharField(max_length=150, null=True)

    on_site = CurrentSiteManager()

    history = edc_models.HistoricalRecords()

    objects = RegisteredSubjectManager()

    def save(self, *args, **kwargs):
        if self.identity:
            self.additional_key = None
        self.set_uuid_as_subject_identifier_if_none()
        self.raise_on_duplicate("subject_identifier")
        self.raise_on_duplicate("identity")
        self.raise_on_changed_subject_identifier()
        super().save(*args, **kwargs)

    def natural_key(self):
        return tuple(self.subject_identifier_as_pk)

    def __str__(self):
        return self.masked_subject_identifier

    natural_key.dependencies = ["sites.Site"]

    def update_subject_identifier_on_save(self):
        """Overridden to not set the subject identifier on save."""
        if not self.subject_identifier:
            self.subject_identifier = self.subject_identifier_as_pk.hex
        elif re.match(UUID_PATTERN, self.subject_identifier):
            pass
        return self.subject_identifier

    def make_new_identifier(self):
        return self.subject_identifier_as_pk.hex

    @property
    def masked_subject_identifier(self):
        """Returns the subject identifier, if set, otherwise
        the string '<identifier not set>'.
        """
        if not self.subject_identifier_is_set:
            return "<identifier not set>"
        return self.subject_identifier

    @property
    def subject_identifier_is_set(self):
        """Returns True if subject identifier has been set to a
        subject identifier; that is, no longer the default UUID.
        """
        is_set = True
        try:
            obj = self.__class__.objects.get(pk=self.id)
        except ObjectDoesNotExist:
            is_set = False
        else:
            if re.match(UUID_PATTERN, obj.subject_identifier):
                return False
        return is_set

    def raise_on_changed_subject_identifier(self):
        """Raises an exception if there is an attempt to change
        the subject identifier for an existing instance if the subject
        identifier is already set.
        """
        if self.id and self.subject_identifier_is_set:
            with transaction.atomic():
                obj = self.__class__.objects.get(pk=self.id)
                if obj.subject_identifier != self.subject_identifier_as_pk.hex:
                    if self.subject_identifier != obj.subject_identifier:
                        raise RegisteredSubjectError(
                            "Subject identifier cannot be changed for "
                            "existing registered subject. "
                            f"Got {self.subject_identifier} <> {obj.subject_identifier}."
                        )

    def raise_on_duplicate(self, attrname):
        """Checks if the subject identifier (or other attr) is in use,
        for new and existing instances.
        """
        if getattr(self, attrname):
            with transaction.atomic():
                error_msg = (
                    f"Cannot {{action}} registered subject with a duplicate "
                    f"'{attrname}'. Got {getattr(self, attrname)}.")
                try:
                    obj = self.__class__.objects.exclude(**{
                        "pk": self.pk
                    } if self.id else {}).get(
                        **{attrname: getattr(self, attrname)})
                    if not self.id:
                        raise RegisteredSubjectError(
                            error_msg.format(action="insert"))
                    elif self.subject_identifier_is_set and obj.id != self.id:
                        raise RegisteredSubjectError(
                            error_msg.format(action="update"))
                    else:
                        raise RegisteredSubjectError(
                            error_msg.format(action="update"))
                except ObjectDoesNotExist:
                    pass

    def set_uuid_as_subject_identifier_if_none(self):
        """Inserts a random uuid as a dummy identifier for a new
        instance.

        Model uses subject_identifier_as_pk as a natural key for
        serialization/deserialization. Value must not change
        once set.
        """
        if not self.subject_identifier:
            self.subject_identifier = self.subject_identifier_as_pk.hex

    class Meta(edc_models.BaseUuidModel.Meta):
        verbose_name = "Registered Subject"
        ordering = ["subject_identifier"]
        unique_together = ("first_name", "dob", "initials", "additional_key")
        indexes = [
            models.Index(
                fields=["first_name", "dob", "initials", "additional_key"]),
            models.Index(fields=[
                "identity", "subject_identifier", "screening_identifier"
            ]),
        ]
        permissions = (
            ("display_firstname", "Can display first name"),
            ("display_lastname", "Can display last name"),
            ("display_dob", "Can display DOB"),
            ("display_identity", "Can display identity number"),
            ("display_initials", "Can display initials"),
        )
Пример #21
0
class KaraboSubjectConsent(CryptoMixin, VerificationFieldsMixin,
                           SiteModelMixin, BaseUuidModel):

    subject_identifier = models.CharField(verbose_name="Subject Identifier",
                                          max_length=50)

    screening_identifier = models.CharField(
        verbose_name="Screening Identifier", max_length=36, unique=True)

    report_datetime = models.DateTimeField(
        verbose_name="Report Date",
        validators=[datetime_not_before_study_start, datetime_not_future],
        default=get_utcnow,
        help_text=('If reporting today, use today\'s date/time, otherwise use '
                   'the date/time this information was reported.'))

    first_name = FirstnameField(verbose_name='First Name',
                                help_text=('(Must match name on file'
                                           ' with Tshilo Dikotla Study)'))

    last_name = LastnameField(verbose_name='Surname',
                              help_text=('(Must match name on file '
                                         'with Tshilo Dikotla Study)'))

    initials = EncryptedCharField(verbose_name='Initials ',
                                  help_text=('(Must match initials on file '
                                             'with Tshilo Dikotla Study)'))

    dob = models.DateField(verbose_name="Date of birth",
                           null=True,
                           blank=False)

    is_dob_estimated = IsDateEstimatedField(
        verbose_name="Is date of birth estimated?", null=True, blank=False)

    language = models.CharField(verbose_name='Language of consent:',
                                max_length=25,
                                choices=settings.LANGUAGES)

    is_literate = models.CharField(verbose_name='Is the participant literate?',
                                   max_length=25,
                                   choices=YES_NO)

    guardian_name = LastnameField(
        verbose_name='Witness\'s last and first name',
        validators=[FullNameValidator()],
        blank=True,
        null=True,
        help_text=mark_safe('Required only if participant is illiterate.<br>'
                            'Format is \'LASTNAME, FIRSTNAME\'. '
                            'All uppercase separated by a comma.'))

    consent_datetime = models.DateTimeField(
        verbose_name='Consent date and time',
        validators=[datetime_not_before_study_start, datetime_not_future])

    identity = IdentityField(verbose_name='Omang of consenting woman',
                             help_text=mark_safe(
                                 '(must match Omang on file '
                                 'with Tshilo Dikotla Study)'
                                 ' (Confirm that all Tshilo Dikotla '
                                 'women have enrolled with an Omang, '
                                 'otherwise will need additional ID '
                                 'questions.)'))

    consent_reviewed = models.CharField(
        verbose_name='I have reviewed the Karabo study '
        'consent with the client.',
        max_length=3,
        choices=YES_NO)

    study_questions = models.CharField(
        verbose_name='I have answered all questions the client'
        ' had about the Karabo study consent.',
        max_length=3,
        choices=YES_NO)

    assessment_score = models.CharField(
        verbose_name='I have asked the client questions'
        ' about the Karabo study and they have demonstrated an'
        ' understanding of the study by their answers.',
        max_length=3,
        choices=YES_NO)

    consent_signature = models.CharField(
        verbose_name='The client has signed the consent form.',
        max_length=3,
        choices=YES_NO)

    consent_copy = models.CharField(
        verbose_name='I have offered the client a signed copy'
        ' of the consent form.',
        max_length=25,
        choices=ANSWERS)

    def __str__(self):
        return f'{self.subject_identifier} {self.screening_identifier}'

    def natural_key(self):
        return (self.subject_identifier, self.screening_identifier)

    objects = KaraboSubjectConsentManager()

    history = HistoricalRecords()

    on_site = CurrentSiteManager()

    class Meta:
        app_label = 'td_maternal'
        verbose_name = "Karabo Subject Consent"
        verbose_name_plural = "Karabo Subject Consents"
Пример #22
0
class SubjectScreening(
    ScreeningModelMixin,
    BaseUuidModel,
):
    identifier_cls = ScreeningIdentifier

    screening_consent = models.CharField(
        verbose_name=(
            "Has the subject given his/her verbal consent "
            "to be screened for the INTE Africa trial?"
        ),
        max_length=15,
        choices=YES_NO,
    )

    selection_method = models.CharField(
        verbose_name="How was the patient selected for screening?",
        max_length=25,
        choices=SELECTION_METHOD,
    )

    clinic_type = models.CharField(
        verbose_name="From which type of clinic was the patient selected?",
        max_length=25,
        choices=CLINIC_CHOICES,
    )

    initials = EncryptedCharField(
        validators=[
            RegexValidator("[A-Z]{1,3}", "Invalid format"),
            MinLengthValidator(2),
            MaxLengthValidator(3),
        ],
        help_text="Use UPPERCASE letters only. May be 2 or 3 letters.",
        blank=False,
    )

    qualifying_condition = models.CharField(
        verbose_name=(
            "Does the patient have at least one of the following "
            "conditions: HIV, Diabetes and/or Hypertension"
        ),
        max_length=15,
        choices=YES_NO,
    )

    requires_acute_care = models.CharField(
        verbose_name=("Does the patient require acute care including in-patient admission"),
        max_length=25,
        choices=YES_NO,
    )

    lives_nearby = models.CharField(
        verbose_name=(
            "Is the patient planning to remain in the catchment area " "for at least 6 months"
        ),
        max_length=15,
        choices=YES_NO,
    )

    def save(self, *args, **kwargs):
        check_eligible_final(self)
        super().save(*args, **kwargs)

    class Meta:
        verbose_name = "Subject Screening"
        verbose_name_plural = "Subject Screening"
Пример #23
0
class CorrectConsent(CorrectConsentMixin, BaseUuidModel):

    """A model linked to the subject consent to record corrections."""

    subject_consent = models.OneToOneField(
        SubjectConsent, on_delete=PROTECT)

    report_datetime = models.DateTimeField(
        verbose_name="Correction report date ad time",
        null=True,
        validators=[
            datetime_not_future],
    )

    old_first_name = FirstnameField(
        null=True,
        blank=True,
    )

    new_first_name = FirstnameField(
        null=True,
        blank=True,
    )

    old_last_name = LastnameField(
        null=True,
        blank=True,
    )
    new_last_name = LastnameField(
        null=True,
        blank=True,
    )

    old_initials = EncryptedCharField(
        blank=True,
        null=True,
        validators=[RegexValidator(
            regex=r'^[A-Z]{2,3}$',
            message='Ensure initials consist of letters only in upper case, no spaces.'), ],
    )

    new_initials = EncryptedCharField(
        validators=[RegexValidator(
            regex=r'^[A-Z]{2,3}$',
            message='Ensure initials consist of letters only in upper case, no spaces.'), ],
        null=True,
        blank=True,
    )

    old_dob = models.DateField(
        verbose_name="Old Date of birth",
        null=True,
        blank=True,
        help_text="Format is YYYY-MM-DD",
    )

    new_dob = models.DateField(
        verbose_name="New Date of birth",
        null=True,
        blank=True,
        help_text="Format is YYYY-MM-DD",
    )

    old_gender = models.CharField(
        choices=GENDER_UNDETERMINED,
        blank=True,
        null=True,
        max_length=1)

    new_gender = models.CharField(
        choices=GENDER_UNDETERMINED,
        max_length=1,
        null=True,
        blank=True,
    )

    old_guardian_name = LastnameField(
        validators=[
            RegexValidator('^[A-Z]{1,50}\, [A-Z]{1,50}$',
                           'Invalid format. Format is '
                           '\'LASTNAME, FIRSTNAME\'. All uppercase separated by a comma')],
        blank=True,
        null=True,
    )

    new_guardian_name = LastnameField(
        validators=[
            RegexValidator('^[A-Z]{1,50}\, [A-Z]{1,50}$',
                           'Invalid format. Format is \'LASTNAME, FIRSTNAME\'. '
                           'All uppercase separated by a comma')],
        blank=True,
        null=True,
    )

    old_may_store_samples = models.CharField(
        verbose_name="Old Sample storage",
        max_length=3,
        blank=True,
        null=True,
        choices=YES_NO,
    )

    new_may_store_samples = models.CharField(
        verbose_name="New Sample storage",
        max_length=3,
        blank=True,
        null=True,
        choices=YES_NO,
    )

    old_is_literate = models.CharField(
        verbose_name="(Old) Is the participant LITERATE?",
        max_length=3,
        blank=True,
        null=True,
        choices=YES_NO,
    )

    new_is_literate = models.CharField(
        verbose_name="(New) Is the participant LITERATE?",
        max_length=3,
        blank=True,
        null=True,
        choices=YES_NO,
    )

    old_witness_name = LastnameField(
        verbose_name="Witness\'s Last and first name (illiterates only)",
        validators=[
            RegexValidator(
                '^[A-Z]{1,50}\, [A-Z]{1,50}$',
                'Invalid format. Format '
                'is \'LASTNAME, FIRSTNAME\'. All uppercase separated by a comma')],
        blank=True,
        null=True,
        help_text=('Required only if subject is illiterate. '
                   'Format is \'LASTNAME, FIRSTNAME\'. '
                   'All uppercase separated by a comma'),
    )

    new_witness_name = LastnameField(
        verbose_name="Witness\'s Last and first name (illiterates only)",
        validators=[
            RegexValidator(
                '^[A-Z]{1,50}\, [A-Z]{1,50}$',
                'Invalid format. Format is \'LASTNAME, FIRSTNAME\'. '
                'All uppercase separated by a comma')],
        blank=True,
        null=True,
        help_text=('Required only if subject is illiterate. '
                   'Format is \'LASTNAME, FIRSTNAME\'. '
                   'All uppercase separated by a comma'),
    )

    objects = CorrectConsentManager()

    history = HistoricalRecords()

    def __str__(self):
        return str(self.subject_consent,)

    def natural_key(self):
        return self.subject_consent.natural_key()
    natural_key.dependencies = ['bcpp_subject.subject_consent']

    def dashboard(self):
        ret = None
        dashboard_type = self.subject_consent.registered_subject.subject_type.lower()
        if self.appointment:
            url = reverse('bcpp_subject_dashboard:dashboard_url',
                          kwargs={'dashboard_type': dashboard_type,
                                  'dashboard_model': 'appointment',
                                  'dashboard_id': self.appointment.pk,
                                  'show': 'appointments'})
            ret = """<a href="{url}" />dashboard</a>""".format(url=url)
        return ret
    dashboard.allow_tags = True

    class Meta:
        app_label = 'bcpp_subject'
Пример #24
0
class SubjectScreening(
    NonUniqueSubjectIdentifierModelMixin,
    MapitioAdditionalIdentifiersModelMixin,
    SearchSlugModelMixin,
    ScreeningMethodsModeMixin,
    ScreeningFieldsModeMixin,
    PersonalFieldsMixin,
    SiteModelMixin,
    edc_models.BaseUuidModel,
):

    screening_identifier = models.CharField(
        verbose_name="Enrollment ID",
        max_length=50,
        blank=True,
        unique=True,
        editable=False,
    )

    initials = EncryptedCharField(
        validators=[
            RegexValidator("[A-Z]{1,3}", "Invalid format"),
            MinLengthValidator(2),
            MaxLengthValidator(3),
        ],
        help_text="Use UPPERCASE letters only. May be 2 or 3 letters.",
        blank=False,
    )

    report_datetime = models.DateTimeField(
        verbose_name="Report Date and Time",
        default=get_utcnow,
        help_text="Date and time of this report.",
    )

    gender = models.CharField(
        verbose_name="Gender", choices=GENDER, max_length=1, null=True, blank=False,
    )

    age_in_years = models.IntegerField(
        validators=[MinValueValidator(0), MaxValueValidator(110)],
    )

    dob = models.DateField(verbose_name="Date of birth", null=True, blank=False)

    is_dob_estimated = edc_models.IsDateEstimatedField(
        verbose_name="Is date of birth estimated?", null=True, blank=False
    )

    confirm_hospital_identifier = EncryptedCharField(
        verbose_name="Confirm HMS Identifier",
        null=True,
        help_text="Retype the Hindu Mandal Hospital Identifier",
    )

    confirm_ctc_identifier = EncryptedCharField(
        verbose_name="Confirm CTC Identifier", null=True, blank=True,
    )

    clinic_registration_date = models.DateField(
        verbose_name="Date patient was <u>first</u> enrolled to this clinic",
        validators=[date_is_past, date_is_not_now],
    )

    last_clinic_date = models.DateField(
        verbose_name="Date patient was <u>last</u> seen at this clinic",
        validators=[date_is_past, date_is_not_now],
        help_text="Date last seen according to information on the patient chart.",
    )

    # not used, keep for compatability
    screening_consent = models.CharField(
        verbose_name=(
            "Has the subject given his/her verbal consent "
            "to be screened for the Mapitio trial?"
        ),
        max_length=15,
        choices=YES_NO_NA,
        default=NOT_APPLICABLE,
    )

    # not used, keep for compatability
    selection_method = models.CharField(
        verbose_name="How was the patient selected for screening?",
        max_length=25,
        choices=SELECTION_METHOD,
        default=INTEGRATED_CLINIC,
    )

    # not used, keep for compatability
    clinic_type = models.CharField(
        verbose_name="From which type of clinic was the patient selected",
        max_length=25,
        choices=CLINIC_CHOICES,
        default=INTEGRATED_CLINIC,
    )

    on_site = CurrentSiteManager()

    objects = SubjectScreeningModelManager()

    def save(self, *args, **kwargs):
        """Screening Identifier is always allocated.
        """
        self.screening_identifier = self.hospital_identifier
        check_eligible_final(self)
        super().save(*args, **kwargs)

    class Meta:
        verbose_name = "Enrollment"
        verbose_name_plural = "Enrollment"
        unique_together = (
            ("first_name", "dob", "initials", "last_name"),
            ("hospital_identifier", "ctc_identifier"),
        )
class MaternalRando(CrfModelMixin):
    """ Stores a prepared infant randomization list.

    If you need to undo a randomization, here is an example of how::
    >>> # To undo a randomization
    >>> subject_identifier = '056-1980294-0'
    >>> void_sid = '222222'
    >>> # clear rando record but set to void to not allow it to be used
    >>> maternal_rando = MaternalRando.objects.get(
                        subject_identifier=subject_identifier, sid=void_sid)
    >>> if maternal_rando:
    >>>     maternal_rando.subject_identifier='void'
    >>>     maternal_rando.randomization_datetime = None
    >>>     maternal_rando.initials = 'XX'
    >>>     maternal_rando.feeding_choice=None
    >>>     maternal_rando.infant_initials='XX'
    >>>     maternal_rando.haart_status=None
    >>>     maternal_rando.comment = "used in error by%s"%(subject_identifier,)
    >>>     maternal_rando.save()
    >>>     print "OK, SID %s is now void" % (void_sid,)
    >>>     # clear SID from registered subject
    >>>     rs = RegisteredSubject.object.get(
                subject_identifier=subject_identifier, sid=void_sid)
    >>>     rs.sid = None
    >>>     rs.registration_status = None
    >>>     print "OK, RegisteredSubject SID set to None"
    >>> else:
    >>>     print "Error"
    """
    # TODO: Site brought in by the Site Model Mixin.
    sid = models.IntegerField(
        verbose_name='SID',
        unique=True)

    rx = EncryptedCharField(
        verbose_name="Treatment Assignment")

    randomization_datetime = models.DateTimeField(
        verbose_name='Randomization Datetime')

    initials = EncryptedCharField(
        validators=[RegexValidator(
            regex=r'^[A-Z]{2,3}$',
            message=('Ensure initials consist of letters '
                     'only in upper case, no spaces.'))])

    dispensed = models.CharField(
        verbose_name='Dispensed',
        max_length=10,
        default=NO,
        choices=YES_NO,
        help_text='To be confirmed by pharmacy staff only')

    comment = models.TextField(
        max_length=250,
        null=True,
        blank=True,
        help_text="Comment if any manual changes made to rando list")

    delivery_clinic = models.CharField(
        max_length=100,
        verbose_name="Which clinic does the mother plan to deliver at?",
        choices=DELIVERY_HEALTH_FACILITY)

    delivery_clinic_other = models.CharField(
        max_length=100,
        verbose_name="if other delivery clinic, specify...",
        blank=True,
        null=True, )

    def __str__(self):
        return '{}'.format(self.sid, self.subject_identifier)

    def save(self, *args, **kwargs):
        if not self.id:
            randomization_helper = Randomization(self, ValidationError)
            (self.sid, self.rx, self.randomization_datetime,
             self.initials) = randomization_helper.randomize()
        super(MaternalRando, self).save(*args, **kwargs)

    class Meta(CrfModelMixin.Meta):
        app_label = "td_maternal"
        verbose_name = "Maternal Randomization"
        verbose_name_plural = "Maternal Randomization"
        ordering = ('sid',)
        unique_together = ('sid', 'rx')
Пример #26
0
class SubjectLocator(UniqueSubjectIdentifierFieldMixin, SiteModelMixin,
                     SubjectContactFieldsMixin, SearchSlugModelMixin,
                     BaseUuidModel):
    """A model completed by the user to that captures participant
    locator information and permission to contact.
    """

    loc_admin = models.CharField(verbose_name='Administered by', max_length=50)

    first_name = FirstnameField(verbose_name='First Names', max_length=50)

    last_name = LastnameField(verbose_name='Surname', max_length=50)

    initials = EncryptedCharField(validators=[
        RegexValidator(regex=r'^[A-Z]{2,3}$',
                       message=('Ensure initials consist of letters '
                                'only in upper case, no spaces.'))
    ],
                                  null=True,
                                  blank=False)

    loc_date = models.DateField(
        verbose_name='Date Completed',
        default=get_utcnow,
        validators=[date_not_before_study_start, date_not_future])

    may_call = models.CharField(
        max_length=3,
        choices=YES_NO,
        verbose_name=mark_safe(
            'Has the participant given permission <b>to be contacted on this '
            'cell number</b>?'),
        blank=True,
        null=True)

    may_call_alt = models.CharField(
        max_length=3,
        choices=YES_NO,
        verbose_name=mark_safe(
            'Has the participant given permission <b>to be contacted on this '
            'cell number</b>?'),
        blank=True,
        null=True)

    subject_cell_alt_3 = EncryptedCharField(
        verbose_name='Cell number (second alternate)', blank=True, null=True)

    may_call_tel = models.CharField(
        max_length=3,
        choices=YES_NO,
        verbose_name=mark_safe(
            'Has the participant given permission <b>to be contacted on this '
            'telephone number</b>?'),
        blank=True,
        null=True)

    loc_email = models.EmailField(blank=True,
                                  null=True,
                                  help_text='If no email, write None')

    may_contact_email = models.CharField(
        max_length=3,
        choices=YES_NO_NA,
        verbose_name=mark_safe(
            'Has the participant given permission <b>to be contacted by '
            'email</b>?'),
        blank=True,
        null=True)

    loc_village = EncryptedTextField(verbose_name='Home Village',
                                     max_length=500,
                                     help_text='')

    loc_address = EncryptedTextField(
        verbose_name='Physical address with detailed description',
        max_length=500,
        blank=True,
        null=True,
        help_text='')

    may_visit_home = models.CharField(
        max_length=25,
        choices=YES_NO,
        blank=True,
        null=True,
        verbose_name=mark_safe(
            'Has the participant given permission for study '
            'staff <b>to make home visits</b> for follow-up purposes?'))

    idcc_clinic = models.CharField(verbose_name='Name of IDCC Clinic',
                                   max_length=25,
                                   blank=True,
                                   null=True)

    may_contact_idcc = models.CharField(verbose_name=(
        'Has the participant given permission to be contacted, '
        'through their IDCC clinic?, if unable to contact phone numbers'),
                                        max_length=3,
                                        choices=YES_NO_NA,
                                        blank=True,
                                        null=True)

    loc_workplace = models.CharField(verbose_name='Name of workplace',
                                     max_length=25,
                                     blank=True,
                                     null=True,
                                     help_text='(for those who are working)')

    loc_workphone = EncryptedCharField(verbose_name='Work Telephone',
                                       blank=True,
                                       null=True)

    may_contact_work = models.CharField(verbose_name=(
        'Has participant given permission to be contacted at their '
        'workplace?, if unable to contact phone numbers'),
                                        max_length=3,
                                        choices=YES_NO_NA,
                                        blank=True,
                                        null=True)

    loc_kincontact = EncryptedTextField(verbose_name=(
        'Name and contact details of next of kin or any individuals '
        'participant allows us to contact if they can\'t be reached.'
        '(can list multiple people)'),
                                        max_length=500,
                                        blank=True,
                                        null=True)

    may_contact_kin = models.CharField(verbose_name=(
        'Has participant given permission to contact anyone else?'
        ' , if unable to contact phone numbers'),
                                       max_length=3,
                                       choices=YES_NO_NA,
                                       blank=True,
                                       null=True)

    date_followup = models.DateField(verbose_name='Date of follow-up visit')

    initial_call_date = models.DateField(verbose_name='Initial call date',
                                         default=get_utcnow)

    review_locator = models.CharField(
        verbose_name=('Did you review this form with the participant to '
                      'find out if there are any updates?'),
        max_length=3,
        choices=YES_NO)

    history = HistoricalRecords()

    objects = LocatorManager()

    def __str__(self):
        return '{}'.format(self.subject_identifier)

    def natural_key(self):
        return (self.subject_identifier, )

    natural_key.dependencies = ['sites.Site']

    def save(self, *args, **kwargs):
        if not self.initials:
            self.initials = f'{self.first_name[:1]}{self.last_name[:1]}'
        super().save(*args, **kwargs)

    class Meta:
        app_label = 'motheo_call_manager'
        verbose_name = 'Subject Locator'
Пример #27
0
class Plot(MapperModelMixin, PlotIdentifierModelMixin, PlotEnrollmentMixin,
           PlotConfirmationMixin, CreateHouseholdsModelMixin,
           SearchSlugModelMixin, BaseUuidModel):
    """A model created by the system and updated by the user to
    represent a Plot in the community.
    """
    def get_search_slug_fields(self):
        return ['plot_identifier', 'map_area', 'cso_number']

    report_datetime = models.DateTimeField(validators=[datetime_not_future],
                                           default=get_utcnow)

    eligible_members = models.IntegerField(
        verbose_name="Approximate number of age eligible members",
        default=0,
        null=True,
        help_text=(("Provide an approximation of the number of people "
                    "who live in this residence who are age eligible.")))

    cso_number = EncryptedCharField(
        verbose_name="CSO Number",
        blank=True,
        null=True,
        help_text=("provide the CSO number or leave BLANK."))

    time_of_week = models.CharField(verbose_name=(
        'Time of week when most of the eligible members will be available'),
                                    max_length=25,
                                    choices=TIME_OF_WEEK,
                                    blank=True,
                                    null=True)

    time_of_day = models.CharField(verbose_name=(
        'Time of day when most of the eligible members will be available'),
                                   max_length=25,
                                   choices=TIME_OF_DAY,
                                   blank=True,
                                   null=True)

    status = models.CharField(verbose_name='Plot status',
                              max_length=35,
                              choices=PLOT_STATUS,
                              null=True,
                              blank=False)

    description = EncryptedTextField(
        verbose_name="Description of plot/residence",
        max_length=250,
        blank=True,
        null=True)

    comment = EncryptedTextField(verbose_name="Comment",
                                 max_length=250,
                                 blank=True,
                                 null=True)

    accessible = models.BooleanField(default=True, editable=False)

    access_attempts = models.IntegerField(
        default=0,
        help_text=(
            'Number of attempts to access a plot to determine it\'s status.'),
        editable=False)

    objects = PlotManager()

    history = HistoricalRecords()

    def __str__(self):
        return '{} {}'.format(self.location_name or 'undetermined',
                              self.plot_identifier)

    def save(self, *args, **kwargs):
        if self.id and not self.location_name:
            self.location_name = 'plot'
        if self.status == INACCESSIBLE:
            self.accessible = False
        else:
            if self.id:
                PlotLogEntry = django_apps.get_model(
                    *'plot.plotlogentry'.split('.'))
                try:
                    PlotLogEntry.objects.get(plot_log__plot__pk=self.id)
                except PlotLogEntry.DoesNotExist:
                    self.accessible = True
                except MultipleObjectsReturned:
                    pass
        super().save(*args, **kwargs)

    def natural_key(self):
        return (self.plot_identifier, )

    def common_clean(self):
        """Asserts the plot map_area is a valid map_area and that
        an enrolled plot cannot be unconfirmed.
        """
        if self.map_area not in site_mappers.map_areas:
            raise MapperError(
                f'Invalid map area. Got \'{self.map_area}\'. Site mapper expects one '
                f'of map_areas={site_mappers.map_areas}.')
        elif self.id:
            try:
                self.get_confirmed()
            except MapperError:
                if self.enrolled:
                    raise PlotEnrollmentError(
                        'Plot is enrolled and may not be unconfirmed')
        super().common_clean()

    @property
    def common_clean_exceptions(self):
        return (super().common_clean_exceptions +
                [PlotEnrollmentError, MapperError])

    @property
    def identifier_segment(self):
        return self.plot_identifier[:-3]

    @property
    def community(self):
        return self.map_area

    class Meta(DeviceModelMixin.Meta):
        ordering = [
            '-plot_identifier',
        ]
        unique_together = (('gps_target_lat', 'gps_target_lon'), )
        household_model = 'household.household'
        device_permissions = DevicePermissions(
            PlotDeviceAddPermission(device_roles=[CENTRAL_SERVER]))
Пример #28
0
class InfantBirth(UniqueSubjectIdentifierFieldMixin, SiteModelMixin,
                  SearchSlugModelMixin, CryptoMixin, BaseUuidModel):
    """ A model completed by the user on the infant's birth. """

    report_datetime = models.DateTimeField(
        verbose_name="Date and Time infant enrolled",
        validators=[
            datetime_not_future,
        ],
        help_text='')

    first_name = FirstnameField(
        max_length=25,
        verbose_name="Infant's first name",
        help_text="If infant name is unknown or not yet determined, "
        "use Baby + birth order + mother's last name, e.g. 'Baby1Malane'")

    initials = EncryptedCharField(validators=[
        RegexValidator(regex=r'^[A-Z]{2,3}$',
                       message=('Ensure initials consist of letters '
                                'only in upper case, no spaces.'))
    ], )

    dob = models.DateField(verbose_name='Date of Birth',
                           help_text="Must match labour and delivery report.",
                           validators=[
                               date_not_future,
                           ])

    gender = models.CharField(max_length=10, choices=GENDER_UNDETERMINED)

    def __str__(self):
        return f'{self.first_name}, {self.initials}, {self.gender}'

    def save(self, *args, **kwargs):
        self.consent_version = self.get_consent_version()
        super(InfantBirth, 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.')

    @property
    def schedule_name(self):
        """Return a visit schedule name.
        """
        schedule_name = None
        subject_consent = django_apps.get_model(
            'td_maternal.subjectconsent').objects.filter(
                subject_identifier=self.registered_subject.relative_identifier
            ).order_by('version').last()
        if subject_consent.version == '1':
            schedule_name = 'infant_schedule_v1'
        elif subject_consent.version == '3':
            schedule_name = 'infant_schedule_v3'
        return schedule_name

    @property
    def registered_subject(self):
        """Return infant registered subject.
        """
        registered_subject_cls = django_apps.get_model(
            'edc_registration.registeredsubject')
        try:
            registered_subject = registered_subject_cls.objects.get(
                subject_identifier=self.subject_identifier)
        except registered_subject_cls.DoesNotExist:
            raise ValidationError(
                f'Registered Subject is missing for {self.subject_identifier}')
        else:
            return registered_subject

    class Meta:
        app_label = 'td_infant'
        verbose_name = "Infant Birth"
Пример #29
0
class PartOneFieldsModelMixin(models.Model):

    screening_consent = models.CharField(
        verbose_name=("Has the subject given his/her verbal consent "
                      "to be screened for the META trial?"),
        max_length=15,
        choices=YES_NO,
    )

    selection_method = models.CharField(
        verbose_name="How was the patient selected from the outpatients CTC?",
        max_length=25,
        choices=SELECTION_METHOD,
    )

    hospital_identifier = EncryptedCharField(unique=True, blank=False)

    initials = EncryptedCharField(
        validators=[
            RegexValidator("[A-Z]{1,3}", "Invalid format"),
            MinLengthValidator(2),
            MaxLengthValidator(3),
        ],
        help_text="Use UPPERCASE letters only. May be 2 or 3 letters.",
        blank=False,
    )

    ethnicity = models.CharField(max_length=15,
                                 choices=ETHNICITY,
                                 help_text="Used for eGFR calculation")

    hiv_pos = models.CharField(verbose_name="Is the patient HIV positive",
                               max_length=15,
                               choices=YES_NO)

    art_six_months = models.CharField(
        verbose_name=
        ("Has the patient been on anti-retroviral therapy for at least 6 months"
         ),
        max_length=15,
        choices=YES_NO_NA,
    )

    on_rx_stable = models.CharField(
        verbose_name="Is the patient considered to be stable on treatment ",
        max_length=15,
        choices=YES_NO_NA,
        help_text="in regular attendance for care",
    )

    lives_nearby = models.CharField(
        verbose_name=
        ("Is the patient living within the catchment population of the facility"
         ),
        max_length=15,
        choices=YES_NO,
    )

    staying_nearby = models.CharField(
        verbose_name=(
            "Is the patient planning to remain in the catchment area "
            "for at least 6 months"),
        max_length=15,
        choices=YES_NO,
    )

    pregnant = models.CharField(verbose_name="Is the patient pregnant?",
                                max_length=15,
                                choices=PREG_YES_NO_NA)

    continue_part_two = models.CharField(
        verbose_name=mark_safe(
            "Continue with <U>part two</U> of the screening process?"),
        max_length=15,
        choices=YESDEFAULT_NO,
        default=YES,
        help_text=mark_safe(
            "<B>Important</B>: This response will be be automatically "
            "set to YES if:<BR><BR>"
            "- the participant meets the eligibility criteria for part one, or;<BR><BR>"
            "- the eligibility criteria for part two is already complete.<BR>"
        ),
    )

    class Meta:
        abstract = True
Пример #30
0
class SubjectLocator(LocatorModelMixin, RequiresConsentFieldsModelMixin,
                     ActionModelMixin, SiteModelMixin, BaseUuidModel):
    """A model completed by the user to that captures participant
    locator information and permission to contact.
    """

    action_name = SUBJECT_LOCATOR_ACTION

    tracking_identifier_prefix = 'SL'

    site = models.ForeignKey(Site,
                             on_delete=models.PROTECT,
                             null=True,
                             editable=False,
                             related_name='subject_locator_site')

    date_signed = models.DateField(
        verbose_name="Date Locator Form signed ",
        default=timezone.now,
        help_text="",
    )

    local_clinic = models.CharField(
        verbose_name=("When you stay in the village, what clinic/health "
                      "post do you normally go to?"),
        max_length=75,
        validators=[
            RegexValidator(
                regex=r'^[0-9]{2}[-][0-9]{1}[-][0-9]{2}$',
                message='The correct clinic code format is XX-X-XX'),
        ],
        help_text="Please give clinic code.",
    )
    home_village = models.CharField(
        verbose_name=("Where is your home village?"),
        max_length=75,
        help_text="",
    )

    has_alt_contact = models.CharField(
        max_length=25,
        choices=YES_NO,
        verbose_name=(
            "If we are unable to contact the person indicated above, is "
            "there another individual (including next of kin) with whom "
            "the study team can get in contact with?"),
        help_text="",
    )

    alt_contact_name = EncryptedCharField(
        max_length=35,
        verbose_name="Full Name of the responsible person",
        help_text="include firstname and surname",
        blank=True,
        null=True,
    )

    alt_contact_rel = EncryptedCharField(
        max_length=35,
        verbose_name="Relationship to participant",
        blank=True,
        null=True,
        help_text="",
    )
    alt_contact_cell = EncryptedCharField(
        max_length=8,
        verbose_name="Cell number",
        validators=[
            CellNumber,
        ],
        help_text="",
        blank=True,
        null=True,
    )

    other_alt_contact_cell = EncryptedCharField(
        max_length=8,
        verbose_name="Cell number (alternate)",
        validators=[
            CellNumber,
        ],
        help_text="",
        blank=True,
        null=True,
    )

    alt_contact_tel = EncryptedCharField(
        max_length=8,
        verbose_name="Telephone number",
        validators=[
            TelephoneNumber,
        ],
        help_text="",
        blank=True,
        null=True,
    )

    history = HistoricalRecords()

    on_site = CurrentSiteManager()

    objects = LocatorManager()

    history = HistoricalRecords()

    def __str__(self):
        return '{}'.format(self.subject_identifier)

    def natural_key(self):
        return (self.subject_identifier, )

    natural_key.dependencies = ['sites.Site']

    class Meta:
        verbose_name = 'Subject Locator'