class LocatorLogEntry(BaseUuidModel):
    """A model completed by the user to track an RA\'s attempts to
    confirm a Plot.
    """

    locator_log = models.ForeignKey(
        LocatorLog,
        on_delete=PROTECT,)

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

    log_status = models.CharField(
        verbose_name='What is the status of the locator?',
        max_length=25,
        choices=LOCATOR_LOG_STATUS)

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

    date_created = models.DateField(default=timezone.now)

    history = HistoricalRecords()

    def __str__(self):
        return f'{self.locator_log.maternal_dataset.study_maternal_identifier} ({self.report_datetime})'

    class Meta:
        unique_together = ('locator_log', 'report_datetime')
        ordering = ('report_datetime', )
Exemplo n.º 2
0
class SubjectTransferModelMixin(
        UniqueSubjectIdentifierFieldMixin,
        SiteModelMixin,
        ActionModelMixin,
        TrackingModelMixin,
        models.Model,
):

    action_name = SUBJECT_TRANSFER_ACTION

    tracking_identifier_prefix = "TR"

    report_datetime = models.DateTimeField(verbose_name="Report Date and Time",
                                           default=get_utcnow)

    transfer_date = models.DateField(verbose_name="Transfer date",
                                     default=get_utcnow)

    initiated_by = models.CharField(
        verbose_name="Who initiated the transfer request",
        max_length=25,
        choices=TRANSFER_INITIATORS,
    )

    initiated_by_other = edc_models.OtherCharField()

    transfer_reason = models.ManyToManyField(
        f"{settings.LIST_MODEL_APP_LABEL}.TransferReasons",
        verbose_name="Reason for transfer",
    )

    transfer_reason_other = edc_models.OtherCharField()

    may_return = models.CharField(
        verbose_name=("Is the participant likely to transfer back before "
                      "the end of their stay in the trial?"),
        max_length=15,
        choices=YES_NO_UNSURE,
    )

    may_contact = models.CharField(
        verbose_name=
        "Is the participant willing to be contacted at the end of the study?",
        max_length=15,
        choices=YES_NO,
    )

    comment = EncryptedTextField(verbose_name="Additional Comments")

    def natural_key(self):
        return tuple([self.subject_identifier])

    class Meta:
        abstract = True
        verbose_name = "Subject Transfer"
        verbose_name_plural = "Subject Transfers"
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')
Exemplo n.º 4
0
class RandomizationList(BaseUuidModel):

    sid = models.IntegerField(
        verbose_name='SID',
        unique=True)

    drug_assignment = EncryptedTextField(
        verbose_name="Treatment Assignment")

    class Meta:
        app_label = 'td_rando'
        verbose_name = 'RandomizationList'
Exemplo n.º 5
0
class MovedMember(HouseholdMemberModelMixin, BaseUuidModel):
    """A model completed by the user to indicate a subject has
    moved from the household and or community.
    """

    moved_household = models.CharField(
        max_length=7,
        verbose_name=
        'Has the participant moved out of the household where last seen',
        choices=YES_NO_UNKNOWN,
        null=True,
        blank=False,
        help_text="")

    moved_community = models.CharField(
        max_length=7,
        verbose_name='Has the participant moved out of the community',
        choices=YES_NO_UNKNOWN,
        null=True,
        blank=False,
        help_text="")

    new_community = models.CharField(
        max_length=50,
        verbose_name=
        'If the participant has moved, provide the name of the new community',
        null=True,
        blank=True,
        help_text=
        "If moved out of the community, provide a new community name or \'UNKNOWN\'"
    )

    update_locator = models.CharField(
        max_length=7,
        verbose_name='Has the locator information changed',
        choices=YES_NO_UNKNOWN,
        null=True,
        blank=False,
        help_text=('If YES, please enter the changed information '
                   'the locator form'))

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

    objects = MemberEntryManager()

    history = HistoricalRecords()

    class Meta(HouseholdMemberModelMixin.Meta):
        app_label = 'member'
        unique_together = ('household_member', 'report_datetime')
Exemplo n.º 6
0
class HouseholdLogEntry(SurveyScheduleModelMixin, BaseUuidModel):
    """A model completed by the user each time the household is visited."""

    household_log = models.ForeignKey(HouseholdLog, on_delete=models.PROTECT)

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

    household_status = models.CharField(verbose_name='Household Status',
                                        max_length=50,
                                        choices=HOUSEHOLD_LOG_STATUS,
                                        null=True,
                                        blank=False)

    next_appt_datetime = models.DateTimeField(
        verbose_name="[RA]: When may we visit again?",
        help_text="The date and time to visit household again.",
        validators=[datetime_is_future],
        null=True,
        blank=True)

    next_appt_datetime_source = models.CharField(
        verbose_name="Source",
        max_length=25,
        choices=NEXT_APPOINTMENT_SOURCE,
        help_text='source of information for the appointment date',
        null=True,
        blank=True)

    comment = EncryptedTextField(null=True, blank=True)

    objects = LogEntryManager()

    history = HistoricalRecords()

    def save(self, *args, **kwargs):
        self.survey_schedule = self.household_log.household_structure.survey_schedule_object.field_value
        super().save(*args, **kwargs)

    def natural_key(self):
        return (self.report_datetime, ) + self.household_log.natural_key()

    natural_key.dependencies = ['household.household_log']

    def __str__(self):
        return '{} on {}'.format(
            self.household_status,
            self.report_datetime.strftime('%Y-%m-%d %H:%M%Z'))

    class Meta:
        unique_together = ('household_log', 'report_datetime')
        ordering = ('report_datetime', )
class DeathReportModelMixin(models.Model):

    death_location_type = models.CharField(
        verbose_name="Where did the participant die?",
        max_length=50,
        choices=DEATH_LOCATIONS,
    )

    death_location_name = models.CharField(
        verbose_name=
        ("If death occurred at hospital / clinic, please give name of the facility"
         ),
        max_length=150,
        null=True,
        blank=True,
    )

    informant_contacts = EncryptedTextField(null=True, blank=True)

    informant_relationship = models.CharField(
        max_length=50,
        choices=INFORMANT_RELATIONSHIP,
        verbose_name="Informants relationship to the participant?",
    )

    other_informant_relationship = OtherCharField()

    death_certificate = models.CharField(
        verbose_name="Is a death certificate is available?",
        max_length=15,
        choices=YES_NO,
    )

    secondary_cause_of_death = models.ForeignKey(
        CauseOfDeath,
        on_delete=models.PROTECT,
        related_name="secondary_cause_of_death",
        verbose_name="Secondary cause of death",
        help_text=("Secondary cause of death in the opinion of the "
                   "local study doctor and local PI"),
    )

    secondary_cause_of_death_other = models.CharField(
        max_length=100,
        blank=True,
        null=True,
        verbose_name='If "Other" above, please specify',
    )

    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 Household(HouseholdIdentifierModelMixin, SearchSlugModelMixin, BaseUuidModel):
    """A system model that represents the household asset.
    See also HouseholdStructure.
    """

    plot = models.ForeignKey(Plot, on_delete=models.PROTECT)

    report_datetime = models.DateTimeField(
        verbose_name='Report Date/Time',
        default=get_utcnow)

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

    # updated by subject_consent save method
    enrolled = models.BooleanField(
        default=False,
        editable=False,
        help_text=('Set to true if one member is consented. '
                   'Updated by Household_structure post_save.'))

    enrolled_datetime = models.DateTimeField(
        null=True,
        editable=False,
        help_text=('datetime that household is enrolled. '
                   'Updated by Household_structure post_save.'))

    objects = Manager()

    history = HistoricalRecords()

    def __str__(self):
        return self.household_identifier

    def save(self, *args, **kwargs):
        if not self.id:
            # plots create households, so use plot.report_datetime.
            self.report_datetime = self.plot.report_datetime
        super().save(*args, **kwargs)

    def natural_key(self):
        return (self.household_identifier, ) + self.plot.natural_key()
    natural_key.dependencies = ['plot.plot']

    class Meta:
        ordering = ['-household_identifier', ]
class RandomizationList(RandomizationListModelMixin, BaseUuidModel):

    randomizer_cls = site_randomizers.get("ambition")

    assignment = EncryptedTextField(choices=((SINGLE_DOSE, SINGLE_DOSE_NAME),
                                             (CONTROL, CONTROL_NAME)), )

    allocation = EncryptedTextField(verbose_name="Original integer allocation",
                                    null=True)

    @property
    def assignment_description(self):
        if self.assignment == CONTROL:
            assignment_description = CONTROL_NAME
        elif self.assignment == SINGLE_DOSE:
            assignment_description = SINGLE_DOSE_NAME
        else:
            raise RandomizationError(
                f"Invalid assignment. Expected one of [{CONTROL}, {SINGLE_DOSE}]. "
                f"Got `{self.assignment}`")
        return assignment_description

    class Meta(RandomizationListModelMixin.Meta):
        pass
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
Exemplo n.º 12
0
class PlotLogEntry(BaseUuidModel):
    """A model completed by the user to track an RA\'s attempts to
    confirm a Plot.
    """

    plot_log = models.ForeignKey(PlotLog, on_delete=PROTECT)

    report_datetime = models.DateTimeField(
        verbose_name="Report date",
        validators=[datetime_not_future],
        # TODO: get this validator to work
        # validators=[datetime_not_future, date_in_survey_for_map_area],
        default=get_utcnow)

    report_date = models.DateField(
        editable=False,
        help_text='date value of report_datetime for unique constraint')

    log_status = models.CharField(
        verbose_name='What is the status of this plot?',
        max_length=25,
        choices=PLOT_LOG_STATUS)

    reason = models.CharField(
        verbose_name='If inaccessible, please indicate the reason.',
        max_length=25,
        blank=True,
        null=True,
        choices=INACCESSIBILITY_REASONS)

    reason_other = models.CharField(
        verbose_name='If Other, specify',
        max_length=100,
        blank=True,
        null=True)

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

    objects = PlotLogEntryManager()

    history = HistoricalRecords()

    def save(self, *args, **kwargs):
        self.report_date = arrow.Arrow.fromdatetime(
            self.report_datetime, self.report_datetime.tzinfo).to('utc').date()
        super().save(*args, **kwargs)

    def natural_key(self):
        return (self.report_datetime, ) + self.plot_log.natural_key()
    natural_key.dependencies = ['plot.plot_log']

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

    class Meta:
        unique_together = ('plot_log', 'report_datetime')
        ordering = ('report_datetime', )
Exemplo n.º 13
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',
                                      validators=[
                                          CellNumber,
                                      ],
                                      blank=True,
                                      null=True,
                                      help_text='')

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

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

    subject_phone_alt = EncryptedCharField(
        verbose_name='Telephone (alternate)',
        validators=[
            TelephoneNumber,
        ],
        blank=True,
        null=True)

    class Meta:
        abstract = True
Exemplo n.º 14
0
class ConsentModelMixin(VerificationFieldsMixin, models.Model):

    """Mixin for a Consent model class such as SubjectConsent.

    Declare with edc_identifier's NonUniqueSubjectIdentifierModelMixin
    """

    consent_helper_cls = ConsentHelper

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

    report_datetime = models.DateTimeField(null=True, editable=False)

    version = models.CharField(
        verbose_name="Consent version",
        max_length=10,
        help_text="See 'Consent Type' for consent versions by period.",
        editable=False,
    )

    updates_versions = models.BooleanField(default=False)

    sid = models.CharField(
        verbose_name="SID",
        max_length=15,
        null=True,
        blank=True,
        editable=False,
        help_text="Used for randomization against a prepared rando-list.",
    )

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

    dm_comment = models.CharField(
        verbose_name="Data Management comment",
        max_length=150,
        null=True,
        editable=False,
        help_text="see also edc.data manager.",
    )

    consent_identifier = models.UUIDField(
        default=uuid4,
        editable=False,
        help_text="A unique identifier for this consent instance",
    )

    objects = ObjectConsentManager()

    consent = ConsentManager()

    on_site = CurrentSiteManager()

    def __str__(self):
        return f"{self.subject_identifier} v{self.version}"

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

    def save(self, *args, **kwargs):
        self.report_datetime = self.consent_datetime
        consent_helper = self.consent_helper_cls(
            model_cls=self.__class__, update_previous=True, **self.__dict__
        )
        self.version = consent_helper.version
        self.updates_versions = True if consent_helper.updates_versions else False
        super().save(*args, **kwargs)

    @property
    def age_at_consent(self):
        """Returns a relativedelta.
        """
        return age(self.dob, self.consent_datetime)

    @property
    def formatted_age_at_consent(self):
        """Returns a string representation.
        """
        return formatted_age(self.dob, self.consent_datetime)

    class Meta:
        abstract = True
        consent_group = None
        get_latest_by = "consent_datetime"
        unique_together = (
            ("first_name", "dob", "initials", "version"),
            ("subject_identifier", "version"),
        )
        ordering = ("created",)

        indexes = [
            models.Index(
                fields=[
                    "subject_identifier",
                    "first_name",
                    "dob",
                    "initials",
                    "version",
                ]
            )
        ]
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
Exemplo n.º 16
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'
Exemplo n.º 17
0
class HomeVisitAttempt(BaseUuidModel):

    home_visit = models.ForeignKey(
        HomeVisit,
        on_delete=PROTECT)

    contact_attempted = models.CharField(
        verbose_name='Was a home visit attempt made?',
        choices=YES_NO,
        max_length=3)

    contact_staff = FirstnameField(
        verbose_name='Name(s) of staff member who visited the participant',
        blank=True,
        null=True)

    contact_date = models.DateField(
        verbose_name='Date of home visit attempt',
        validators=[date_not_future, ],
        blank=True,
        null=True)

    contact_loc = EncryptedTextField(
        verbose_name='Which address was used for contact attempt?',
        max_length=500,
        help_text='Provide a detailed description of the physical address.',
        blank=True,
        null=True)

    contact_outcome = models.TextField(
        verbose_name='What was the outcome of the in person visit.',
        max_length=500,
        null=True,
        blank=True)

    appt = models.CharField(
        verbose_name='Is the participant willing to schedule an appointment',
        max_length=7,
        choices=YES_NO,
        null=True,
        blank=True)

    appt_date = models.DateField(
        verbose_name='Appointment Date',
        validators=[date_is_future],
        null=True,
        blank=True,
        help_text='This can only come from the participant.')

    offstudy = models.CharField(
        verbose_name='Is the participant going offstudy?',
        choices=YES_NO,
        max_length=3,
        blank=True,
        null=True)

    comment = models.TextField(
        verbose_name='Additional Comments',
        blank=True,
        null=True)

    class Meta:
        app_label = 'motheo_call_manager'
Exemplo n.º 18
0
class MoccaRegisterContact(SiteModelMixin, BaseUuidModel):

    mocca_register = models.ForeignKey(MoccaRegister, on_delete=models.PROTECT)

    report_datetime = models.DateTimeField(default=get_utcnow)

    answered = models.CharField(max_length=15,
                                choices=YES_NO,
                                null=True,
                                blank=False)

    respondent = models.CharField(max_length=15,
                                  choices=RESPONDENT_CHOICES,
                                  default=NOT_APPLICABLE)

    survival_status = models.CharField(
        max_length=15,
        choices=ALIVE_DEAD_UNKNOWN_NA,
        default=NOT_APPLICABLE,
    )

    death_date = models.DateField(verbose_name="Date of death",
                                  null=True,
                                  blank=True)

    willing_to_attend = models.CharField(max_length=15,
                                         choices=YES_NO_UNSURE_NA,
                                         default=NOT_APPLICABLE)

    icc = models.CharField(
        verbose_name=
        "Does the patient currently receive regular integrated care",
        max_length=25,
        choices=YES_NO_UNSURE_NA,
        null=True,
        blank=False,
        help_text="Either at this facility or elsewhere",
    )

    next_appt_date = models.DateField(verbose_name="Next Appt.",
                                      null=True,
                                      blank=True)

    call_again = models.CharField(verbose_name="Call again?",
                                  max_length=15,
                                  choices=YES_NO)

    comment = EncryptedTextField(verbose_name="Note", null=True, blank=True)

    on_site = CurrentSiteManager()
    objects = Manager()
    history = HistoricalRecords()

    def __str__(self):
        return str(self.mocca_register)

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

    natural_key.dependencies = [
        "sites.Site",
        "mocca_screening.MoccaRegister",
    ]

    class Meta:
        verbose_name = "MOCCA Patient Register Contact"
        verbose_name_plural = "MOCCA Patient Register Contacts"
        ordering = ["report_datetime"]
Exemplo n.º 19
0
class ObjectHistory(ExportTrackingFieldsModelMixin, BaseUuidModel):

    model = models.CharField(max_length=64)

    tx_pk = models.UUIDField()

    tx = EncryptedTextField()

    exported_datetime = models.DateTimeField()

    timestamp = models.CharField(max_length=50, null=True, db_index=True)

    status = models.CharField(
        max_length=15,
        default=NEW,
        choices=(
            (NEW, "New"),
            (EXPORTED, "Exported"),
            (CLOSED, "Closed"),
            (CANCELLED, "Cancelled"),
        ),
        help_text="exported by export_transactions, closed by import_receipts",
    )

    received = models.BooleanField(default=False, help_text="True if ACK received")

    received_datetime = models.DateTimeField(null=True, help_text="date ACK received")

    is_ignored = models.BooleanField(default=False, help_text="Ignore if update")

    is_error = models.BooleanField(default=False)

    objects = ObjectHistoryManager()

    history = HistoricalRecords()

    def __str__(self):
        return f"{self.model_name} {self.status} {self.export_uuid}"

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

    def render(self):
        url = reverse(
            "view_transaction_url",
            kwargs={
                "app_label": self._meta.app_label,
                "model_name": self._meta.model_name.lower(),
                "pk": self.pk,
            },
        )
        ret = (
            f'<a href="{url}" class="add-another" id="add_id_report" onclick="return '
            'showAddAnotherPopup(this);"> <img src="/static/admin/img/icon_addlink.gif" '
            'width="10" height="10" alt="View transaction"/></a>'
        )
        return ret

    render.allow_tags = True

    class Meta:
        ordering = ("-timestamp",)
Exemplo n.º 20
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]))
Exemplo n.º 21
0
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')
Exemplo n.º 22
0
class MoccaRegister(SiteModelMixin, BaseUuidModel):

    report_datetime = models.DateTimeField(default=get_utcnow)

    screening_identifier = models.CharField(
        verbose_name="MOCCA (ext) screening identifier",
        max_length=15,
        null=True,
    )

    mocca_screening_identifier = models.CharField(
        verbose_name="MOCCA (original) screening identifier",
        max_length=15,
        null=True,
        blank=True,
        help_text="If known",
    )

    mocca_study_identifier = models.CharField(
        verbose_name="MOCCA (original) study identifier",
        max_length=25,
        validators=[
            RegexValidator(
                r"0[0-9]{1}\-0[0-9]{3}|[0-9]{6}",
                "Invalid format. Expected 12-3456 for UG, 123456 for TZ",
            )
        ],
        help_text="Format must match original identifier. e.g. 12-3456 for UG, 123456 for TZ",
    )

    mocca_country = models.CharField(
        max_length=25, choices=(("uganda", "Uganda"), ("tanzania", "Tanzania"))
    )

    mocca_site = models.ForeignKey(
        MoccaOriginalSites,
        on_delete=models.PROTECT,
        limit_choices_to=get_mocca_site_limited_to,
    )

    first_name = FirstnameField(null=True)

    last_name = LastnameField(null=True)

    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.",
        null=True,
        blank=False,
    )
    gender = models.CharField(max_length=10, choices=GENDER, null=True, blank=False)

    age_in_years = models.IntegerField(
        validators=[MinValueValidator(18), MaxValueValidator(110)],
        null=True,
        blank=False,
    )

    birth_year = models.IntegerField(
        validators=[MinValueValidator(1900), MaxValueValidator(2002)],
        null=True,
        blank=False,
    )
    dob = models.DateField(null=True, blank=True)

    survival_status = models.CharField(
        max_length=25, choices=ALIVE_DEAD_UNKNOWN, default=UNKNOWN
    )

    contact_attempts = models.IntegerField(default=0, help_text="auto-updated")

    call = models.CharField(verbose_name="Call?", max_length=15, choices=YES_NO, default=YES)

    subject_present = models.CharField(
        verbose_name="Patient is present. Screen now instead of calling?",
        max_length=15,
        choices=YES_NO,
        default=NO,
        help_text="Only select 'yes' if the patient is present in the clinic now.",
    )

    date_last_called = models.DateField(null=True, help_text="auto-updated")

    next_appt_date = models.DateField(
        verbose_name="Appt", null=True, blank=True, help_text="auto-updated"
    )

    notes = EncryptedTextField(verbose_name="General notes", null=True, blank=True)

    tel_one = EncryptedCharField("Tel/Mobile(1)", max_length=15, null=True)
    tel_two = EncryptedCharField("Tel/Mobile(2)", max_length=15, null=True)
    tel_three = EncryptedCharField("Tel/Mobile(3)", max_length=15, null=True)
    best_tel = models.CharField(
        verbose_name="Prefered Telephone / Mobile",
        max_length=15,
        choices=TEL_CHOICES,
        null=True,
        blank=True,
        help_text="If any, select the best telephone/mobile from above",
    )

    on_site = CurrentSiteManager()
    objects = Manager()
    history = HistoricalRecords()

    def __str__(self):
        return (
            f"{self.mocca_study_identifier} {self.initials} {self.age_in_years} {self.gender}"
        )

    def save(self, *args, **kwargs):
        if self.screening_identifier:
            self.call = NO
        super().save(*args, **kwargs)

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

    natural_key.dependencies = [
        "sites.Site",
        "mocca_lists.MoccaOriginalSites",
    ]

    class Meta(BaseUuidModel.Meta):
        verbose_name = "MOCCA Patient Register"
        verbose_name_plural = "MOCCA Patient Register"
        ordering = ["mocca_country", "mocca_site"]
        indexes = [
            Index(fields=["mocca_country", "mocca_site"]),
            Index(fields=["mocca_study_identifier", "initials", "gender"]),
        ]
        constraints = [
            UniqueConstraint(
                fields=["mocca_screening_identifier"],
                name="unique_mocca_screening_identifier",
            ),
            UniqueConstraint(
                fields=["mocca_study_identifier"], name="unique_mocca_study_identifier"
            ),
        ]