mirror of
				https://gitlab.crans.org/bde/nk20
				synced 2025-11-04 09:12:11 +01:00 
			
		
		
		
	Automatic allergens and expiry_date update
This commit is contained in:
		@@ -2,6 +2,7 @@
 | 
			
		||||
# SPDX-License-Identifier: GPL-3.0-or-later
 | 
			
		||||
 | 
			
		||||
from django.contrib import admin
 | 
			
		||||
from django.db import transaction
 | 
			
		||||
from note_kfet.admin import admin_site
 | 
			
		||||
 | 
			
		||||
from .models import Allergen, BasicFood, QRCode, TransformedFood
 | 
			
		||||
@@ -9,27 +10,29 @@ from .models import Allergen, BasicFood, QRCode, TransformedFood
 | 
			
		||||
 | 
			
		||||
@admin.register(QRCode, site=admin_site)
 | 
			
		||||
class QRCodeAdmin(admin.ModelAdmin):
 | 
			
		||||
    """
 | 
			
		||||
    TEMPORARY
 | 
			
		||||
    """
 | 
			
		||||
    pass
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@admin.register(BasicFood, site=admin_site)
 | 
			
		||||
class BasicFoodAdmin(admin.ModelAdmin):
 | 
			
		||||
    """
 | 
			
		||||
    TEMPORARY
 | 
			
		||||
    """
 | 
			
		||||
    @transaction.atomic
 | 
			
		||||
    def save_related(self, *args, **kwargs):
 | 
			
		||||
        ans = super().save_related(*args, **kwargs)
 | 
			
		||||
        args[1].instance.update()
 | 
			
		||||
        return ans
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@admin.register(TransformedFood, site=admin_site)
 | 
			
		||||
class TransformedFoodAdmin(admin.ModelAdmin):
 | 
			
		||||
    """
 | 
			
		||||
    TEMPORARY
 | 
			
		||||
    """
 | 
			
		||||
    exclude = ["allergens", "expiry_date"]
 | 
			
		||||
 | 
			
		||||
    @transaction.atomic
 | 
			
		||||
    def save_related(self, *args, **kwargs):
 | 
			
		||||
        ans = super().save_related(*args, **kwargs)
 | 
			
		||||
        args[1].instance.update()
 | 
			
		||||
        return ans
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@admin.register(Allergen, site=admin_site)
 | 
			
		||||
class AllergenAdmin(admin.ModelAdmin):
 | 
			
		||||
    """
 | 
			
		||||
    TEMPORARY
 | 
			
		||||
    """
 | 
			
		||||
    pass
 | 
			
		||||
 
 | 
			
		||||
@@ -89,7 +89,7 @@ class TransformedFoodForms(forms.ModelForm):
 | 
			
		||||
 | 
			
		||||
    class Meta:
 | 
			
		||||
        model = TransformedFood
 | 
			
		||||
        fields = ('name', 'creation_date', 'owner', 'is_active', 'allergens')
 | 
			
		||||
        fields = ('name', 'creation_date', 'owner', 'is_active')
 | 
			
		||||
        widgets = {
 | 
			
		||||
            "owner": Autocomplete(
 | 
			
		||||
                model=Club,
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,8 @@
 | 
			
		||||
# 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 _
 | 
			
		||||
@@ -120,6 +122,23 @@ class BasicFood(Food):
 | 
			
		||||
    #     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')
 | 
			
		||||
@@ -146,6 +165,47 @@ class TransformedFood(Food):
 | 
			
		||||
        verbose_name=_('is active'),
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    @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 + timedelta(days=3)
 | 
			
		||||
        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')
 | 
			
		||||
 
 | 
			
		||||
@@ -12,6 +12,15 @@ SPDX-License-Identifier: GPL-3.0-or-later
 | 
			
		||||
  </h3>
 | 
			
		||||
  <div class="card-body">
 | 
			
		||||
    <p>name : {{ food.name }}</p>
 | 
			
		||||
    <p>owner : {{ food.owner }}</p>
 | 
			
		||||
    <p>arrival_date : {{ food.arrival_date }}</p>
 | 
			
		||||
    <p>expiry_date : {{ food.expiry_date }}</p>
 | 
			
		||||
    <p>allergens :</p>
 | 
			
		||||
    <ul>
 | 
			
		||||
      {% for allergen in food.allergens.iterator %}
 | 
			
		||||
        <li>{{ allergen.name }}</li>
 | 
			
		||||
      {% endfor %}
 | 
			
		||||
    </ul>
 | 
			
		||||
    <a href="{% url "food:basic_update" pk=food.pk %}">Update</a>
 | 
			
		||||
  </div>
 | 
			
		||||
</div>
 | 
			
		||||
 
 | 
			
		||||
@@ -10,15 +10,10 @@ SPDX-License-Identifier: GPL-3.0-or-later
 | 
			
		||||
    HTML not finished <br>
 | 
			
		||||
    {{ title }}
 | 
			
		||||
  </h3>
 | 
			
		||||
  <div class="row">
 | 
			
		||||
     <div class="col-xl-12">
 | 
			
		||||
        <div class="btn-group btn-block">
 | 
			
		||||
           <a href="{% url "food:qrcode_basic_create" slug=slug %}" class="btn btn-sm btn-outline-primary">Basic</a>
 | 
			
		||||
           <a href="{% url "food:qrcode_transformed_create" slug=slug %}" class="btn btn-sm btn-outline-primary">Transformed</a>
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
  </div>
 | 
			
		||||
  <div class="card-body" id="form">
 | 
			
		||||
    <a class="btn btn-sm btn-success" href="{% url "food:qrcode_basic_create" slug=slug %}" data-turbolinks="false">
 | 
			
		||||
      New basic food
 | 
			
		||||
    </a>
 | 
			
		||||
    <form method="post">
 | 
			
		||||
      {%  csrf_token %}
 | 
			
		||||
      {{ form|crispy }}
 | 
			
		||||
 
 | 
			
		||||
@@ -13,6 +13,8 @@ SPDX-License-Identifier: GPL-3.0-or-later
 | 
			
		||||
  <div class="card-body">
 | 
			
		||||
    <p>name : {{ food.name }}</p>
 | 
			
		||||
    <p>owner : {{ food.owner }}</p>
 | 
			
		||||
    <p>creation_date : {{ food.creation_date }}</p>
 | 
			
		||||
    <p>expiry_date : {{ food.expiry_date }}</p>
 | 
			
		||||
    <p>allergens :</p>
 | 
			
		||||
    <ul>
 | 
			
		||||
      {% for allergen in food.allergens.iterator %}
 | 
			
		||||
 
 | 
			
		||||
@@ -15,7 +15,6 @@ urlpatterns = [
 | 
			
		||||
    path('<int:slug>/create_qrcode', views.QRCodeCreateView.as_view(), name='qrcode_create'),
 | 
			
		||||
    path('create', views.FoodCreateView.as_view(), name='food_create'),
 | 
			
		||||
    path('<int:slug>/create_qrcode/basic', views.QRCodeBasicFoodCreateView.as_view(), name='qrcode_basic_create'),
 | 
			
		||||
    path('<int:slug>/create_qrcode/transformed', views.QRCodeTransformedFoodCreateView.as_view(), name='qrcode_transformed_create'),
 | 
			
		||||
    path('create/transformed', views.TransformedFoodCreateView.as_view(), name='transformed_create'),
 | 
			
		||||
 | 
			
		||||
    path('update/basic/<int:pk>', views.BasicFoodUpdateView.as_view(), name='basic_update'),
 | 
			
		||||
 
 | 
			
		||||
@@ -45,9 +45,7 @@ class AddIngredientView(ProtectQuerysetMixin, FormView):
 | 
			
		||||
        for transformed_pk in self.request.POST.getlist('ingredient'):
 | 
			
		||||
            transformed = TransformedFood.objects.get(pk=transformed_pk)
 | 
			
		||||
            transformed.ingredient.add(food)
 | 
			
		||||
            transformed._force_save = True
 | 
			
		||||
            transformed.save()
 | 
			
		||||
            transformed.refresh_from_db()
 | 
			
		||||
            transformed.update()
 | 
			
		||||
 | 
			
		||||
        return super().form_valid(form)
 | 
			
		||||
 | 
			
		||||
@@ -77,14 +75,9 @@ class BasicFoodUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView):
 | 
			
		||||
        if not basic_food_form.is_valid():
 | 
			
		||||
            return self.form_invalid(form)
 | 
			
		||||
 | 
			
		||||
        # Save the aliment and the allergens associed
 | 
			
		||||
        basic_food = form.save(commit=False)
 | 
			
		||||
        # We assume the date of labeling and the same as the date of arrival
 | 
			
		||||
        basic_food.arrival_date = timezone.now()
 | 
			
		||||
        basic_food._force_save = True
 | 
			
		||||
        basic_food.save()
 | 
			
		||||
        basic_food.refresh_from_db()
 | 
			
		||||
        return super().form_valid(form)
 | 
			
		||||
        ans = super().form_valid(form)
 | 
			
		||||
        form.instance.update()
 | 
			
		||||
        return ans
 | 
			
		||||
 | 
			
		||||
    def get_success_url(self, **kwargs):
 | 
			
		||||
        self.object.refresh_from_db()
 | 
			
		||||
@@ -206,49 +199,6 @@ class QRCodeCreateView(ProtectQuerysetMixin, ProtectedCreateView):
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class QRCodeTransformedFoodCreateView(ProtectQuerysetMixin, ProtectedCreateView):
 | 
			
		||||
    """
 | 
			
		||||
    A view to add a transformed food with a qrcode
 | 
			
		||||
    """
 | 
			
		||||
    model = TransformedFood
 | 
			
		||||
    template_name = 'food/transformed_food_form.html'
 | 
			
		||||
    form_class = TransformedFoodForms
 | 
			
		||||
    extra_context = {"title": _("Add a new transformed food with QRCode")}
 | 
			
		||||
 | 
			
		||||
    @transaction.atomic
 | 
			
		||||
    def form_valid(self, form):
 | 
			
		||||
        form.instance.creater = self.request.user
 | 
			
		||||
        transformed_food_form = TransformedFoodForms(data=self.request.POST)
 | 
			
		||||
        if not transformed_food_form.is_valid():
 | 
			
		||||
            return self.form_invalid(form)
 | 
			
		||||
 | 
			
		||||
        # Save the aliment and allergens associated
 | 
			
		||||
        transformed_food = form.save(commit=False)
 | 
			
		||||
        # Without microbiological analyzes, the storage time is 3 days
 | 
			
		||||
        transformed_food.expiry_date = transformed_food.creation_date + timedelta(days=3)
 | 
			
		||||
        transformed_food.is_ready = True
 | 
			
		||||
        transformed_food._force_save = True
 | 
			
		||||
        transformed_food.save()
 | 
			
		||||
        transformed_food.refresh_from_db()
 | 
			
		||||
 | 
			
		||||
        qrcode = QRCode()
 | 
			
		||||
        qrcode.qr_code_number = self.kwargs['slug']
 | 
			
		||||
        qrcode.food_container = transformed_food
 | 
			
		||||
        qrcode.save()
 | 
			
		||||
 | 
			
		||||
        return super().form_valid(form)
 | 
			
		||||
 | 
			
		||||
    def get_success_url(self, **kwargs):
 | 
			
		||||
        self.object.refresh_from_db()
 | 
			
		||||
        return reverse('food:qrcode_view', kwargs={"slug": self.kwargs['slug']})
 | 
			
		||||
 | 
			
		||||
    def get_sample_object(self):
 | 
			
		||||
        return TransformedFood(
 | 
			
		||||
            name="",
 | 
			
		||||
            creation_date=timezone.now(),
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class QRCodeView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView):
 | 
			
		||||
    """
 | 
			
		||||
    A view to see a qrcode
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user