Automatic allergens and expiry_date update

This commit is contained in:
korenstin 2024-07-06 19:26:21 +02:00
parent 48462f2ffc
commit 226a2a6357
8 changed files with 94 additions and 76 deletions

View File

@ -2,6 +2,7 @@
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
from django.contrib import admin from django.contrib import admin
from django.db import transaction
from note_kfet.admin import admin_site from note_kfet.admin import admin_site
from .models import Allergen, BasicFood, QRCode, TransformedFood from .models import Allergen, BasicFood, QRCode, TransformedFood
@ -9,27 +10,29 @@ from .models import Allergen, BasicFood, QRCode, TransformedFood
@admin.register(QRCode, site=admin_site) @admin.register(QRCode, site=admin_site)
class QRCodeAdmin(admin.ModelAdmin): class QRCodeAdmin(admin.ModelAdmin):
""" pass
TEMPORARY
"""
@admin.register(BasicFood, site=admin_site) @admin.register(BasicFood, site=admin_site)
class BasicFoodAdmin(admin.ModelAdmin): class BasicFoodAdmin(admin.ModelAdmin):
""" @transaction.atomic
TEMPORARY def save_related(self, *args, **kwargs):
""" ans = super().save_related(*args, **kwargs)
args[1].instance.update()
return ans
@admin.register(TransformedFood, site=admin_site) @admin.register(TransformedFood, site=admin_site)
class TransformedFoodAdmin(admin.ModelAdmin): class TransformedFoodAdmin(admin.ModelAdmin):
""" exclude = ["allergens", "expiry_date"]
TEMPORARY
""" @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) @admin.register(Allergen, site=admin_site)
class AllergenAdmin(admin.ModelAdmin): class AllergenAdmin(admin.ModelAdmin):
""" pass
TEMPORARY
"""

View File

@ -89,7 +89,7 @@ class TransformedFoodForms(forms.ModelForm):
class Meta: class Meta:
model = TransformedFood model = TransformedFood
fields = ('name', 'creation_date', 'owner', 'is_active', 'allergens') fields = ('name', 'creation_date', 'owner', 'is_active')
widgets = { widgets = {
"owner": Autocomplete( "owner": Autocomplete(
model=Club, model=Club,

View File

@ -1,6 +1,8 @@
# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay # Copyright (C) 2018-2024 by BDE ENS Paris-Saclay
# SPDX-License-Identifier: GPL-3.0-or-later # SPDX-License-Identifier: GPL-3.0-or-later
from datetime import timedelta
from django.db import models, transaction from django.db import models, transaction
from django.utils import timezone from django.utils import timezone
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
@ -120,6 +122,23 @@ class BasicFood(Food):
# upload_to='label/', # 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: class Meta:
verbose_name = _('Basic food') verbose_name = _('Basic food')
verbose_name_plural = _('Basic foods') verbose_name_plural = _('Basic foods')
@ -146,6 +165,47 @@ class TransformedFood(Food):
verbose_name=_('is active'), 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: class Meta:
verbose_name = _('Transformed food') verbose_name = _('Transformed food')
verbose_name_plural = _('Transformed foods') verbose_name_plural = _('Transformed foods')

View File

@ -12,6 +12,15 @@ SPDX-License-Identifier: GPL-3.0-or-later
</h3> </h3>
<div class="card-body"> <div class="card-body">
<p>name : {{ food.name }}</p> <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> <a href="{% url "food:basic_update" pk=food.pk %}">Update</a>
</div> </div>
</div> </div>

View File

@ -10,15 +10,10 @@ SPDX-License-Identifier: GPL-3.0-or-later
HTML not finished <br> HTML not finished <br>
{{ title }} {{ title }}
</h3> </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"> <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"> <form method="post">
{% csrf_token %} {% csrf_token %}
{{ form|crispy }} {{ form|crispy }}

View File

@ -13,6 +13,8 @@ SPDX-License-Identifier: GPL-3.0-or-later
<div class="card-body"> <div class="card-body">
<p>name : {{ food.name }}</p> <p>name : {{ food.name }}</p>
<p>owner : {{ food.owner }}</p> <p>owner : {{ food.owner }}</p>
<p>creation_date : {{ food.creation_date }}</p>
<p>expiry_date : {{ food.expiry_date }}</p>
<p>allergens :</p> <p>allergens :</p>
<ul> <ul>
{% for allergen in food.allergens.iterator %} {% for allergen in food.allergens.iterator %}

View File

@ -15,7 +15,6 @@ urlpatterns = [
path('<int:slug>/create_qrcode', views.QRCodeCreateView.as_view(), name='qrcode_create'), path('<int:slug>/create_qrcode', views.QRCodeCreateView.as_view(), name='qrcode_create'),
path('create', views.FoodCreateView.as_view(), name='food_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/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('create/transformed', views.TransformedFoodCreateView.as_view(), name='transformed_create'),
path('update/basic/<int:pk>', views.BasicFoodUpdateView.as_view(), name='basic_update'), path('update/basic/<int:pk>', views.BasicFoodUpdateView.as_view(), name='basic_update'),

View File

@ -45,9 +45,7 @@ class AddIngredientView(ProtectQuerysetMixin, FormView):
for transformed_pk in self.request.POST.getlist('ingredient'): for transformed_pk in self.request.POST.getlist('ingredient'):
transformed = TransformedFood.objects.get(pk=transformed_pk) transformed = TransformedFood.objects.get(pk=transformed_pk)
transformed.ingredient.add(food) transformed.ingredient.add(food)
transformed._force_save = True transformed.update()
transformed.save()
transformed.refresh_from_db()
return super().form_valid(form) return super().form_valid(form)
@ -77,14 +75,9 @@ class BasicFoodUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView):
if not basic_food_form.is_valid(): if not basic_food_form.is_valid():
return self.form_invalid(form) return self.form_invalid(form)
# Save the aliment and the allergens associed ans = super().form_valid(form)
basic_food = form.save(commit=False) form.instance.update()
# We assume the date of labeling and the same as the date of arrival return ans
basic_food.arrival_date = timezone.now()
basic_food._force_save = True
basic_food.save()
basic_food.refresh_from_db()
return super().form_valid(form)
def get_success_url(self, **kwargs): def get_success_url(self, **kwargs):
self.object.refresh_from_db() 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): class QRCodeView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView):
""" """
A view to see a qrcode A view to see a qrcode