mirror of
				https://gitlab.crans.org/bde/nk20
				synced 2025-10-31 15:50:03 +01:00 
			
		
		
		
	
		
			
				
	
	
		
			227 lines
		
	
	
		
			5.9 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			227 lines
		
	
	
		
			5.9 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| # Copyright (C) 2018-2024 by BDE ENS Paris-Saclay
 | |
| # SPDX-License-Identifier: GPL-3.0-or-later
 | |
| 
 | |
| from datetime import timedelta
 | |
| 
 | |
| from django.db import models, transaction
 | |
| from django.utils import timezone
 | |
| from django.utils.translation import gettext_lazy as _
 | |
| from member.models import Club
 | |
| from polymorphic.models import PolymorphicModel
 | |
| 
 | |
| 
 | |
| class QRCode(models.Model):
 | |
|     """
 | |
|     An QRCode model
 | |
|     """
 | |
|     qr_code_number = models.PositiveIntegerField(
 | |
|         verbose_name=_("QR-code number"),
 | |
|         unique=True,
 | |
|     )
 | |
| 
 | |
|     food_container = models.ForeignKey(
 | |
|         'Food',
 | |
|         on_delete=models.CASCADE,
 | |
|         related_name='QR_code',
 | |
|         verbose_name=_('food container'),
 | |
|     )
 | |
| 
 | |
|     class Meta:
 | |
|         verbose_name = _("QR-code")
 | |
|         verbose_name_plural = _("QR-codes")
 | |
| 
 | |
|     def __str__(self):
 | |
|         return _("QR-code number {qr_code_number}").format(qr_code_number=self.qr_code_number)
 | |
| 
 | |
| 
 | |
| class Allergen(models.Model):
 | |
|     """
 | |
|     A list of allergen and alimentary restrictions
 | |
|     """
 | |
|     name = models.CharField(
 | |
|         verbose_name=_('name'),
 | |
|         max_length=255,
 | |
|     )
 | |
| 
 | |
|     class Meta:
 | |
|         verbose_name = _('Allergen')
 | |
|         verbose_name_plural = _('Allergens')
 | |
| 
 | |
|     def __str__(self):
 | |
|         return self.name
 | |
| 
 | |
| 
 | |
| class Food(PolymorphicModel):
 | |
|     name = models.CharField(
 | |
|         verbose_name=_('name'),
 | |
|         max_length=255,
 | |
|     )
 | |
| 
 | |
|     owner = models.ForeignKey(
 | |
|         Club,
 | |
|         on_delete=models.PROTECT,
 | |
|         related_name='+',
 | |
|         verbose_name=_('owner'),
 | |
|     )
 | |
| 
 | |
|     allergens = models.ManyToManyField(
 | |
|         Allergen,
 | |
|         blank=True,
 | |
|         verbose_name=_('allergen'),
 | |
|     )
 | |
| 
 | |
|     expiry_date = models.DateTimeField(
 | |
|         verbose_name=_('expiry date'),
 | |
|         null=False,
 | |
|     )
 | |
| 
 | |
|     was_eaten = models.BooleanField(
 | |
|         default=False,
 | |
|         verbose_name=_('was eaten'),
 | |
|     )
 | |
| 
 | |
|     # is_ready != is_active : is_ready signifie que la nourriture est prête à être manger,
 | |
|     #                         is_active signifie que la nourriture n'est pas encore archivé
 | |
|     # il sert dans les cas où il est plus intéressant que de l'open soit conservé (confiture par ex)
 | |
| 
 | |
|     is_ready = models.BooleanField(
 | |
|         default=False,
 | |
|         verbose_name=_('is ready'),
 | |
|     )
 | |
| 
 | |
|     is_active = models.BooleanField(
 | |
|         default=True,
 | |
|         verbose_name=_('is active'),
 | |
|     )
 | |
| 
 | |
|     def __str__(self):
 | |
|         return self.name
 | |
| 
 | |
|     @transaction.atomic
 | |
|     def save(self, force_insert=False, force_update=False, using=None, update_fields=None):
 | |
|         return super().save(force_insert, force_update, using, update_fields)
 | |
| 
 | |
|     class Meta:
 | |
|         verbose_name = _('food')
 | |
|         verbose_name = _('foods')
 | |
| 
 | |
| 
 | |
| class BasicFood(Food):
 | |
|     """
 | |
|     Food which has been directly buy on supermarket
 | |
|     """
 | |
|     date_type = models.CharField(
 | |
|         max_length=255,
 | |
|         choices=(
 | |
|             ("DLC", "DLC"),
 | |
|             ("DDM", "DDM"),
 | |
|         )
 | |
|     )
 | |
| 
 | |
|     arrival_date = models.DateTimeField(
 | |
|         verbose_name=_('arrival date'),
 | |
|         default=timezone.now,
 | |
|     )
 | |
| 
 | |
|     # label = models.ImageField(
 | |
|     #     verbose_name=_('food label'),
 | |
|     #     max_length=255,
 | |
|     #     blank=False,
 | |
|     #     null=False,
 | |
|     #     upload_to='label/',
 | |
|     # )
 | |
| 
 | |
|     @transaction.atomic
 | |
|     def update_allergens(self):
 | |
|         # update parents
 | |
|         for parent in self.transformed_ingredient_inv.iterator():
 | |
|             parent.update_allergens()
 | |
| 
 | |
|     @transaction.atomic
 | |
|     def update_expiry_date(self):
 | |
|         # update parents
 | |
|         for parent in self.transformed_ingredient_inv.iterator():
 | |
|             parent.update_expiry_date()
 | |
| 
 | |
|     @transaction.atomic
 | |
|     def update(self):
 | |
|         self.update_allergens()
 | |
|         self.update_expiry_date()
 | |
| 
 | |
|     class Meta:
 | |
|         verbose_name = _('Basic food')
 | |
|         verbose_name_plural = _('Basic foods')
 | |
| 
 | |
| 
 | |
| class TransformedFood(Food):
 | |
|     """
 | |
|     Transformed food  are a mix between basic food and meal
 | |
|     """
 | |
|     creation_date = models.DateTimeField(
 | |
|         verbose_name=_('creation date'),
 | |
|     )
 | |
| 
 | |
|     ingredient = models.ManyToManyField(
 | |
|         Food,
 | |
|         blank=True,
 | |
|         symmetrical=False,
 | |
|         related_name='transformed_ingredient_inv',
 | |
|         verbose_name=_('transformed ingredient'),
 | |
|     )
 | |
| 
 | |
|     # Without microbiological analyzes, the storage time is 3 days
 | |
|     shelf_life = models.DurationField(
 | |
|         verbose_name=_("shelf life"),
 | |
|         default=timedelta(days=3),
 | |
|     )
 | |
| 
 | |
|     @transaction.atomic
 | |
|     def archive(self):
 | |
|         # When a meal are archived, if it was eaten, update ingredient fully used for this meal
 | |
|         raise NotImplementedError
 | |
| 
 | |
|     @transaction.atomic
 | |
|     def update_allergens(self):
 | |
|         # When allergens are changed, simply update the parents' allergens
 | |
|         old_allergens = list(self.allergens.all())
 | |
|         self.allergens.clear()
 | |
|         for ingredient in self.ingredient.iterator():
 | |
|             self.allergens.set(self.allergens.union(ingredient.allergens.all()))
 | |
| 
 | |
|         if old_allergens == list(self.allergens.all()):
 | |
|             return
 | |
|         super().save()
 | |
| 
 | |
|         # update parents
 | |
|         for parent in self.transformed_ingredient_inv.iterator():
 | |
|             parent.update_allergens()
 | |
| 
 | |
|     @transaction.atomic
 | |
|     def update_expiry_date(self):
 | |
|         # When expiry_date is changed, simply update the parents' expiry_date
 | |
|         old_expiry_date = self.expiry_date
 | |
|         self.expiry_date = self.creation_date + self.shelf_life
 | |
|         for ingredient in self.ingredient.iterator():
 | |
|             self.expiry_date = min(self.expiry_date, ingredient.expiry_date)
 | |
| 
 | |
|         if old_expiry_date == self.expiry_date:
 | |
|             return
 | |
|         super().save()
 | |
| 
 | |
|         # update parents
 | |
|         for parent in self.transformed_ingredient_inv.iterator():
 | |
|             parent.update_expiry_date()
 | |
| 
 | |
|     @transaction.atomic
 | |
|     def update(self):
 | |
|         self.update_allergens()
 | |
|         self.update_expiry_date()
 | |
| 
 | |
|     @transaction.atomic
 | |
|     def save(self, *args, **kwargs):
 | |
|         super().save(*args, **kwargs)
 | |
| 
 | |
|     class Meta:
 | |
|         verbose_name = _('Transformed food')
 | |
|         verbose_name_plural = _('Transformed foods')
 |