models.py 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. # -*- coding: utf-8 -*-
  2. from __future__ import unicode_literals
  3. from django.db import models
  4. from django.conf import settings
  5. from django.utils import timezone
  6. from .fields import MACAddressField
  7. class ItemType(models.Model):
  8. name = models.CharField(max_length=100, verbose_name='nom')
  9. def __unicode__(self):
  10. return self.name
  11. class Meta:
  12. verbose_name = 'type d’objet'
  13. verbose_name_plural = 'types d’objet'
  14. class ItemQuerySet(models.QuerySet):
  15. def _get_borrowed_pks(self):
  16. return Loan.objects.running().values_list('item', flat=True)
  17. def available(self):
  18. return self.exclude(pk__in=self._get_borrowed_pks())
  19. def borrowed(self):
  20. return self.filter(pk__in=self._get_borrowed_pks())
  21. class Item(models.Model):
  22. type = models.ForeignKey(ItemType, verbose_name='type de matériel',
  23. related_name='items')
  24. designation = models.CharField(max_length=100, verbose_name='désignation')
  25. storage = models.ForeignKey(
  26. 'Storage', related_name='items',
  27. verbose_name='Lieu de stockage',
  28. null=True, blank=True,
  29. help_text='Laisser vide si inconnu')
  30. mac_address = MACAddressField(
  31. verbose_name='adresse MAC',
  32. blank=True, null=True, unique=True,
  33. help_text="préférable au n° de série si possible")
  34. serial = models.CharField(
  35. verbose_name='N° de série',
  36. max_length=250, blank=True, null=True, unique=True,
  37. help_text='ou toute autre référence unique')
  38. buy_date = models.DateField(verbose_name='date d’achat' , blank=True , null=True)
  39. owner = models.ForeignKey(
  40. settings.AUTH_USER_MODEL,
  41. verbose_name='Propriétaire',
  42. related_name='items',
  43. null=True, blank=True,
  44. help_text="dans le cas de matériel n'appartenant pas à l'association")
  45. deployed = models.BooleanField(verbose_name='déployé', default=False,
  46. help_text='Cocher si le matériel est en production')
  47. comment = models.TextField(verbose_name='commentaire', blank=True,
  48. null=True)
  49. objects = ItemQuerySet().as_manager()
  50. def __unicode__(self):
  51. return self.designation
  52. def save(self, *args, **kwargs):
  53. # workaround for unique=True, null=True
  54. # see https://code.djangoproject.com/ticket/4136#comment:33
  55. self.mac_address = self.mac_address or None
  56. self.serial = self.serial or None
  57. return super(Item, self).save(*args, **kwargs)
  58. def get_current_loan(self):
  59. """
  60. Returns the current Loan for this Item, if exists, or None.
  61. """
  62. try:
  63. return self.loans.get(loan_date_end__isnull=True)
  64. except Loan.DoesNotExist:
  65. return None
  66. def is_available(self):
  67. """
  68. Returns the status of the Item. If a running loan exists,
  69. or if the item is deployed, returns False (else True).
  70. """
  71. return (not self.deployed) and (not self.loans.running().exists())
  72. is_available.boolean = True
  73. is_available.short_description = 'disponible'
  74. def get_mac_and_serial(self):
  75. mac = self.mac_address
  76. serial = self.serial
  77. if mac and serial:
  78. return "{} / {}".format(mac, serial)
  79. else:
  80. return mac or serial or ''
  81. class Meta:
  82. verbose_name = 'objet'
  83. def give_back(self, storage=None):
  84. self.storage = storage
  85. self.save()
  86. self.loans.running().update(
  87. loan_date_end=timezone.now())
  88. class LoanQuerySet(models.QuerySet):
  89. @staticmethod
  90. def _running_filter():
  91. return (
  92. models.Q(loan_date_end__gt=timezone.now()) |
  93. models.Q(loan_date_end__isnull=True))
  94. def running(self):
  95. return self.filter(self._running_filter())
  96. def finished(self):
  97. return self.exclude(self._running_filter())
  98. class Loan(models.Model):
  99. item = models.ForeignKey(Item, verbose_name='objet', related_name='loans')
  100. user = models.ForeignKey(settings.AUTH_USER_MODEL, verbose_name='membre',
  101. related_name='loans')
  102. loan_date = models.DateTimeField(verbose_name='date de prêt')
  103. loan_date_end = models.DateTimeField(verbose_name='date de fin de prêt',
  104. null=True, blank=True)
  105. notes = models.TextField(null=True, blank=True)
  106. def __unicode__(self):
  107. return 'prêt de {item} à {user}'.format(
  108. item=self.item, user=self.user)
  109. def get_mac_and_serial(self):
  110. return self.item.get_mac_and_serial()
  111. get_mac_and_serial.short_description = "Adresse MAC / n° de série"
  112. def user_can_close(self, user):
  113. return (not self.item.is_available()) and (self.user == user)
  114. def is_running(self):
  115. return not self.loan_date_end or self.loan_date_end > timezone.now()
  116. is_running.boolean = True
  117. is_running.short_description = 'En cours ?'
  118. class Meta:
  119. verbose_name = 'prêt d’objet'
  120. verbose_name_plural = 'prêts d’objets'
  121. objects = LoanQuerySet().as_manager()
  122. class Storage(models.Model):
  123. name = models.CharField(max_length=100, verbose_name='nom')
  124. notes = models.TextField(
  125. blank=True,
  126. help_text='Lisible par tous les adhérents')
  127. def __unicode__(self):
  128. return self.name
  129. def items_count(self):
  130. return self.items.count()
  131. items_count.short_description = 'Nb. items stockés'
  132. class Meta:
  133. verbose_name = 'lieu de stockage'
  134. verbose_name_plural = 'lieux de stockage'