Example #1
0
    def test_get_machine_type(self):
        for machine_type in MachineTypeField.possible_machine_types:
            self.assertEqual(
                machine_type,
                MachineTypeField.get_machine_type(machine_type.id))

        self.assertEqual(None, MachineTypeField.get_machine_type(0))
Example #2
0
 def test_get_prep_value(self):
     machine_type_field = MachineTypeField()
     self.assertEqual(None, machine_type_field.get_prep_value(None))
     for machine_type in MachineTypeField.possible_machine_types:
         self.assertEqual(machine_type.id,
                          machine_type_field.get_prep_value(machine_type))
         self.assertEqual(machine_type.id,
                          machine_type_field.get_prep_value([machine_type]))
Example #3
0
 def setUp(self):
     event = Event.objects.create(title="TEST EVENT")
     self.timeplace = TimePlace.objects.create(
         pub_date=timezone.now(),
         start_date=timezone.now(),
         start_time=(timezone.now() + timedelta(seconds=1)).time(),
         event=event)
     self.machine_type = MachineTypeField.get_machine_type(1)
     self.machine = Machine.objects.create(name="C1",
                                           location="Printer room",
                                           status="F",
                                           machine_type=self.machine_type)
     self.user = User.objects.create_user("User", "*****@*****.**",
                                          "user_pass")
     self.user_quota = Quota.objects.create(user=self.user,
                                            ignore_rules=False,
                                            number_of_reservations=2,
                                            machine_type=self.machine_type)
     self.course_registration = Printer3DCourse.objects.create(
         user=self.user,
         username=self.user.username,
         date=datetime.now().date(),
         name=self.user.get_full_name())
     self.max_time_reservation = 5
     ReservationRule.objects.create(
         machine_type=self.machine_type,
         start_time=time(0, 0),
         end_time=time(23, 59),
         days_changed=6,
         start_days=1,
         max_hours=self.max_time_reservation,
         max_inside_border_crossed=self.max_time_reservation)
Example #4
0
class MachineUsageRule(models.Model):
    """
    Allows for specification of rules for each type of machine
    """
    machine_type = MachineTypeField(unique=True)
    content = RichTextUploadingField()
    content_en = RichTextUploadingField()
Example #5
0
    def test_calendar_reservation_url(self, now_mock):
        now_mock.return_value = local_to_date(
            timezone.datetime(2018, 12, 9, 12, 24))
        user = User.objects.create_user("user", "*****@*****.**",
                                        "weak_pass")
        user.save()

        machine_type_printer = MachineTypeField.get_machine_type(1)
        Quota.objects.create(user=user,
                             number_of_reservations=2,
                             ignore_rules=True,
                             machine_type=machine_type_printer)
        printer = Machine.objects.create(name="U1",
                                         location="S1",
                                         machine_model="Ultimaker",
                                         status="F",
                                         machine_type=machine_type_printer)
        Printer3DCourse.objects.create(user=user,
                                       username=user.username,
                                       name=user.get_full_name(),
                                       date=timezone.now())

        reservation = Reservation.objects.create(user=user,
                                                 machine=printer,
                                                 event=None,
                                                 start_time=timezone.now(),
                                                 end_time=timezone.now() +
                                                 timedelta(hours=2))

        self.assertEqual(current_calendar_url(printer),
                         calendar_url_reservation(reservation))
Example #6
0
    def test_get_user_reservations_different_types(self):
        machine_type_sewing = MachineTypeField.get_machine_type(2)
        Quota.objects.create(user=self.user,
                             number_of_reservations=2,
                             machine_type=machine_type_sewing,
                             ignore_rules=True)

        sewing_machine = Machine.objects.create(
            name="T1",
            machine_model="Generic",
            location="M1",
            status="F",
            machine_type=machine_type_sewing)

        Reservation.objects.create(user=self.user,
                                   machine=sewing_machine,
                                   start_time=timezone.now(),
                                   end_time=timezone.now() +
                                   timezone.timedelta(hours=2),
                                   event=None)

        self.assertEqual([
            Reservation.objects.get(user=self.user,
                                    machine__machine_type=machine_type_sewing),
            Reservation.objects.get(
                user=self.user,
                machine__machine_type=self.machine_type_printer)
        ],
                         list(
                             template_view_get_context_data(
                                 MyReservationsView,
                                 request_user=self.user)["reservations"]))
Example #7
0
    def setUp(self):
        self.user = User.objects.create_user("user", "*****@*****.**")

        self.machine_type_printer = MachineTypeField.get_machine_type(1)

        printer = Machine.objects.create(
            name="U1",
            machine_type=self.machine_type_printer,
            machine_model="Ultimaker 2",
            location="S1",
            status="F")
        Quota.objects.create(user=self.user,
                             number_of_reservations=2,
                             ignore_rules=True,
                             machine_type=self.machine_type_printer)
        Printer3DCourse.objects.create(user=self.user,
                                       name=self.user.get_full_name(),
                                       username=self.user.username,
                                       date=timezone.now().date())

        Reservation.objects.create(user=self.user,
                                   machine=printer,
                                   event=None,
                                   start_time=timezone.now(),
                                   end_time=timezone.now() +
                                   timezone.timedelta(hours=2))
Example #8
0
class Machine(models.Model):
    status_choices = (
        ("R", _("Reserved")),
        ("F", _("Available")),
        ("I", _("In use")),
        ("O", _("Out of order")),
        ("M", _("Maintenance")),
    )

    status = models.CharField(max_length=2,
                              choices=status_choices,
                              verbose_name=_("Status"),
                              default="F")
    name = models.CharField(max_length=30, unique=True, verbose_name=_("Name"))
    location = models.CharField(max_length=40, verbose_name=_("Location"))
    location_url = models.URLField(verbose_name=_("Location URL"))
    machine_model = models.CharField(max_length=40,
                                     verbose_name=_("Machine model"))
    machine_type = MachineTypeField(null=True, verbose_name=_("Machine type"))

    @abstractmethod
    def get_reservation_set(self):
        return Reservation.objects.filter(machine=self)

    def get_next_reservation(self):
        return self.get_reservation_set().filter(
            start_time__gt=timezone.now()).order_by('start_time').first()

    @abstractmethod
    def can_user_use(self, user):
        return self.machine_type.can_user_use(user)

    def reservations_in_period(self, start_time, end_time):
        return self.get_reservation_set().filter(start_time__lte=start_time, end_time__gt=start_time) | \
               self.get_reservation_set().filter(start_time__gte=start_time, end_time__lte=end_time) | \
               self.get_reservation_set().filter(start_time__lt=end_time, start_time__gt=start_time,
                                                 end_time__gte=end_time)

    def __str__(self):
        return self.name + " - " + self.machine_model

    def get_status(self):
        if self.status in "OM":
            return self.status
        return self.reservations_in_period(
            timezone.now(),
            timezone.now() + timedelta(seconds=1)) and "R" or "F"

    def _get_FIELD_display(self, field):
        if field.attname == "status":
            return self._get_status_display()
        return super()._get_FIELD_display(field)

    def _get_status_display(self):
        current_status = self.get_status()
        return [
            full_name for short_hand, full_name in self.status_choices
            if short_hand == current_status
        ][0]
Example #9
0
 def setUp(self):
     machine_type_printer = MachineTypeField.get_machine_type(1)
     self.machine = Machine.objects.create(
         name="Test",
         machine_model="Ultimaker 2+",
         machine_type=machine_type_printer)
     self.event = Event.objects.create(title="Test_Event")
     self.timeplace = TimePlace.objects.create(event=self.event)
Example #10
0
 def setUp(self):
     self.machine_type_printer = MachineTypeField.get_machine_type(1)
     self.machine_type_sewing = MachineTypeField.get_machine_type(2)
     self.user = User.objects.create_user("test")
     self.user2 = User.objects.create_user("test2")
     self.quota1 = Quota.objects.create(
         all=True,
         machine_type=self.machine_type_sewing,
         number_of_reservations=2)
     self.quota2 = Quota.objects.create(
         all=True,
         machine_type=self.machine_type_printer,
         number_of_reservations=4)
     self.quota3 = Quota.objects.create(
         user=self.user,
         machine_type=self.machine_type_printer,
         number_of_reservations=4)
     self.quota4 = Quota.objects.create(
         user=self.user2,
         machine_type=self.machine_type_sewing,
         number_of_reservations=1)
Example #11
0
 def create_reservation(start_time, end_time):
     machine_type = MachineTypeField.get_machine_type(1)
     Machine.objects.create(name="S1", location="U1", machine_model="Ultimaker", status="F",
                            machine_type=machine_type)
     user = User.objects.create_user("User", "*****@*****.**", "unsecure_pass")
     user.save()
     Quota.objects.create(user=user, number_of_reservations=2, ignore_rules=True, machine_type=machine_type)
     Printer3DCourse.objects.create(user=user, username=user.username, name=user.get_full_name(),
                                    date=timezone.datetime.now().date())
     Reservation.objects.create(user=user, machine=Machine.objects.get(name="S1"),
                                start_time=start_time,
                                end_time=end_time,
                                event=None)
     return Reservation.objects.first()
Example #12
0
    def test_to_python(self):
        machine_type_field = MachineTypeField()
        self.assertEqual(None, machine_type_field.to_python(None))
        for machine_type in MachineTypeField.possible_machine_types:
            self.assertEqual(machine_type,
                             machine_type_field.to_python(machine_type.id))
            self.assertEqual(machine_type,
                             machine_type_field.to_python(machine_type.id))

        try:
            machine_type_field.to_python(0)
            self.fail("Invalid IDs should fail when converting to python")
        except ValidationError:
            pass

        try:
            machine_type_field.to_python(machine_type_field)
            self.fail("Invalid types should fail when converting to python")
        except ValidationError:
            pass
    def test_get_admin_reservation(self):
        user = User.objects.create_user("test")
        machine_type = MachineTypeField.get_machine_type(1)
        Quota.objects.create(machine_type=machine_type,
                             number_of_reservations=10,
                             ignore_rules=True,
                             user=user)
        permission = Permission.objects.get(
            codename="can_create_event_reservation")
        user.user_permissions.add(permission)
        event = Event.objects.create(title="Test_event")
        timeplace = TimePlace.objects.create(
            event=event,
            start_time=(timezone.now() + timedelta(hours=1)).time(),
            start_date=(timezone.now() + timedelta(hours=1)).date(),
            end_time=(timezone.now() + timedelta(hours=2)).time(),
            end_date=(timezone.now() + timedelta(hours=2)).date())
        printer = Machine.objects.create(machine_type=machine_type,
                                         machine_model="Ultimaker")
        Printer3DCourse.objects.create(user=user,
                                       username=user.username,
                                       name=user.get_full_name(),
                                       date=timezone.now())
        special_reservation = Reservation.objects.create(
            start_time=timezone.now() + timedelta(hours=1),
            special_text="Test",
            special=True,
            user=user,
            machine=printer,
            end_time=timezone.now() + timedelta(hours=2))
        normal_reservation = Reservation.objects.create(
            start_time=timezone.now() + timedelta(hours=2),
            user=user,
            machine=printer,
            end_time=timezone.now() + timedelta(hours=3))
        event_reservation = Reservation.objects.create(
            start_time=timezone.now() + timedelta(hours=3),
            event=timeplace,
            user=user,
            machine=printer,
            end_time=timezone.now() + timedelta(hours=4))

        context_data = template_view_get_context_data(AdminReservationView,
                                                      request_user=user)
        self.assertEqual(context_data["admin"], True)
        self.assertEqual(set(context_data["reservations"]),
                         {special_reservation, event_reservation})
Example #14
0
    def test_is_valid_rule_external(self):
        ReservationRule(start_time=datetime.time(10, 0),
                        end_time=datetime.time(6, 0),
                        days_changed=1,
                        start_days=53,
                        max_inside_border_crossed=0,
                        machine_type=self.machine_type,
                        max_hours=0).save()

        rule = ReservationRule(start_time=datetime.time(12, 0),
                               end_time=datetime.time(18, 0),
                               days_changed=0,
                               start_days=66,
                               max_inside_border_crossed=0,
                               machine_type=self.machine_type,
                               max_hours=0)

        self.assertTrue(rule.is_valid_rule())

        rule = ReservationRule(start_time=datetime.time(5, 0),
                               end_time=datetime.time(12, 0),
                               days_changed=0,
                               start_days=2,
                               max_inside_border_crossed=0,
                               machine_type=self.machine_type,
                               max_hours=0)

        self.assertFalse(rule.is_valid_rule())
        try:
            rule.is_valid_rule(raise_error=True)
            self.fail(
                "An exception should have been raised for a check on a non valid rule with raise_error set"
            )
        except ValidationError:
            pass

        try:
            rule.save()
            self.fail("Valid rules should not be saveable")
        except ValidationError:
            pass

        rule.machine_type = MachineTypeField.get_machine_type(2)
        self.assertTrue(
            rule.is_valid_rule(),
            "Rules for different machine types should not effect the validity of each other"
        )
Example #15
0
    def test_status(self):
        machine_type = MachineTypeField.get_machine_type(1)
        printer = Machine.objects.create(name="C1",
                                         location="Printer room",
                                         status="F",
                                         machine_model="Ultimaker 2 Extended",
                                         machine_type=machine_type)
        user = User.objects.create_user("test")
        Printer3DCourse.objects.create(name="Test",
                                       username="******",
                                       user=user,
                                       date=timezone.datetime.now().date())
        Quota.objects.create(machine_type=machine_type,
                             user=user,
                             ignore_rules=True,
                             number_of_reservations=1)

        self.assertEquals(printer.get_status(), "F")
        self.assertEquals(printer.get_status_display(), "Ledig")
        printer.status = "O"
        self.assertEquals(printer.get_status(), "O")
        self.assertEquals(printer.get_status_display(), "I ustand")
        printer.status = "M"
        self.assertEquals(printer.get_status(), "M")
        self.assertEquals(printer.get_status_display(), "Vedlikehold")
        printer.status = "R"
        self.assertEquals(printer.get_status(), "F")
        self.assertEquals(printer.get_status_display(), "Ledig")

        Reservation.objects.create(machine=printer,
                                   start_time=timezone.now(),
                                   end_time=timezone.now() +
                                   timedelta(hours=1),
                                   user=user)

        self.assertEquals(printer.get_status(), "R")
        self.assertEquals(printer.get_status_display(), "Reservert")
        printer.status = "F"
        self.assertEquals(printer.get_status(), "R")
        self.assertEquals(printer.get_status_display(), "Reservert")
        printer.status = "O"
        self.assertEquals(printer.get_status(), "O")
        self.assertEquals(printer.get_status_display(), "I ustand")
        printer.status = "M"
        self.assertEquals(printer.get_status(), "M")
        self.assertEquals(printer.get_status_display(), "Vedlikehold")
 def setUp(self):
     self.user = User.objects.create_user("test")
     self.machine_type = MachineTypeField.get_machine_type(2)
     self.machine = Machine.objects.create(machine_type=self.machine_type,
                                           status="F",
                                           name="Test")
     Quota.objects.create(machine_type=self.machine_type,
                          number_of_reservations=2,
                          ignore_rules=False,
                          all=True)
     ReservationRule.objects.create(start_time=time(0, 0),
                                    end_time=time(23, 59),
                                    start_days=1,
                                    days_changed=6,
                                    max_inside_border_crossed=6,
                                    max_hours=6,
                                    machine_type=self.machine_type)
 def setUp(self):
     Reservation.reservation_future_limit_days = 7
     self.machine_type_sewing = MachineTypeField.get_machine_type(2)
     self.user = User.objects.create_user(username="******")
     self.machine = Machine.objects.create(
         machine_model="Test", machine_type=self.machine_type_sewing)
     self.event = Event.objects.create(title="Test_event")
     Quota.objects.create(user=self.user,
                          machine_type=self.machine_type_sewing,
                          number_of_reservations=100,
                          ignore_rules=True)
     self.timeplace = TimePlace.objects.create(
         event=self.event,
         start_time=(timezone.now() + timedelta(hours=1)).time(),
         start_date=(timezone.now() + timedelta(hours=1)).date(),
         end_time=(timezone.now() + timedelta(hours=2)).time(),
         end_date=(timezone.now() + timedelta(hours=2)).date())
Example #18
0
class Machine(models.Model):
    status_choices = (
        ("R", _("Reserved")),
        ("F", _("Available")),
        ("I", _("In use")),
        ("O", _("Out of order")),
        ("M", _("Maintenance")),
    )

    status = models.CharField(max_length=2, choices=status_choices)
    name = models.CharField(max_length=30, unique=True)
    location = models.CharField(max_length=40)
    location_url = models.URLField()
    machine_model = models.CharField(max_length=40)
    machine_type = MachineTypeField(null=True)

    @abstractmethod
    def get_reservation_set(self):
        return Reservation.objects.filter(machine=self)

    @abstractmethod
    def can_user_use(self, user):
        return self.machine_type.can_user_use(user)

    def reservations_in_period(self, start_time, end_time):
        return self.get_reservation_set().filter(start_time__lte=start_time, end_time__gt=start_time) | \
               self.get_reservation_set().filter(start_time__gte=start_time, end_time__lte=end_time) | \
               self.get_reservation_set().filter(start_time__lt=end_time, start_time__gt=start_time,
                                                 end_time__gte=end_time)

    def __str__(self):
        return self.name + " - " + self.machine_model

    def get_status(self):
        if self.status in "OM":
            return self.status
        return self.reservations_in_period(
            timezone.now(),
            timezone.now() + timedelta(seconds=1)) and "R" or "F"

    def get_status_display(self):
        current_status = self.get_status()
        return [
            full_name for short_hand, full_name in self.status_choices
            if short_hand == current_status
        ][0]
Example #19
0
    def test_get_user_quota(self):
        user = User.objects.create_user("test")
        user2 = User.objects.create_user("test2")
        machine_type = MachineTypeField.get_machine_type(1)
        Quota.objects.create(all=True,
                             user=user,
                             machine_type=machine_type,
                             number_of_reservations=2)
        quota2 = Quota.objects.create(user=user,
                                      machine_type=machine_type,
                                      number_of_reservations=2)
        Quota.objects.create(user=user2,
                             machine_type=machine_type,
                             number_of_reservations=2)

        context_data = template_view_get_context_data(GetUserQuotaView,
                                                      request_user=user,
                                                      user=user)
        context_data["user_quotas"] = list(context_data["user_quotas"])

        self.assertEqual(context_data, {"user_quotas": [quota2]})
    def setUp(self):
        user1 = User.objects.create_user("user1", "*****@*****.**",
                                         "weak_pass")
        user1.save()
        user2 = User.objects.create_user("user2", "*****@*****.**",
                                         "weak_pass")
        user2.save()

        machine_type_printer = MachineTypeField.get_machine_type(1)
        Machine.objects.create(name="U1",
                               location="Make",
                               machine_model="Ultimaker 2",
                               status="F",
                               machine_type=machine_type_printer)

        Quota.objects.create(user=user1,
                             number_of_reservations=2,
                             ignore_rules=True,
                             machine_type=machine_type_printer)
        Quota.objects.create(user=user2,
                             number_of_reservations=2,
                             ignore_rules=True,
                             machine_type=machine_type_printer)

        Printer3DCourse.objects.create(user=user1,
                                       username=user1.username,
                                       name=user1.get_full_name(),
                                       date=timezone.now())
        Printer3DCourse.objects.create(user=user2,
                                       username=user2.username,
                                       name=user2.get_full_name(),
                                       date=timezone.now())

        self.reservation = Reservation.objects.create(
            user=user1,
            machine=Machine.objects.get(name="U1"),
            start_time=timezone.now() + timezone.timedelta(hours=2),
            end_time=timezone.now() + timezone.timedelta(hours=4),
            event=None)
Example #21
0
 def to_python(self, value):
     return MachineTypeField.get_machine_type(int(value))
Example #22
0
 def setUp(self):
     self.period = ReservationRule.Period
     self.machine_type = MachineTypeField.get_machine_type(1)
Example #23
0
 def setUp(self):
     self.machine_type = MachineTypeField.get_machine_type(1)
Example #24
0
class ReservationRule(models.Model):
    start_time = models.TimeField(verbose_name=_("Start time"))
    end_time = models.TimeField(verbose_name=_("End time"))
    # Number of times passed by midnight between start and end time
    days_changed = models.IntegerField(verbose_name=_("Days"))
    start_days = models.IntegerField(default=0, verbose_name=_("Start days"))
    max_hours = models.FloatField(verbose_name=_("Hours single period"))
    max_inside_border_crossed = models.FloatField(
        verbose_name=_("Hours multiperiod"))
    machine_type = MachineTypeField(verbose_name=_("Machine type"))

    def save(self, **kwargs):
        if not self.is_valid_rule():
            raise ValidationError("Rule is not valid")
        return super().save(**kwargs)

    @staticmethod
    def valid_time(start_time, end_time, machine_type):
        # Normal non rule ignoring reservations will not be longer than 1 week
        if timedelta_to_hours(end_time - start_time) > 168:
            return False
        rules = [
            rule for rule in ReservationRule.objects.filter(
                machine_type=machine_type)
            if rule.hours_inside(start_time, end_time)
        ]
        if timedelta_to_hours(end_time - start_time) > max(rule.max_hours
                                                           for rule in rules):
            return False

        if timedelta_to_hours(end_time - start_time) <= min(rule.max_hours
                                                            for rule in rules):
            return True

        return all(
            rule.valid_time_in_rule(start_time, end_time,
                                    len(rules) > 1) for rule in rules)

    class Period:
        def __init__(self, start_day, rule):
            self.start_time = self.__to_inner_rep(start_day, rule.start_time)
            self.end_time = self.__to_inner_rep(start_day + rule.days_changed,
                                                rule.end_time)
            self.rule = rule

        def hours_inside(self, start_time, end_time):
            return self.hours_overlap(
                self.start_time, self.end_time,
                self.__to_inner_rep(start_time.weekday(), start_time.time()),
                self.__to_inner_rep(end_time.weekday(), end_time.time()))

        @staticmethod
        def hours_overlap(a, b, c, d):
            b, c, d = (b - a) % 7, (c - a) % 7, (d - a) % 7

            if c > d:
                return min(b, d) * 24
            return (min(b, d) - min(b, c)) * 24

        @staticmethod
        def __to_inner_rep(day, time):
            return day + time.hour / 24 + time.minute / 1440 + time.second / 86400

        def overlap(self, other):
            return self.hours_overlap(self.start_time, self.end_time,
                                      other.start_time, other.end_time) > 0

    def is_valid_rule(self, raise_error=False):
        # Check if the time period is a valid time period (within a week)
        if self.start_time > self.end_time and not self.days_changed or self.days_changed > 7 or \
                self.days_changed == 7 and self.start_time < self.end_time:
            if raise_error:
                raise ValidationError(
                    "Period is either too long (7+ days) or start time is earlier than end time."
                )
            return False

        # Check for internal overlap
        time_periods = self.time_periods()
        if any(
                t1.overlap(t2) for t1 in time_periods for t2 in time_periods if
                t1.end_time != t2.end_time and t1.start_time != t2.end_time):
            if raise_error:
                raise ValidationError(
                    "Rule has internal overlap of time periods.")
            return False

        # Check for overlap with other time periods
        other_time_periods = [
            time_period for rule in ReservationRule.objects.filter(
                machine_type=self.machine_type).exclude(pk=self.pk)
            for time_period in rule.time_periods()
        ]

        other_overlap = any(
            t1.overlap(t2) for t1 in time_periods for t2 in other_time_periods)

        if raise_error and other_overlap:
            raise ValidationError(
                "Rule time periods overlap with time periods of other rules.")

        return not other_overlap

    def valid_time_in_rule(self, start_time, end_time, border_cross):
        if border_cross:
            return self.hours_inside(
                start_time, end_time) <= self.max_inside_border_crossed
        return timedelta_to_hours(end_time - start_time) <= self.max_hours

    def hours_inside(self, start_time, end_time):
        return sum(
            period.hours_inside(start_time, end_time)
            for period in self.time_periods())

    def time_periods(self):
        return [
            self.Period(day_index, self) for day_index, _ in filter(
                lambda enumerate_obj: enumerate_obj[1] == "1",
                enumerate(format(self.start_days, "07b")[::-1]))
        ]
Example #25
0
class Quota(models.Model):
    number_of_reservations = models.IntegerField(
        default=1, verbose_name=_("Number of reservations"))
    diminishing = models.BooleanField(default=False,
                                      verbose_name=_("Diminishing"))
    ignore_rules = models.BooleanField(default=False,
                                       verbose_name=_("Ignores rules"))
    all = models.BooleanField(default=False, verbose_name=_("All users"))
    user = models.ForeignKey(User,
                             on_delete=models.CASCADE,
                             null=True,
                             verbose_name=_("User"))
    machine_type = MachineTypeField(null=True, verbose_name=_("Machine type"))

    class Meta:
        permissions = (
            ("can_create_event_reservation", "Can create event reservation"),
            ("can_edit_quota", "Can edit quotas"),
        )

    def get_active_reservations(self, user):
        if self.diminishing:
            return self.reservation_set.all()
        reservation_set = self.reservation_set if not self.all else self.reservation_set.filter(
            user=user)
        return reservation_set.all().filter(end_time__gte=timezone.now())

    def can_make_more_reservations(self, user):
        return self.number_of_reservations != self.get_active_reservations(
            user).count()

    def is_valid_in(self, reservation):
        return (self.reservation_set.filter(pk=reservation.pk).exists() or
                self.can_make_more_reservations(reservation.user)) and \
               (self.ignore_rules or ReservationRule.valid_time(reservation.start_time, reservation.end_time,
                                                                reservation.machine.machine_type))

    @staticmethod
    def can_make_new_reservation(user, machine_type):
        return any(
            quota.can_make_more_reservations(user)
            for quota in Quota.get_user_quotas(user, machine_type))

    @staticmethod
    def get_user_quotas(user, machine_type):
        return Quota.objects.filter(Q(user=user) | Q(all=True)).filter(
            machine_type=machine_type)

    @staticmethod
    def get_best_quota(reservation):
        """
        Selects the best quota for the given reservation, by preferring non-diminishing quotas that do not ignore the
        rules
        :param reservation: The reservation to check
        :return: The best quota, that can handle the given reservation, or None if none can
        """
        valid_quotas = [
            quota for quota in Quota.get_user_quotas(
                reservation.user, reservation.machine.machine_type)
            if quota.is_valid_in(reservation)
        ]

        if not valid_quotas:
            return None

        best_quota = valid_quotas[0]
        for quota in valid_quotas[1:]:
            if best_quota.diminishing:
                if not quota.diminishing or best_quota.ignore_rules and not quota.ignore_rules:
                    best_quota = quota
            elif best_quota.ignore_rules and not quota.ignore_rules:
                best_quota = quota

        return best_quota

    @staticmethod
    def can_make_reservation(reservation):
        return Quota.get_best_quota(reservation) is not None