Пример #1
0
class MedicationProduct(SiteModelMixin, edc_models.BaseUuidModel):

    product_identifier = models.CharField(max_length=36,
                                          default=uuid4,
                                          unique=True)

    name = models.CharField(max_length=250, unique=True, editable=False)

    container = models.ForeignKey(Container, on_delete=PROTECT)

    count_per_container = models.DecimalField(
        verbose_name="Items per container", max_digits=6, decimal_places=1)

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

    lot_no = models.ForeignKey(MedicationLot, on_delete=PROTECT)

    objects = Manager()

    history = edc_models.HistoricalRecords()

    def __str__(self):
        return self.name

    def save(self, *args, **kwargs):
        self.name = (
            f"{self.formulation.medication.display_name} {self.formulation.strength}{self.formulation.units}. "
            f"LOT#: {self.lot_no.lot_no}. {self.container} of {self.count_per_container}"
        )
        super().save(*args, **kwargs)

    class Meta(SiteModelMixin.Meta, edc_models.BaseUuidModel.Meta):
        verbose_name = "Medication product"
        verbose_name_plural = "Medication products"
Пример #2
0
class ReturnHistory(edc_models.BaseUuidModel):

    rx_refill = models.ForeignKey(RxRefill, on_delete=PROTECT)

    return_datetime = models.DateTimeField(default=get_utcnow)

    returned = models.DecimalField(max_digits=6, decimal_places=1)

    objects = Manager()

    history = edc_models.HistoricalRecords()

    def __str__(self):
        return f"{str(self.rx_refill)}"

    def natural_key(self):
        return (
            self.rx_refill,
            self.return_datetime,
        )

    # TODO: calculate to verify number of returns makes sense
    # def save(self, *args, **kwargs):
    #     if self.prescription_item.get_remaining(exclude_id=self.id) < self.returned:
    #         raise ReturnError("Attempt to return more than prescribed.")
    #     super().save(*args, **kwargs)

    @property
    def return_date(self):
        return self.return_datetime.date()

    class Meta(edc_models.BaseUuidModel.Meta):
        verbose_name = "Return history"
        verbose_name_plural = "Return history"
        unique_together = ["rx_refill", "return_datetime"]
Пример #3
0
class Medication(edc_models.BaseUuidModel):

    name = models.CharField(max_length=35, unique=True)

    display_name = models.CharField(max_length=50, unique=True)

    notes = models.TextField(max_length=250, null=True, blank=True)

    objects = Manager()

    history = edc_models.HistoricalRecords()

    def __str__(self):
        return self.name

    def save(self, *args, **kwargs):
        self.name = self.name.strip().lower().replace(" ", "_")
        super().save(*args, **kwargs)

    def natural_key(self):
        return self.name

    class Meta(edc_models.BaseUuidModel.Meta):
        verbose_name = "Medication"
        verbose_name_plural = "Medications"
class MedicationStockReceiving(edc_models.BaseUuidModel):

    medication_product = models.ForeignKey(MedicationProduct,
                                           on_delete=PROTECT)

    qty = models.IntegerField()

    stock_identifiers = models.TextField()

    received = models.BooleanField(default=False)

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

    objects = Manager()

    history = edc_models.HistoricalRecords()

    def __str__(self):
        return (
            f"{self.medication_product}: {self.qty} recv'd on {self.received_datetime}"
        )

    class Meta(edc_models.BaseUuidModel.Meta):
        verbose_name = "Medication stock: Receiving"
        verbose_name_plural = "Medication stock: Receiving"
class Labels(edc_models.BaseUuidModel):

    medication_stock_create_labels = models.ForeignKey(
        MedicationStockCreateLabels, on_delete=PROTECT)

    stock_identifier = models.CharField(max_length=36,
                                        default=uuid4,
                                        unique=True)

    printed = models.BooleanField(default=False)

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

    in_stock = models.BooleanField(default=False)

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

    objects = Manager()

    history = edc_models.HistoricalRecords()

    def __str__(self):
        return f"{self.medication_stock_labels}: {self.stock_identifier} "

    class Meta(edc_models.BaseUuidModel.Meta):
        verbose_name = "Label"
        verbose_name_plural = "Labels"
Пример #6
0
class Aliquot(
    AliquotModelMixin,
    AliquotIdentifierModelMixin,
    AliquotTypeModelMixin,
    AliquotShippingMixin,
    SearchSlugModelMixin,
    edc_models.BaseUuidModel,
):
    def get_search_slug_fields(self):
        return [
            "aliquot_identifier",
            "human_readable_identifier",
            "subject_identifier",
            "parent_identifier",
            "requisition_identifier",
        ]

    on_site = CurrentSiteManager()

    objects = Manager()

    history = edc_models.HistoricalRecords()

    @property
    def human_readable_identifier(self):
        """Returns a human readable aliquot identifier."""
        x = self.aliquot_identifier
        return "{}-{}-{}-{}-{}".format(x[0:3], x[3:6], x[6:10], x[10:14], x[14:18])

    class Meta(edc_models.BaseUuidModel.Meta):
        verbose_name = "Aliquot"
Пример #7
0
class Formulation(edc_models.BaseUuidModel):

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

    strength = models.DecimalField(max_digits=6, decimal_places=1)

    units = models.ForeignKey(Units, on_delete=PROTECT)

    formulation_type = models.ForeignKey(FormulationType, on_delete=PROTECT)

    route = models.ForeignKey(Route, on_delete=PROTECT)

    notes = models.TextField(max_length=250, null=True, blank=True)

    objects = Manager()

    history = edc_models.HistoricalRecords()

    def __str__(self):
        return self.description

    def natural_key(self):
        return (
            self.medication,
            self.strength,
            self.units,
            self.formulation_type,
        )

    @property
    def description(self):
        return (
            f"{self.medication} {round(self.strength, 0)}{self.get_units_display()} "
            f"{self.get_formulation_type_display()} "
            f"{self.get_route_display()}")

    def get_formulation_type_display(self):
        return self.formulation_type.display_name

    def get_units_display(self):
        return self.units.display_name

    def get_route_display(self):
        return self.route.display_name

    class Meta(edc_models.BaseUuidModel.Meta):
        verbose_name = "Formulation"
        verbose_name_plural = "Formulations"
        unique_together = [
            "medication", "strength", "units", "formulation_type"
        ]
Пример #8
0
class SubjectVisit(
    VisitModelMixin,
    ReferenceModelMixin,
    CreatesMetadataModelMixin,
    SiteModelMixin,
    RequiresConsentFieldsModelMixin,
    edc_models.BaseUuidModel,
):
    """A model completed by the user that captures the covering
    information for the data collected for this timepoint/appointment,
    e.g.report_datetime.
    """

    reason = models.CharField(
        verbose_name="What is the reason for this visit report?",
        max_length=25,
        choices=VISIT_REASON,
        help_text=(
            "Only baseline (0m), 6m and 12m are considered "
            "`scheduled` visits as per the MOCCA protocol."
            f"If `missed' complete CRF {SubjectVisitMissed._meta.verbose_name}."
        ),
    )

    reason_unscheduled = models.CharField(
        verbose_name="If 'unscheduled', provide reason for the unscheduled visit",
        max_length=25,
        choices=VISIT_UNSCHEDULED_REASON,
        default=NOT_APPLICABLE,
    )

    clinic_services = models.ManyToManyField(
        ClinicServices,
        verbose_name="Why is the patient at the clinic today?",
        related_name="visit_clinic_services",
    )

    clinic_services_other = edc_models.OtherCharField()

    info_source = models.CharField(
        verbose_name="What is the main source of this information?",
        max_length=25,
        choices=INFO_SOURCE,
    )

    on_site = CurrentSiteManager()

    objects = VisitModelManager()

    history = edc_models.HistoricalRecords()

    class Meta(VisitModelMixin.Meta, edc_models.BaseUuidModel.Meta):
        pass
Пример #9
0
class VisitSchedule(VisitScheduleMethodsModelMixin, edc_models.BaseUuidModel):

    visit_schedule_name = models.CharField(max_length=150)

    schedule_name = models.CharField(max_length=150)

    visit_code = models.CharField(max_length=150)

    visit_name = models.CharField(max_length=150)

    visit_title = models.CharField(max_length=150)

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

    active = models.BooleanField(default=False)

    objects = VisitScheduleManager()

    history = edc_models.HistoricalRecords()

    def __str__(self):
        return (f"{self.visit_code}@{self.timepoint}: {self.visit_title} "
                f"({self.visit_schedule_name}.{self.schedule_name})")

    def natural_key(self):
        return (
            self.visit_schedule_name,
            self.schedule_name,
            self.visit_code,
        )

    class Meta(edc_models.BaseUuidModel.Meta):
        ordering = ("visit_schedule_name", "schedule_name", "visit_code")
        unique_together = (
            ("visit_schedule_name", "schedule_name", "visit_code"),
            ("visit_schedule_name", "schedule_name", "timepoint"),
        )
        indexes = [
            models.Index(fields=[
                "visit_schedule_name",
                "schedule_name",
                "visit_code",
                "visit_name",
                "visit_title",
            ]),
            models.Index(
                fields=["visit_schedule_name", "schedule_name", "timepoint"]),
        ]
Пример #10
0
class ResultItem(ResultItemModelMixin, edc_models.BaseUuidModel):

    result = models.ForeignKey(Result, on_delete=PROTECT)

    on_site = CurrentSiteManager()

    objects = ResultItemManager()

    history = edc_models.HistoricalRecords()

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

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

    class Meta(edc_models.BaseUuidModel.Meta):
        verbose_name = "Result Item"
Пример #11
0
class Shipper(edc_models.AddressMixin, edc_models.BaseUuidModel):

    name = models.CharField(unique=True, max_length=50)

    objects = ShipperManager()

    history = edc_models.HistoricalRecords()

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

    def __str__(self):
        return self.name

    class Meta(edc_models.BaseUuidModel.Meta):
        verbose_name = "Shipper"
        ordering = ("name",)
Пример #12
0
class Result(ResultModelMixin, edc_models.BaseUuidModel):

    order = models.ForeignKey(Order, on_delete=PROTECT)

    on_site = CurrentSiteManager()

    objects = ResultManager()

    history = edc_models.HistoricalRecords()

    def natural_key(self):
        return (self.report_datetime, self.order.order_identifier)

    natural_key.dependencies = ["edc_lab.order", "edc_lab.panel", "sites.Site"]

    class Meta(edc_models.BaseUuidModel.Meta):
        verbose_name = "Result"
Пример #13
0
class MedicationLot(edc_models.BaseUuidModel):

    lot_no = models.CharField(max_length=50, unique=True)

    expiration_date = models.DateField()

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

    objects = Manager()

    history = edc_models.HistoricalRecords()

    def __str__(self):
        return self.lot_no

    class Meta(edc_models.BaseUuidModel.Meta):
        verbose_name = "Medication lot"
        verbose_name_plural = "Medication lots"
Пример #14
0
class Consignee(edc_models.AddressMixin, edc_models.BaseUuidModel):

    name = models.CharField(unique=True,
                            max_length=50,
                            help_text="Company name")

    objects = ConsigneeManager()

    history = edc_models.HistoricalRecords()

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

    def __str__(self):
        return self.name

    class Meta(edc_models.BaseUuidModel.Meta):
        verbose_name = "Consignee"
        ordering = ("name", )
class MedicationStockCreateLabels(edc_models.BaseUuidModel):

    medication_product = models.ForeignKey(MedicationProduct,
                                           on_delete=PROTECT)

    qty = models.IntegerField(verbose_name="Number of labels to print")

    printed = models.BooleanField(default=False)

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

    objects = Manager()

    history = edc_models.HistoricalRecords()

    def __str__(self):
        return f"{self.medication_product}: {self.qty} "

    class Meta(edc_models.BaseUuidModel.Meta):
        verbose_name = "Medication stock: Create labels"
        verbose_name_plural = "Medication stock: Create labels"
Пример #16
0
class DispensingHistory(edc_models.BaseUuidModel):

    rx_refill = models.ForeignKey(RxRefill, on_delete=PROTECT)

    dispensed_datetime = models.DateTimeField(default=get_utcnow)

    dispensed = models.DecimalField(max_digits=6, decimal_places=1)

    status = models.CharField(verbose_name="Status",
                              max_length=25,
                              default=DISPENSED,
                              choices=DISPENSE_STATUS)

    objects = Manager()

    history = edc_models.HistoricalRecords()

    def __str__(self):
        return f"{str(self.rx_refill)}"

    def natural_key(self):
        return (
            self.rx_refill,
            self.dispensed_datetime,
        )

    def save(self, *args, **kwargs):
        Dispensing(rx_refill=self.rx_refill,
                   dispensed=self.dispensed,
                   exclude_id=self.id)
        super().save(*args, **kwargs)

    @property
    def dispensed_date(self):
        return self.dispensed_datetime.date()

    class Meta(edc_models.BaseUuidModel.Meta):
        verbose_name = "Dispensing history"
        verbose_name_plural = "Dispensing history"
        unique_together = ["rx_refill", "dispensed_datetime"]
Пример #17
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"
Пример #18
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"
Пример #19
0
class BoxItem(SearchSlugModelMixin, VerifyModelMixin, edc_models.BaseUuidModel):

    box = models.ForeignKey(Box, on_delete=PROTECT)

    position = models.IntegerField()

    identifier = models.CharField(max_length=25)

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

    objects = BoxItemManager()

    history = edc_models.HistoricalRecords()

    def natural_key(self):
        return (self.position, self.identifier) + self.box.natural_key()

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

    @property
    def human_readable_identifier(self):
        """Returns a human readable identifier."""
        if self.identifier:
            x = self.identifier
            if re.match(aliquot_pattern, self.identifier):
                return "{}-{}-{}-{}-{}".format(x[0:3], x[3:6], x[6:10], x[10:14], x[14:18])
        return self.identifier

    def get_slugs(self):
        slugs = [self.identifier, self.human_readable_identifier]
        return slugs

    class Meta(edc_models.BaseUuidModel.Meta):
        verbose_name = "Box Item"
        ordering = ("position",)
        unique_together = (("box", "position"), ("box", "identifier"))
class RegisteredSubject(UniqueSubjectIdentifierModelMixin, SiteModelMixin,
                        edc_models.BaseUuidModel):
    """A model mixin for the RegisteredSubject model (only)."""

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    on_site = CurrentSiteManager()

    history = edc_models.HistoricalRecords()

    objects = RegisteredSubjectManager()

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

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

    def __str__(self):
        return self.masked_subject_identifier

    natural_key.dependencies = ["sites.Site"]

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

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

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

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

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

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

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

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

    class Meta(edc_models.BaseUuidModel.Meta):
        verbose_name = "Registered Subject"
        ordering = ["subject_identifier"]
        unique_together = ("first_name", "dob", "initials", "additional_key")
        indexes = [
            models.Index(
                fields=["first_name", "dob", "initials", "additional_key"]),
            models.Index(fields=[
                "identity", "subject_identifier", "screening_identifier"
            ]),
        ]
        permissions = (
            ("display_firstname", "Can display first name"),
            ("display_lastname", "Can display last name"),
            ("display_dob", "Can display DOB"),
            ("display_identity", "Can display identity number"),
            ("display_initials", "Can display initials"),
        )
Пример #21
0
class 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"],
        ]
Пример #22
0
class DosageGuideline(edc_models.BaseUuidModel):

    """Dosage guidelines."""

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

    dose = models.DecimalField(
        max_digits=8,
        decimal_places=2,
        null=True,
        blank=True,
        help_text="dose per 'frequency unit' if NOT considering subject's weight",
    )

    dose_per_kg = models.DecimalField(
        max_digits=8,
        decimal_places=2,
        null=True,
        blank=True,
        help_text="dose per 'frequency unit' if considering subject's weight",
    )

    dose_units = models.ForeignKey(Units, on_delete=PROTECT)

    frequency = models.DecimalField(
        verbose_name="Frequency",
        max_digits=6,
        decimal_places=2,
        validators=[MinValueValidator(1.0)],
        default=1,
        help_text="number of times per 'frequency unit'",
    )

    frequency_units = models.ForeignKey(
        FrequencyUnits,
        verbose_name="Frequency unit",
        on_delete=PROTECT,
    )

    objects = Manager()

    history = edc_models.HistoricalRecords()

    def __str__(self):
        return (
            f"{self.medication.name} {round(self.dose or 0, 0)}{self.dose_units} "
            f"{round((self.frequency or 0), 0)} "
            f"{self.get_frequency_units_display()}{' (per kg)' if self.dose_per_kg else ''}"
        )

    def natural_key(self):
        return (
            self.medication,
            self.dose,
            self.dose_units,
            self.dose_per_kg,
        )

    def get_dose_units_display(self):
        return self.dose_units.display_name

    def get_frequency_units_display(self):
        return self.frequency_units.display_name

    class Meta(edc_models.BaseUuidModel.Meta):
        verbose_name = "Dosage Guideline"
        verbose_name_plural = "Dosage Guidelines"
        unique_together = ["medication", "dose", "dose_units", "dose_per_kg"]
Пример #23
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"