# Path: rental/models.py
# class ItemRental(models.Model):
#     """
#     Intermediate relationship for each item within a rental.
#     Defines the quantity and number of returned items.
# 
#     :author: Benedikt Seidl
#     """
# 
#     rental = models.ForeignKey(Rental, on_delete=models.CASCADE)
#     item = models.ForeignKey(Item, on_delete=models.CASCADE)
#     quantity = models.PositiveSmallIntegerField()
#     returned = models.PositiveSmallIntegerField(default=0)
# 
#     def clean(self):
#         if self.rental.depot_id != self.item.depot_id:
#             raise ValidationError({
#                 'item': 'The item must come from the depot the rental was created for.'
#             })
# 
#         if self.item.visibility != Item.VISIBILITY_PUBLIC:
#             organization = self.rental.depot.organization
#             user = self.rental.user
# 
#             if user is None or not organization.is_member(user):
#                 raise ValidationError({
#                     'item': 'You have to be a member of the organization '
#                             'that manages this depot to rent an internal item.'
#                 })
# 
#         if self.quantity <= 0 or self.quantity > self.item.quantity:
#             raise ValidationError({
#                 'quantity': 'The quantity must be positive and less than or '
#                             'equal to the total amount of available items.'
#             })
# 
#         if self.returned > self.quantity:
#             raise ValidationError({
#                 'returned': 'The amount of returned items must be less than or '
#                             'equal to the total amount of rented items.'
#             })
# 
# class Rental(models.Model):
#     """
#     A rental defines the amount of items and has a start and return date.
#     Only items from one depot can be requested for rental at once.
# 
#     When requesting a rental, the user has to enter his full name and email
#     address as well as describing the purpose of his rental. If they are
#     logged in, the user's id will be stored as well.
# 
#     After creating a rental request, it is in the PENDING state. From this,
#     it can be either APPROVED or DECLINED by a depot manager or REVOKED by
#     the requesting user. If all items were returned correctly, it can be
#     set to RETURNED to finish the rental process.
# 
#     :author: Benedikt Seidl
#     """
# 
#     STATE_PENDING = '1'
#     STATE_APPROVED = '2'
#     STATE_DECLINED = '3'
#     STATE_REVOKED = '4'
#     STATE_RETURNED = '5'
#     STATES = (
#         (STATE_PENDING, _('pending')),
#         (STATE_APPROVED, _('approved')),
#         (STATE_DECLINED, _('declined')),
#         (STATE_REVOKED, _('revoked')),
#         (STATE_RETURNED, _('returned')),
#     )
# 
#     uuid = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
#     depot = models.ForeignKey(Depot, on_delete=models.CASCADE)
#     items = models.ManyToManyField(Item, through='ItemRental')
#     firstname = models.CharField(max_length=32)
#     lastname = models.CharField(max_length=32)
#     email = models.EmailField()
#     purpose = models.CharField(max_length=256)
#     user = models.ForeignKey(User, blank=True, null=True, on_delete=models.SET_NULL)
#     start_date = models.DateTimeField()
#     return_date = models.DateTimeField()
#     state = models.CharField(max_length=1, choices=STATES, default=STATE_PENDING)
# 
#     def clean(self):
#         if not self.depot.active:
#             raise ValidationError({'depot': 'The depot has to be active.'})
# 
#         if self.start_date > self.return_date:
#             raise ValidationError({
#                 'start_date': 'The start date must be before the return date.'
#             })
# 
#         if self.start_date < datetime.now() and self.state == self.STATE_PENDING:
#             raise ValidationError({
#                 'start_date': 'The start date must be in the future for new rentals.'
#             })
# 
#     def __str__(self):
#         return 'Rental by %s %s' % (self.firstname, self.lastname)


from django.db.models import Prefetch
from rental.models import ItemRental, Rental
        self.end = end
        self.value = value

    def __lt__(self, other):
        return self.value < other.value

    def __eq__(self, other):
        return (self.begin == other.begin
                and self.end == other.end
                and self.value == other.value)

    def __repr__(self):
        return '<Interval begin:"%s" end:"%s" value:%d>' % (self.begin, self.end, self.value)

    def __str__(self):
        return '[%s, %s] -> %d' % (self.begin, self.end, self.value)


class Availability:
    """
    Helper class to determine the availability of a list of items

    An item becomes only unavailable when it is parted of an approved rental
    request. All other items, especially in rental requests that are still
    pending, are considered available.

    :author: Leo Tappe
    """

    def __init__(self, start_date, return_date, depot_id,

# Path: rental/models.py
# class ItemRental(models.Model):
#     """
#     Intermediate relationship for each item within a rental.
#     Defines the quantity and number of returned items.
# 
#     :author: Benedikt Seidl
#     """
# 
#     rental = models.ForeignKey(Rental, on_delete=models.CASCADE)
#     item = models.ForeignKey(Item, on_delete=models.CASCADE)
#     quantity = models.PositiveSmallIntegerField()
#     returned = models.PositiveSmallIntegerField(default=0)
# 
#     def clean(self):
#         if self.rental.depot_id != self.item.depot_id:
#             raise ValidationError({
#                 'item': 'The item must come from the depot the rental was created for.'
#             })
# 
#         if self.item.visibility != Item.VISIBILITY_PUBLIC:
#             organization = self.rental.depot.organization
#             user = self.rental.user
# 
#             if user is None or not organization.is_member(user):
#                 raise ValidationError({
#                     'item': 'You have to be a member of the organization '
#                             'that manages this depot to rent an internal item.'
#                 })
# 
#         if self.quantity <= 0 or self.quantity > self.item.quantity:
#             raise ValidationError({
#                 'quantity': 'The quantity must be positive and less than or '
#                             'equal to the total amount of available items.'
#             })
# 
#         if self.returned > self.quantity:
#             raise ValidationError({
#                 'returned': 'The amount of returned items must be less than or '
#                             'equal to the total amount of rented items.'
#             })
# 
# class Rental(models.Model):
#     """
#     A rental defines the amount of items and has a start and return date.
#     Only items from one depot can be requested for rental at once.
# 
#     When requesting a rental, the user has to enter his full name and email
#     address as well as describing the purpose of his rental. If they are
#     logged in, the user's id will be stored as well.
# 
#     After creating a rental request, it is in the PENDING state. From this,
#     it can be either APPROVED or DECLINED by a depot manager or REVOKED by
#     the requesting user. If all items were returned correctly, it can be
#     set to RETURNED to finish the rental process.
# 
#     :author: Benedikt Seidl
#     """
# 
#     STATE_PENDING = '1'
#     STATE_APPROVED = '2'
#     STATE_DECLINED = '3'
#     STATE_REVOKED = '4'
#     STATE_RETURNED = '5'
#     STATES = (
#         (STATE_PENDING, _('pending')),
#         (STATE_APPROVED, _('approved')),
#         (STATE_DECLINED, _('declined')),
#         (STATE_REVOKED, _('revoked')),
#         (STATE_RETURNED, _('returned')),
#     )
# 
#     uuid = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
#     depot = models.ForeignKey(Depot, on_delete=models.CASCADE)
#     items = models.ManyToManyField(Item, through='ItemRental')
#     firstname = models.CharField(max_length=32)
#     lastname = models.CharField(max_length=32)
#     email = models.EmailField()
#     purpose = models.CharField(max_length=256)
#     user = models.ForeignKey(User, blank=True, null=True, on_delete=models.SET_NULL)
#     start_date = models.DateTimeField()
#     return_date = models.DateTimeField()
#     state = models.CharField(max_length=1, choices=STATES, default=STATE_PENDING)
# 
#     def clean(self):
#         if not self.depot.active:
#             raise ValidationError({'depot': 'The depot has to be active.'})
# 
#         if self.start_date > self.return_date:
#             raise ValidationError({
#                 'start_date': 'The start date must be before the return date.'
#             })
# 
#         if self.start_date < datetime.now() and self.state == self.STATE_PENDING:
#             raise ValidationError({
#                 'start_date': 'The start date must be in the future for new rentals.'
#             })
# 
#     def __str__(self):
#         return 'Rental by %s %s' % (self.firstname, self.lastname)


# Path: rental/availability.py
from django.db.models import Prefetch
from rental.models import ItemRental, Rental

        self.end = end
        self.value = value

    def __lt__(self, other):
        return self.value < other.value

    def __eq__(self, other):
        return (self.begin == other.begin
                and self.end == other.end
                and self.value == other.value)

    def __repr__(self):
        return '<Interval begin:"%s" end:"%s" value:%d>' % (self.begin, self.end, self.value)

    def __str__(self):
        return '[%s, %s] -> %d' % (self.begin, self.end, self.value)


class Availability:
    """
    Helper class to determine the availability of a list of items

    An item becomes only unavailable when it is parted of an approved rental
    request. All other items, especially in rental requests that are still
    pending, are considered available.

    :author: Leo Tappe
    """

    def __init__(self, start_date, return_date, depot_id,

                 conflicting_states=[Rental.STATE_APPROVED]):