Esempio n. 1
0
class SubjectRefusalScreening(SiteModelMixin, BaseUuidModel):
    mocca_register = models.OneToOneField(
        "mocca_screening.moccaregister",
        on_delete=models.PROTECT,
        null=True,
        verbose_name="MOCCA (original) register details",
    )

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

    reason = models.CharField(
        verbose_name="Reason for refusal to screen",
        max_length=25,
        choices=REFUSAL_REASONS_SCREENING,
    )

    other_reason = OtherCharField()

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

    on_site = CurrentSiteManager()

    objects = Manager()

    history = HistoricalRecords()

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

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

    @staticmethod
    def get_search_slug_fields():
        return ["screening_identifier"]

    class Meta(BaseUuidModel.Meta):
        verbose_name = "Refusal to Screen"
        verbose_name_plural = "Refusal to Screen"
Esempio n. 2
0
class UnblindingReview(
        NonUniqueSubjectIdentifierFieldMixin,
        SiteModelMixin,
        ActionModelMixin,
        TrackingModelMixin,
        BaseUuidModel,
):

    action_name = UNBLINDING_REVIEW_ACTION

    tracking_identifier_prefix = "UR"

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

    reviewer = models.ForeignKey(
        UnblindingReviewerUser,
        related_name="+",
        on_delete=models.PROTECT,
        verbose_name="Unblinding request reviewed by",
        help_text="Select a name from the list",
    )

    approved = models.CharField(max_length=15, default=TBD, choices=YES_NO_TBD)

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

    on_site = CurrentSiteManager()

    objects = SubjectIdentifierManager()

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

    class Meta(BaseUuidModel.Meta):
        verbose_name = "Unblinding Review"
        verbose_name_plural = "Unblinding Reviews"
        indexes = [
            models.Index(fields=[
                "subject_identifier", "action_identifier", "site", "id"
            ])
        ]
Esempio n. 3
0
class IncomingTransaction(TransactionModelMixin, SiteModelMixin,
                          BaseUuidModel):
    """ Transactions received from a remote host.
    """

    site = models.ForeignKey(Site,
                             on_delete=models.CASCADE,
                             null=True,
                             editable=False)

    is_consumed = models.BooleanField(default=False)

    is_self = models.BooleanField(default=False)

    on_site = CurrentSiteManager()

    objects = models.Manager()

    class Meta:
        ordering = ["timestamp"]
Esempio n. 4
0
class Appointment(AppointmentModelMixin, SiteModelMixin, BaseUuidModel):
    on_site = CurrentSiteManager()

    objects = AppointmentManager()

    history = HistoricalRecords()

    def natural_key(self) -> tuple:
        return (
            self.subject_identifier,
            self.visit_schedule_name,
            self.schedule_name,
            self.visit_code,
            self.visit_code_sequence,
        )

    # noinspection PyTypeHints
    natural_key.dependencies = ["sites.Site"]  # type: ignore

    class Meta(AppointmentModelMixin.Meta, BaseUuidModel.Meta):
        pass
Esempio n. 5
0
class SubjectRefusal(SiteModelMixin, BaseUuidModel):
    screening_identifier = models.CharField(max_length=50, unique=True)

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

    reason = models.CharField(
        verbose_name="Reason for refusal to join",
        max_length=25,
        choices=REFUSAL_REASONS,
    )

    other_reason = OtherCharField()

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

    on_site = CurrentSiteManager()

    objects = SubjectRefusalManager()

    history = HistoricalRecords()

    def __str__(self):
        return self.screening_identifier

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

    @staticmethod
    def get_search_slug_fields():
        return ["screening_identifier"]

    class Meta(BaseUuidModel.Meta):
        verbose_name = "Refusal to Consent"
        verbose_name_plural = "Refusal to Consent"
Esempio n. 6
0
class CareStatus(SiteModelMixin, CareModelMixin, BaseUuidModel):

    willing_to_answer = models.CharField(
        verbose_name=
        "Is the patient willing to provide information about their care status?",
        max_length=15,
        choices=YES_NO,
        default=YES,
    )

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

    mocca_register = models.OneToOneField(
        "mocca_screening.moccaregister",
        on_delete=models.PROTECT,
        null=True,
        verbose_name="MOCCA (original) register details",
    )

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

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

    natural_key.dependencies = [
        "sites.Site",
        "mocca_screen ing.MoccaRegister",
    ]

    class Meta(BaseUuidModel.Meta):
        verbose_name = "Care Status"
        verbose_name_plural = "Care Status"
Esempio n. 7
0
class Order(SiteModelMixin, edc_models.BaseUuidModel):

    aliquot = models.ForeignKey(Aliquot, on_delete=PROTECT)

    order_identifier = models.CharField(max_length=25, editable=False, unique=True)

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

    panel_name = models.CharField(max_length=25)

    on_site = CurrentSiteManager()

    objects = OrderManager()

    history = edc_models.HistoricalRecords()

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

    natural_key.dependencies = ["edc_lab.aliquot", "sites.Site"]

    class Meta(edc_models.BaseUuidModel.Meta):
        verbose_name = "Order"
Esempio n. 8
0
class Manifest(ManifestModelMixin, SearchSlugModelMixin, edc_models.BaseUuidModel):
    def get_search_slug_fields(self):
        return [
            "manifest_identifier",
            "human_readable_identifier",
            "shipper.name",
            "consignee.name",
        ]

    consignee = models.ForeignKey(Consignee, verbose_name="Consignee", on_delete=PROTECT)

    shipper = models.ForeignKey(Shipper, verbose_name="Shipper/Exporter", on_delete=PROTECT)

    on_site = CurrentSiteManager()

    objects = Manager()

    history = edc_models.HistoricalRecords()

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

    natural_key.dependencies = ["edc_lab.shipper", "edc_lab.consignee"]

    def __str__(self):
        return "{} created on {} by {}".format(
            self.manifest_identifier,
            self.manifest_datetime.strftime("%Y-%m-%d"),
            self.user_created,
        )

    @property
    def count(self):
        return self.manifestitem_set.all().count()

    class Meta(ManifestModelMixin.Meta, edc_models.BaseUuidModel.Meta):
        verbose_name = "Manifest"
Esempio n. 9
0
class DailyClosingLog(SiteModelMixin, BaseUuidModel):

    site = models.ForeignKey(
        Site,
        on_delete=models.PROTECT,
        null=True,
        related_name="+",
        blank=False,
    )

    log_date = models.DateField(verbose_name="Clinic date", default=get_utcnow)

    clinic_services = models.CharField(
        verbose_name="Which services are being offered at the clinic today?",
        max_length=25,
        choices=CLINIC_DAYS,
    )

    attended = models.IntegerField(
        verbose_name="Total number of patients who attended the clinic today",
        validators=[MinValueValidator(0)],
    )

    selection_method = models.CharField(
        verbose_name="How were patients selected to be approached?",
        max_length=25,
        choices=SELECTION_METHOD,
    )

    approached = models.IntegerField(
        verbose_name=
        "Of those who attended, how many were approached by the study team",
        validators=[MinValueValidator(0)],
    )

    agreed_to_screen = models.IntegerField(
        verbose_name="Of those approached, how many agreed to be screened",
        validators=[MinValueValidator(0)],
    )

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

    on_site = CurrentSiteManager()

    objects = DailyClosingLogManager()

    history = HistoricalRecords()

    def __str__(self):
        return self.log_date.strftime(
            convert_php_dateformat(settings.DATE_FORMAT))

    def natural_key(self):
        return (self.log_date, self.site)

    class Meta:
        verbose_name = "Daily Closing Log"
        verbose_name_plural = "Daily Closing Logs"
        constraints = [
            models.UniqueConstraint(fields=["log_date", "site"],
                                    name="unique_date_for_site"),
        ]
class RequisitionMetadata(CrfMetadataModelMixin, SiteModelMixin,
                          edc_models.BaseUuidModel):

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

    on_site = CurrentSiteManager()

    objects = RequisitionMetadataManager()

    def __str__(self) -> str:
        return (
            f"RequisitionMeta {self.model} {self.visit_schedule_name}."
            f"{self.schedule_name}.{self.visit_code}.{self.visit_code_sequence}@"
            f"{self.timepoint} {self.panel_name} {self.entry_status} "
            f"{self.subject_identifier}")

    @property
    def verbose_name(self) -> str:
        from edc_lab.site_labs import site_labs

        return site_labs.panel_names.get(self.panel_name) or self.panel_name

    def natural_key(self) -> tuple:
        return (
            self.panel_name,
            self.model,
            self.subject_identifier,
            self.visit_schedule_name,
            self.schedule_name,
            self.visit_code,
            self.visit_code_sequence,
        )

    # noinspection PyTypeHints
    natural_key.dependencies = ["sites.Site"]  # type: ignore

    class Meta(CrfMetadataModelMixin.Meta, edc_models.BaseUuidModel.Meta):
        app_label = "edc_metadata"
        verbose_name = "Requisition Metadata"
        verbose_name_plural = "Requisition Metadata"
        unique_together = ((
            "subject_identifier",
            "visit_schedule_name",
            "schedule_name",
            "visit_code",
            "visit_code_sequence",
            "model",
            "panel_name",
        ), )
        indexes = [
            models.Index(fields=[
                "subject_identifier",
                "visit_schedule_name",
                "schedule_name",
                "visit_code",
                "visit_code_sequence",
                "timepoint",
                "model",
                "entry_status",
                "show_order",
                "panel_name",
            ])
        ]
Esempio n. 11
0
class EndOfStudy(
        OffScheduleModelMixin,
        VisitScheduleFieldsModelMixin,
        ActionModelMixin,
        TrackingModelMixin,
        edc_models.BaseUuidModel,
):

    action_name = END_OF_STUDY_ACTION

    tracking_identifier_prefix = "ST"

    offschedule_datetime = models.DateTimeField(
        verbose_name="Date patient was terminated from the study",
        validators=[edc_models.datetime_not_future],
        blank=False,
        null=True,
    )

    last_study_fu_date = models.DateField(
        verbose_name="Date of last research follow up (if different):",
        validators=[edc_models.date_not_future],
        blank=True,
        null=True,
    )

    offschedule_reason = models.ForeignKey(
        OffstudyReasons,
        verbose_name="Reason patient was terminated from the study",
        on_delete=models.PROTECT,
        null=True,
    )

    other_offschedule_reason = models.TextField(
        verbose_name="If OTHER, please specify",
        max_length=500,
        blank=True,
        null=True)

    ltfu_last_alive_date = models.DateField(
        verbose_name="If lost to followup, date last known to be alive",
        validators=[edc_models.date_not_future],
        blank=True,
        null=True,
    )

    death_date = models.DateField(
        verbose_name="If deceased, date of death",
        validators=[edc_models.date_not_future],
        blank=True,
        null=True,
    )

    ltfu_date = models.DateField(
        verbose_name="Date lost to followup, if applicable",
        validators=[edc_models.date_not_future],
        blank=True,
        null=True,
    )

    transfer_date = models.DateField(
        verbose_name="Date transferred, if applicable",
        validators=[edc_models.date_not_future],
        blank=True,
        null=True,
    )

    transferred_consent = models.CharField(
        verbose_name=
        "If transferred, has the patient provided consent to be followed-up?",
        choices=YES_NO_NA,
        max_length=15,
        default=NOT_APPLICABLE,
    )

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

    on_site = CurrentSiteManager()

    def save(self, *args, **kwargs):
        if self.action_name == END_OF_STUDY_ACTION:
            raise ImproperlyConfigured(
                f"Invalid action name. Set this on the proxy model. Got {END_OF_STUDY_ACTION}."
            )
        super().save(*args, **kwargs)

    class Meta(OffScheduleModelMixin.Meta):
        verbose_name = "End of Study"
        verbose_name_plural = "End of Study"
Esempio n. 12
0
class RxRefill(
        MedicationOrderModelMixin,
        VisitCodeFieldsModelMixin,
        SiteModelMixin,
        edc_models.BaseUuidModel,
):

    rx = models.ForeignKey(Rx, on_delete=PROTECT)

    dosage_guideline = models.ForeignKey(DosageGuideline, on_delete=PROTECT)

    formulation = models.ForeignKey(Formulation, on_delete=PROTECT, null=True)

    dose = models.DecimalField(
        max_digits=6,
        decimal_places=1,
        null=True,
        blank=True,
        help_text="dose per frequency if NOT considering weight",
    )

    calculate_dose = models.BooleanField(default=True)

    frequency = models.IntegerField(
        validators=[MinValueValidator(1)],
        null=True,
        blank=True,
    )

    frequency_units = models.ForeignKey(
        FrequencyUnits,
        verbose_name="per",
        on_delete=PROTECT,
        null=True,
        blank=True,
    )

    weight_in_kgs = models.DecimalField(max_digits=6,
                                        decimal_places=1,
                                        null=True,
                                        blank=True)

    refill_date = models.DateField(verbose_name="Refill date",
                                   default=get_utcnow_as_date,
                                   help_text="")

    number_of_days = models.IntegerField(null=True)

    total = models.DecimalField(
        max_digits=6,
        decimal_places=1,
        null=True,
        blank=True,
        help_text="Leave blank to auto-calculate",
    )

    remaining = models.DecimalField(
        max_digits=6,
        decimal_places=1,
        null=True,
        blank=True,
        help_text="Leave blank to auto-calculate",
    )

    notes = models.TextField(
        max_length=250,
        null=True,
        blank=True,
        help_text="Additional information for patient",
    )

    active = models.BooleanField(default=False)

    verified = models.BooleanField(default=False)
    verified_datetime = models.DateTimeField(null=True, blank=True)

    as_string = models.CharField(max_length=150, editable=False)

    on_site = CurrentSiteManager()

    objects = Manager()

    history = edc_models.HistoricalRecords()

    def __str__(self):
        return (
            f"{self.rx} "
            f"Take {self.dose} {self.formulation.formulation_type.display_name} {self.formulation.route.display_name} "
            # f"{self.frequency} {self.frequency_units.display_name}"
        )

    def natural_key(self):
        return (
            self.rx,
            self.medication,
            self.refill_date,
        )

    def save(self, *args, **kwargs):
        if self.active:
            opts = dict(id=self.id) if self.id else {}
            if (self.__class__.objects.filter(
                    rx__subject_identifier=self.rx.subject_identifier,
                    dosage_guideline=self.dosage_guideline,
                    active=True,
            ).exclude(**opts).exists()):
                raise ActivePrescriptionRefillExists(
                    f"Unable to save as an active refill. An active refill already exists."
                )

        self.medication = self.dosage_guideline.medication
        # if not self.dose and self.calculate_dose:
        self.dose = dosage_per_day(
            self.dosage_guideline,
            weight_in_kgs=self.weight_in_kgs,
            strength=self.formulation.strength,
            strength_units=self.formulation.units.name,
        )
        self.frequency = self.dosage_guideline.frequency
        self.frequency_units = self.dosage_guideline.frequency_units
        self.total = float(self.dose) * float(self.number_of_days)
        if not self.id:
            self.remaining = self.total
        self.as_string = str(self)
        super().save(*args, **kwargs)

    @property
    def subject_identifier(self):
        return self.rx.subject_identifier

    class Meta(edc_models.BaseUuidModel.Meta):
        verbose_name = "RX refill"
        verbose_name_plural = "RX refills"
        unique_together = [
            ["rx", "dosage_guideline", "refill_date"],
            ["rx", "visit_code", "visit_code_sequence"],
        ]
Esempio n. 13
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"), )
Esempio n. 14
0
class IcpReferral(SiteModelMixin, BaseUuidModel):

    subject_screening = models.OneToOneField(SubjectScreening,
                                             null=True,
                                             on_delete=models.PROTECT)

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

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

    hospital_identifier = models.CharField(max_length=25, unique=True)

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

    age_in_years = models.IntegerField()

    initials = models.CharField(max_length=3)

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

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

    art_six_months = models.CharField(
        verbose_name="On anti-retroviral therapy for at least 6 months",
        max_length=15,
        choices=YES_NO_NA,
    )

    ifg_value = models.DecimalField(
        verbose_name="Fasting glucose levels",
        max_digits=8,
        decimal_places=4,
        null=True,
        help_text="mmol/L",
    )

    hba1c_value = models.DecimalField(
        verbose_name="HbA1c",
        max_digits=8,
        decimal_places=4,
        null=True,
        help_text="in %",
    )

    ogtt_value = models.DecimalField(
        verbose_name=
        "Blood glucose levels 2-hours after glucose solution given",
        max_digits=8,
        decimal_places=4,
        null=True,
        help_text="mmol/L",
    )

    meta_eligible = models.BooleanField(verbose_name="META eligibile")

    meta_eligibility_datetime = models.DateTimeField(
        null=True, help_text="Date and time META eligibility was determined")

    referred = models.BooleanField(verbose_name="Referred", default=False)

    referred = models.DateTimeField(null=True,
                                    help_text="Date and time of referral")

    referral_reasons = models.TextField(null=True)

    on_site = CurrentSiteManager()

    objects = IcpReferralManager()

    history = HistoricalRecords(inherit=True)

    def save(self, *args, **kwargs):
        try:
            SubjectScreening.objects.get(
                screening_identifier=self.screening_identifier)
        except ObjectDoesNotExist:
            raise IcpReferralError(
                f"Invalid META screening identifier. Got {self.screening_identifier}"
            )
        super().save(*args, **kwargs)

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

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

    class Meta:
        pass
Esempio n. 15
0
class ScreeningFieldsModeMixin(SiteModelMixin, models.Model):
    reference = models.UUIDField(verbose_name="Reference",
                                 unique=True,
                                 default=uuid4,
                                 editable=False)

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

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

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

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

    consent_ability = models.CharField(
        verbose_name="Participant or legal guardian/representative able and "
        "willing to give informed consent.",
        max_length=25,
        choices=YES_NO,
    )

    unsuitable_for_study = models.CharField(
        verbose_name=("Is there any other reason the patient is "
                      "deemed to not be suitable for the study?"),
        max_length=5,
        choices=YES_NO,
        default=NO,
        help_text="If YES, patient NOT eligible, please give reason below.",
    )

    reasons_unsuitable = models.TextField(
        verbose_name="Reason not suitable for the study",
        max_length=150,
        null=True,
        blank=True,
    )

    unsuitable_agreed = models.CharField(
        verbose_name=("Does the study coordinator agree that the patient "
                      "is not suitable for the study?"),
        max_length=5,
        choices=YES_NO_NA,
        default=NOT_APPLICABLE,
    )

    eligible = models.BooleanField(default=False, editable=False)

    reasons_ineligible = models.TextField(verbose_name="Reason not eligible",
                                          max_length=150,
                                          null=True,
                                          editable=False)

    eligibility_datetime = models.DateTimeField(
        null=True,
        editable=False,
        help_text="Date and time eligibility was determined")

    consented = models.BooleanField(default=False, editable=False)

    refused = models.BooleanField(default=False, editable=False)

    on_site = CurrentSiteManager()

    objects = ScreeningManager()

    history = HistoricalRecords(inherit=True)

    class Meta:
        abstract = True
Esempio n. 16
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"]
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"),
        )
Esempio n. 18
0
class Rx(
        NonUniqueSubjectIdentifierFieldMixin,
        SiteModelMixin,
        ActionModelMixin,
        SearchSlugModelMixin,
        edc_models.BaseUuidModel,
):

    action_name = PRESCRIPTION_ACTION

    action_identifier = models.CharField(max_length=50, unique=True, null=True)

    registered_subject = models.ForeignKey(
        Subject,
        verbose_name="Subject Identifier",
        on_delete=PROTECT,
        null=True,
        blank=False,
    )

    report_datetime = models.DateTimeField(default=get_utcnow)

    rx_date = models.DateField(verbose_name="Date RX written",
                               default=get_utcnow)

    rx_expiration_date = models.DateField(
        verbose_name="Date RX expires",
        null=True,
        blank=True,
        help_text=
        "Leave blank. Will be filled when end of study report is submitted",
    )

    status = models.CharField(max_length=25,
                              default=NEW,
                              choices=PRESCRIPTION_STATUS)

    medication = models.ForeignKey(Medication, on_delete=PROTECT, null=True)

    refill = models.IntegerField(
        null=True,
        blank=True,
        help_text="Number of times this prescription may be refilled",
    )

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

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

    weight_in_kgs = models.DecimalField(max_digits=6,
                                        decimal_places=1,
                                        null=True,
                                        blank=True)

    clinician_initials = models.CharField(max_length=3, null=True)

    notes = models.TextField(
        max_length=250,
        null=True,
        blank=True,
        help_text="Private notes for pharmacist only",
    )

    on_site = CurrentSiteManager()

    def __str__(self):
        return (
            f"{self.medication} "
            f"{self.registered_subject.subject_identifier} {self.registered_subject.initials} "
            f"{formatted_age(born=self.registered_subject.dob, reference_dt=get_utcnow())} "
            f"{self.registered_subject.gender} "
            f"Written: {self.rx_date}")

    def save(self, *args, **kwargs):
        self.registered_subject = RegisteredSubject.objects.get(
            subject_identifier=self.subject_identifier)
        if self.randomizer_name:
            randomizer = site_randomizers.get(self.randomizer_name)
            self.rando_sid = (randomizer.model_cls().objects.get(
                subject_identifier=self.subject_identifier).sid)
        super().save(*args, **kwargs)

    class Meta(edc_models.BaseUuidModel.Meta):
        verbose_name = "Prescription"
        verbose_name_plural = "Prescriptions"
Esempio n. 19
0
class Box(SearchSlugModelMixin, VerifyBoxModelMixin, SiteModelMixin,
          edc_models.BaseUuidModel):

    search_slug_fields = [
        "box_identifier", "human_readable_identifier", "name"
    ]

    box_identifier = models.CharField(max_length=25,
                                      editable=False,
                                      unique=True)

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

    box_datetime = models.DateTimeField(default=timezone.now)

    box_type = models.ForeignKey(BoxType, on_delete=PROTECT)

    category = models.CharField(max_length=25,
                                default=TESTING,
                                choices=BOX_CATEGORY)

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

    specimen_types = models.CharField(
        max_length=25,
        help_text=("List of specimen types in this box. Use two-digit numeric "
                   "codes separated by commas."),
    )

    status = models.CharField(max_length=15, default=OPEN, choices=STATUS)

    accept_primary = models.BooleanField(
        default=False,
        help_text="Tick to allow 'primary' specimens to be added to this box",
    )

    comment = models.TextField(null=True, blank=True)

    on_site = CurrentSiteManager()

    objects = BoxManager()

    history = edc_models.HistoricalRecords()

    def save(self, *args, **kwargs):
        if not self.box_identifier:
            identifier = BoxIdentifier()
            self.box_identifier = identifier.identifier
        if not self.name:
            self.name = self.box_identifier
        self.update_verified()
        super().save(*args, **kwargs)

    def __str__(self):
        return self.name

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

    natural_key.dependencies = ["edc_lab.boxtype", "sites.Site"]

    @property
    def count(self):
        return self.boxitem_set.all().count()

    @property
    def items(self):
        return self.boxitem_set.all().order_by("position")

    @property
    def human_readable_identifier(self):
        x = self.box_identifier
        return "{}-{}-{}".format(x[0:4], x[4:8], x[8:12])

    @property
    def next_position(self):
        """Returns an integer or None."""
        last_obj = self.boxitem_set.all().order_by("position").last()
        if not last_obj:
            next_position = 1
        else:
            next_position = last_obj.position + 1
        if next_position > self.box_type.total:
            raise BoxIsFullError(
                f"Box is full. Box {self.human_readable_identifier} has "
                f"{self.box_type.total} specimens.")
        return next_position

    @property
    def max_position(self):
        return

    class Meta(edc_models.BaseUuidModel.Meta):
        verbose_name = "Box"
        ordering = ("-box_datetime", )
        verbose_name_plural = "Boxes"
Esempio n. 20
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",
                ]
            )
        ]
Esempio n. 21
0
class DataQuery(ActionModelMixin, SiteModelMixin, BaseUuidModel):

    tracking_identifier_prefix = "DQ"

    action_name = DATA_QUERY_ACTION

    report_datetime = models.DateTimeField(verbose_name="Query date",
                                           default=get_utcnow)

    subject_identifier = models.CharField(max_length=50,
                                          null=True,
                                          editable=False)

    title = models.CharField(max_length=150, null=True, blank=False)

    sender = models.ForeignKey(
        DataManagerUser,
        related_name="+",
        on_delete=PROTECT,
        verbose_name="Query raised by",
        help_text="Select a name from the list",
    )

    recipients = models.ManyToManyField(
        QueryUser,
        related_name="+",
        verbose_name="Sent to",
        help_text=
        ("Select any additional recipients. Users in the `Site Data Manager` "
         "group are automatically included."),
        blank=True,
    )

    registered_subject = models.ForeignKey(
        QuerySubject,
        verbose_name="Subject Identifier",
        on_delete=PROTECT,
        null=True,
        blank=False,
    )

    query_priority = models.CharField(verbose_name="Priority",
                                      max_length=25,
                                      choices=QUERY_PRIORITY,
                                      default=NORMAL)

    visit_schedule = models.ForeignKey(
        QueryVisitSchedule,
        verbose_name="Visit",
        on_delete=PROTECT,
        null=True,
        blank=True,
    )

    visit_code_sequence = models.IntegerField(
        verbose_name="Visit code sequence",
        default=0,
        validators=[MinValueValidator(0),
                    MaxValueValidator(25)],
        null=True,
        blank=True,
        help_text=("Defaults to '0'. For example, when combined with the "
                   "visit code `1000` would make `1000.0`."),
    )

    timepoint = models.DecimalField(null=True, decimal_places=1, max_digits=6)

    data_dictionaries = models.ManyToManyField(
        DataDictionary,
        verbose_name="CRF question(s)",
        blank=True,
        help_text="select all that apply",
    )

    requisition_panel = models.ForeignKey(
        RequisitionPanel,
        verbose_name="Are responses linked to a requisition? If so, which",
        related_name="+",
        on_delete=PROTECT,
        null=True,
        blank=True,
        help_text="Requisition will be expected on day of visit.",
    )

    query_text = models.TextField(help_text="Describe the query in detail.")

    site_resolved_datetime = models.DateTimeField(
        verbose_name="Site resolved on", null=True, blank=True)

    site_response_text = models.TextField(null=True, blank=True)

    site_response_status = models.CharField(verbose_name="Site status",
                                            max_length=25,
                                            choices=RESPONSE_STATUS,
                                            default=NEW)

    status = models.CharField(verbose_name="DM status",
                              max_length=25,
                              choices=DM_STATUS,
                              default=OPEN)

    dm_user = models.ForeignKey(
        DataManagerUser,
        verbose_name="DM resolved by",
        related_name="dm_user",
        on_delete=PROTECT,
        null=True,
        blank=True,
        help_text="select a name from the list",
    )

    resolved_datetime = models.DateTimeField(verbose_name="DM resolved on",
                                             null=True,
                                             blank=True)

    auto_resolved = models.BooleanField(default=False)

    plan_of_action = models.TextField(
        null=True,
        blank=True,
        help_text="If required, provide a plan of action")

    missed_visit = models.BooleanField(
        verbose_name="Visit reported as missed",
        default=False,
        help_text="If visit/timepoint was missed, data is not expected",
    )

    locked = models.BooleanField(
        default=False,
        help_text="If locked, this query will NEVER be reopened.",
    )

    locked_reason = models.TextField(
        verbose_name="Reason query locked",
        null=True,
        blank=True,
        help_text="If required, the reason the query cannot be resolved.",
    )

    rule_generated = models.BooleanField(
        default=False,
        help_text="This query was auto-generated by a query rule.")

    rule_reference = models.CharField(verbose_name="Query rule reference",
                                      max_length=150,
                                      null=True,
                                      default=uuid4)

    on_site = CurrentSiteManager()

    objects = models.Manager()

    def __str__(self):
        return f"{self.action_identifier[-9:]}, {self.status}"

    def save(self, *args, **kwargs):
        self.subject_identifier = self.registered_subject.subject_identifier
        self.try_to_resolve_auto_generated_query()
        super().save(*args, **kwargs)

    def try_to_resolve_auto_generated_query(self):
        if (not self.dm_resolved and self.rule_generated
                and self.site_response_status == RESOLVED
                and self.site_response_text
                and self.site_response_text.strip() == AUTO_RESOLVED):
            self.status = CLOSED
            self.resolved_datetime = get_utcnow()
            self.dm_user = self.sender

    @property
    def dm_resolved(self):
        return self.status in [CLOSED, CLOSED_WITH_ACTION]

    @property
    def site_resolved(self):
        return self.site_response_status == RESOLVED

    def form_and_numbers_to_string(self):
        ret = []
        model_verbose_names = [
            o.model_verbose_name
            for o in self.data_dictionaries.all().order_by("model")
        ]
        model_verbose_names = list(set(model_verbose_names))
        for model_verbose_name in model_verbose_names:
            numbers = [
                str(o.number) for o in self.data_dictionaries.filter(
                    model_verbose_name=model_verbose_name).order_by("number")
            ]
            numbers = ", ".join(numbers)
            ret.append((model_verbose_name, numbers))
        return ret

    def get_action_item_display_name(self):
        pass

    def get_action_item_reason(self):
        try:
            url = url_names.get("subject_dashboard_url")
        except InvalidUrlName:
            visit_href = "#"
        else:
            try:
                visit = get_subject_visit_model_cls().objects.get(
                    subject_identifier=self.registered_subject.
                    subject_identifier,
                    visit_schedule_name=self.visit_schedule.
                    visit_schedule_name,
                    schedule_name=self.visit_schedule.schedule_name,
                    visit_code=self.visit_schedule.visit_code,
                    visit_code_sequence=self.visit_code_sequence,
                )
            except ObjectDoesNotExist:
                visit_href = "#"
            except AttributeError as e:
                if ("visit_schedule_name" not in str(e)
                        and "schedule_name" not in str(e)
                        and "visit_code" not in str(e)):
                    raise
                visit_href = "#"
            else:
                visit_href = reverse(
                    url,
                    kwargs=dict(
                        appointment=str(visit.appointment.id),
                        subject_identifier=self.registered_subject.
                        subject_identifier,
                    ),
                )

        template_name = (f"edc_data_manager/bootstrap{settings.EDC_BOOTSTRAP}/"
                         f"columns/query_text.html")
        context = dict(
            form_and_numbers=self.form_and_numbers_to_string(),
            query_priority=self.query_priority,
            query_priority_display=self.get_query_priority_display(),
            query_text=self.query_text,
            questions=self.data_dictionaries.all().order_by("model", "number"),
            report_datetime=self.report_datetime,
            requisition_panel=self.requisition_panel,
            resolved_datetime=self.resolved_datetime,
            site_resolved_datetime=self.site_resolved_datetime,
            site_response_text=self.site_response_text,
            site_response_status=self.get_site_response_status_display(),
            status=self.status,
            dm_user=self.dm_user,
            title=self.title,
            visit_schedule=self.visit_schedule,
            visit_href=visit_href,
        )
        return render_to_string(template_name, context=context)

    @property
    def model_names(self):
        model_names = list(
            set([dd.model for dd in self.data_dictionaries.all()]))
        return "|".join(model_names)

    class Meta(BaseUuidModel.Meta):
        verbose_name = "Data Query"
        verbose_name_plural = "Data Queries"
        unique_together = [
            "registered_subject", "rule_reference", "visit_schedule"
        ]
        indexes = [
            models.Index(fields=[
                "subject_identifier",
                "action_identifier",
                "title",
                "rule_reference",
                "registered_subject",
                "visit_schedule",
            ])
        ]
Esempio n. 22
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"),
        )
Esempio n. 23
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"
            ),
        ]