<commit_before># -*- coding: utf-8; -*-
"""Inventory application models."""
from django.db import models
from django.utils.translation import gettext as _
from django.core.exceptions import ValidationError
from django.contrib.contenttypes.fields import GenericForeignKey, GenericRelation
from django.contrib.contenttypes.models import ContentType

from mptt.models import MPTTModel, TreeForeignKey

from pharmaship.inventory import constants
from pharmaship.inventory import utils

from pharmaship.core.utils import log


# Create your models here.
class Allowance(models.Model):
    """Model for articles and medicines allowances."""

    name = models.CharField(max_length=100)  # Example: Dotation A
    author = models.CharField(max_length=100)  # Example: CCMM Purpan
    signature = models.CharField(max_length=200)
    date = models.DateField()
    version = models.CharField(max_length=20)
    # For use with complements.
    # True will add quantity, false will be treated as an absolute quantity.
    additional = models.BooleanField(default=False)
    active = models.BooleanField(default=False)

    def __str__(self):  # noqa: D105
        """Return human readable name and version of the Allowance instance."""
        return "{0} ({1})".format(self.name, self.version)

    class Meta:  # noqa: D106
        unique_together = ('name', )


class GroupManager(models.Manager):
    """Manager for class MedicineGroup and MaterialGroup.

    For deserialization purpose only.
    """

    def get_by_natural_key(self, name):
        return self.get(name=name)


class MoleculeGroup(models.Model):
    """Model for groups attached to a :model:`pharmaship.inventory.Molecule` instance."""

    objects = GroupManager()  # For deserialization

    name = models.CharField(max_length=100)  # Example: Cardiology
    order = models.IntegerField()  # Example: 1

    def __str__(self):  # noqa: D105
        return "{0}. {1}".format(self.order, _(self.name))

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

    class Meta:  # noqa: D106
        ordering = ("order", "name",)
        unique_together = ('name', )


class EquipmentGroup(models.Model):
    """Model for groups attached to a :model:`pharmaship.inventory.Equipment` instance."""

    objects = GroupManager()  # For deserialization

    name = models.CharField(max_length=100)  # Example: Reanimation
    order = models.IntegerField()  # Example: 1

    def __str__(self):  # noqa: D105
        return "{0}. {1}".format(self.order, _(self.name))

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

    class Meta:  # noqa: D106
        ordering = ("order", "name",)
        unique_together = ('name', )


class TagManager(models.Manager):
    """Manager for class Tag.

    For deserialization purpose only.
    """

    def get_by_natural_key(self, name):
        return self.get(name=name)


class Tag(models.Model):
    """Stores tags attached to a :model:`pharmaship.inventory.Equipment` or
    model:`pharmaship.inventory.Molecule` instance.
    """

    objects = TagManager()  # For deserialization

    name = models.CharField(max_length=100)  # Example: Common Use
    comment = models.TextField(blank=True, null=True)  # Description of the tag

    def __str__(self):  # noqa: D105
        return _(self.name)

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

    class Meta:  # noqa: D106
        ordering = ("name",)
        unique_together = ('name',)


class Location(MPTTModel):
    """Stores locations attached to a :model:`pharmaship.inventory.Equipment` or
    model:`pharmaship.inventory.Molecule` instance.
    """

    name = models.CharField(_("Name"), max_length=100)
    parent = TreeForeignKey('self', on_delete=models.SET_NULL, null=True, blank=True, related_name='children')
    is_rescue_bag = models.BooleanField(default=False)

    def __str__(self):  # noqa: D105
        return _(self.name)

    class MPTTMeta:  # noqa: D106
        order_insertion_by = ['name']


class QtyTransaction(models.Model):
    """Stores a quantity transaction related to :model:`pharmaship.inventory.Article`
    or :model:`pharmaship.inventory.Medicine`.

    There are 5 types of transactions:
    * 1 IN: a material is added,
    * 2 USED: the material is used for a treatment,
    * 4 PERISHED: the material has expired,
    * 8 PHYSICAL_COUNT: the stock is refreshed after a human count,
    * 9 OTHER: other reason.
    """

    transaction_type = models.PositiveIntegerField(_("Type"), choices=constants.TRANSACTION_TYPE_CHOICES)
    date = models.DateTimeField(auto_now_add=True)
    remark = models.TextField(blank=True, null=True)
    value = models.IntegerField(_("Value"), )

    content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
    object_id = models.PositiveIntegerField()
    content_object = GenericForeignKey('content_type', 'object_id')

    def __str__(self):  # noqa: D105
        return "{0} ({1}: {2})".format(self.content_object, self.get_transaction_type_display(), self.value)


class BaseItem(models.Model):
    name = models.CharField(_("Name"), max_length=100)  # Brand Name. Example: Doliprane for INN Paracétamol
    exp_date = models.DateField(_("Expiration Date"), blank=True, null=True)
    used = models.BooleanField(default=False)
    transactions = GenericRelation(QtyTransaction)
    remark = models.CharField(_("Remark"), max_length=256, blank=True, null=True)

    def __str__(self):  # noqa: D105
        return "{0} (exp: {1})".format(self.name, self.exp_date)

    def get_quantity(self):
        """Compute the quantity according to the transactions attached to this medicine."""
        # Get latest Stock Count and then make the sum
        transactions = self.transactions.order_by("date")

        # Find last IN (type=1) or STOCK COUNT (type=8)
        last_stock_count = transactions.filter(transaction_type__in=[1, 8]).latest("date")
        # If found: filter by date (only recent transaction to take in account)<commit_msg>Add quantity transaction related query name<commit_after># -*- coding: utf-8; -*-
"""Inventory application models."""
from django.db import models
from django.utils.translation import gettext as _
from django.core.exceptions import ValidationError
from django.contrib.contenttypes.fields import GenericForeignKey, GenericRelation
from django.contrib.contenttypes.models import ContentType

from mptt.models import MPTTModel, TreeForeignKey

from pharmaship.inventory import constants
from pharmaship.inventory import utils

from pharmaship.core.utils import log


# Create your models here.
class Allowance(models.Model):
    """Model for articles and medicines allowances."""

    name = models.CharField(max_length=100)  # Example: Dotation A
    author = models.CharField(max_length=100)  # Example: CCMM Purpan
    signature = models.CharField(max_length=200)
    date = models.DateField()
    version = models.CharField(max_length=20)
    # For use with complements.
    # True will add quantity, false will be treated as an absolute quantity.
    additional = models.BooleanField(default=False)
    active = models.BooleanField(default=False)

    def __str__(self):  # noqa: D105
        """Return human readable name and version of the Allowance instance."""
        return "{0} ({1})".format(self.name, self.version)

    class Meta:  # noqa: D106
        unique_together = ('name', )


class GroupManager(models.Manager):
    """Manager for class MedicineGroup and MaterialGroup.

    For deserialization purpose only.
    """

    def get_by_natural_key(self, name):
        return self.get(name=name)


class MoleculeGroup(models.Model):
    """Model for groups attached to a :model:`pharmaship.inventory.Molecule` instance."""

    objects = GroupManager()  # For deserialization

    name = models.CharField(max_length=100)  # Example: Cardiology
    order = models.IntegerField()  # Example: 1

    def __str__(self):  # noqa: D105
        return "{0}. {1}".format(self.order, _(self.name))

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

    class Meta:  # noqa: D106
        ordering = ("order", "name",)
        unique_together = ('name', )


class EquipmentGroup(models.Model):
    """Model for groups attached to a :model:`pharmaship.inventory.Equipment` instance."""

    objects = GroupManager()  # For deserialization

    name = models.CharField(max_length=100)  # Example: Reanimation
    order = models.IntegerField()  # Example: 1

    def __str__(self):  # noqa: D105
        return "{0}. {1}".format(self.order, _(self.name))

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

    class Meta:  # noqa: D106
        ordering = ("order", "name",)
        unique_together = ('name', )


class TagManager(models.Manager):
    """Manager for class Tag.

    For deserialization purpose only.
    """

    def get_by_natural_key(self, name):
        return self.get(name=name)


class Tag(models.Model):
    """Stores tags attached to a :model:`pharmaship.inventory.Equipment` or
    model:`pharmaship.inventory.Molecule` instance.
    """

    objects = TagManager()  # For deserialization

    name = models.CharField(max_length=100)  # Example: Common Use
    comment = models.TextField(blank=True, null=True)  # Description of the tag

    def __str__(self):  # noqa: D105
        return _(self.name)

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

    class Meta:  # noqa: D106
        ordering = ("name",)
        unique_together = ('name',)


class Location(MPTTModel):
    """Stores locations attached to a :model:`pharmaship.inventory.Equipment` or
    model:`pharmaship.inventory.Molecule` instance.
    """

    name = models.CharField(_("Name"), max_length=100)
    parent = TreeForeignKey('self', on_delete=models.SET_NULL, null=True, blank=True, related_name='children')
    is_rescue_bag = models.BooleanField(default=False)

    def __str__(self):  # noqa: D105
        return _(self.name)

    class MPTTMeta:  # noqa: D106
        order_insertion_by = ['name']


class QtyTransaction(models.Model):
    """Stores a quantity transaction related to :model:`pharmaship.inventory.Article`
    or :model:`pharmaship.inventory.Medicine`.

    There are 5 types of transactions:
    * 1 IN: a material is added,
    * 2 USED: the material is used for a treatment,
    * 4 PERISHED: the material has expired,
    * 8 PHYSICAL_COUNT: the stock is refreshed after a human count,
    * 9 OTHER: other reason.
    """

    transaction_type = models.PositiveIntegerField(_("Type"), choices=constants.TRANSACTION_TYPE_CHOICES)
    date = models.DateTimeField(auto_now_add=True)
    remark = models.TextField(blank=True, null=True)
    value = models.IntegerField(_("Value"), )

    content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
    object_id = models.PositiveIntegerField()
    content_object = GenericForeignKey('content_type', 'object_id')

    def __str__(self):  # noqa: D105
        return "{0} ({1}: {2})".format(self.content_object, self.get_transaction_type_display(), self.value)


class BaseItem(models.Model):
    name = models.CharField(_("Name"), max_length=100)  # Brand Name. Example: Doliprane for INN Paracétamol
    exp_date = models.DateField(_("Expiration Date"), blank=True, null=True)
    used = models.BooleanField(default=False)
    transactions = GenericRelation(QtyTransaction, related_query_name='transactions')
    remark = models.CharField(_("Remark"), max_length=256, blank=True, null=True)

    def __str__(self):  # noqa: D105
        return "{0} (exp: {1})".format(self.name, self.exp_date)

    def get_quantity(self):
        """Compute the quantity according to the transactions attached to this medicine."""
        # Get latest Stock Count and then make the sum
        transactions = self.transactions.order_by("date")

        # Find last IN (type=1) or STOCK COUNT (type=8)
        last_stock_count = transactions.filter(transaction_type__in=[1, 8]).latest("date")
        # If found: filter by date (only recent transaction to take in account)
{'fraction_non_alphanumeric': 0.11559830831743925, 'fraction_numerical': 0.01476075959864002, 'mean_word_length': 3.712778429073857, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5977364', 'n_tokens_mistral': 3799, 'n_tokens_neox': 3535, 'n_words': 957}