1
0
mirror of https://gitlab.crans.org/bde/nk20 synced 2025-11-07 23:39:50 +01:00

Add model Recipe

This commit is contained in:
Ehouarn
2025-11-06 23:49:16 +01:00
parent 48b1ef9ec8
commit 0db794c70e
13 changed files with 613 additions and 22 deletions

View File

@@ -9,7 +9,8 @@ from django_tables2.views import SingleTableView, MultiTableMixin
from django.core.exceptions import PermissionDenied
from django.db import transaction
from django.db.models import Q, Count
from django.http import HttpResponseRedirect, Http404
from django.http import HttpResponseRedirect, Http404, JsonResponse
from django.views.decorators.http import require_GET
from django.views.generic import DetailView, UpdateView, CreateView
from django.views.generic.list import ListView
from django.views.generic.base import RedirectView
@@ -22,12 +23,13 @@ from activity.models import Activity
from permission.backends import PermissionBackend
from permission.views import ProtectQuerysetMixin, ProtectedCreateView, LoginRequiredMixin
from .models import Food, BasicFood, TransformedFood, QRCode, Order, Dish, Supplement
from .models import Food, BasicFood, TransformedFood, QRCode, Order, Dish, Supplement, Recipe
from .forms import QRCodeForms, BasicFoodForms, TransformedFoodForms, \
ManageIngredientsForm, ManageIngredientsFormSet, AddIngredientForms, \
BasicFoodUpdateForms, TransformedFoodUpdateForms, \
DishForm, SupplementFormSet, SupplementFormSetHelper, OrderForm
from .tables import FoodTable, DishTable, OrderTable
DishForm, SupplementFormSet, SupplementFormSetHelper, OrderForm, RecipeForm, \
RecipeIngredientsForm, RecipeIngredientsFormSet, UseRecipeForm
from .tables import FoodTable, DishTable, OrderTable, RecipeTable
from .utils import pretty_duration
@@ -118,6 +120,10 @@ class FoodListView(ProtectQuerysetMixin, LoginRequiredMixin, MultiTableMixin, Li
context['can_add_meal'] = PermissionBackend.check_perm(self.request, 'food.transformedfood_add')
context['can_add_recipe'] = PermissionBackend.check_perm(self.request, 'food.recipe_add')
context['can_view_recipes'] = PermissionBackend.check_perm(self.request, 'food.recipe_view')
context["open_activities"] = Activity.objects.filter(activity_type__name="Perm bouffe", open=True)
return context
@@ -329,7 +335,7 @@ class ManageIngredientsView(LoginRequiredMixin, UpdateView):
if not (ingredient.polymorphic_ctype.model == 'basicfood' and ingredient.date_type == 'DDM'):
self.object.expiry_date = min(self.object.expiry_date, ingredient.expiry_date)
self.object.allergens.set(self.object.allergens.union(ingredient.allergens.all()))
self.object.tracese.set(self.object.traces.union(ingredient.traces.all()))
self.object.traces.set(self.object.traces.union(ingredient.traces.all()))
self.object.save(old_ingredients=old_ingredients, old_allergens=old_allergens, old_traces=old_traces)
return HttpResponseRedirect(self.get_success_url())
@@ -355,6 +361,7 @@ class ManageIngredientsView(LoginRequiredMixin, UpdateView):
'qr_number': '' if qr.count() == 0 else qr[0].qr_code_number,
'fully_used': 'true' if ingredient.end_of_life else '',
})
return context
def get_success_url(self, **kwargs):
@@ -874,3 +881,166 @@ class KitchenView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableView):
table.columns.hide(field)
return table
class RecipeCreateView(ProtectQuerysetMixin, ProtectedCreateView):
"""
Create a recipe
"""
model = Recipe
form_class = RecipeForm
extra_context = {"title": _("Create a recipe")}
def get_sample_object(self):
return Recipe(name='Sample recipe')
@transaction.atomic
def form_valid(self, form):
formset = RecipeIngredientsFormSet(self.request.POST)
if formset.is_valid():
ingredients = [f.cleaned_data['name'] for f in formset if f.cleaned_data.get('name')]
self.object = form.save(commit=False)
self.object.ingredients = ingredients
self.object.save()
return super().form_valid(form)
else:
return self.form_invalid(form)
def get_context_data(self, *args, **kwargs):
context = super().get_context_data(*args, **kwargs)
context['form'] = RecipeIngredientsForm()
context['recipe_form'] = self.get_form()
if self.request.POST:
context['formset'] = RecipeIngredientsFormSet(self.request.POST,)
else:
context['formset'] = RecipeIngredientsFormSet()
return context
def get_success_url(self):
return reverse_lazy('food:recipe_create')
class RecipeListView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableView):
"""
List all recipes
"""
model = Recipe
table_class = RecipeTable
extra_context = {"title": _('All recipes')}
def get_context_data(self, *args, **kwargs):
context = super().get_context_data(*args, **kwargs)
context['can_add_recipe'] = PermissionBackend.check_perm(self.request, 'food.recipe_add')
return context
class RecipeDetailView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView):
"""
List all recipes
"""
model = Recipe
extra_context = {"title": _('Details of:')}
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["ingredients"] = self.object.ingredients
context["update"] = PermissionBackend.check_perm(self.request, "food.change_recipe")
return context
class RecipeUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView):
"""
Create a recipe
"""
model = Recipe
form_class = RecipeForm
extra_context = {"title": _("Create a recipe")}
def get_sample_object(self):
return Recipe(name='Sample recipe')
@transaction.atomic
def form_valid(self, form):
formset = RecipeIngredientsFormSet(self.request.POST)
if formset.is_valid():
ingredients = [f.cleaned_data['name'] for f in formset if f.cleaned_data.get('name')]
self.object = form.save(commit=False)
self.object.ingredients = ingredients
self.object.save()
return super().form_valid(form)
else:
return self.form_invalid(form)
def get_context_data(self, *args, **kwargs):
context = super().get_context_data(*args, **kwargs)
context['form'] = RecipeIngredientsForm()
context['recipe_form'] = self.get_form()
if self.request.POST:
formset = RecipeIngredientsFormSet(self.request.POST,)
else:
formset = RecipeIngredientsFormSet()
ingredients = self.object.ingredients
context["ingredients_count"] = len(ingredients)
formset.extra += len(ingredients)
context["formset"] = formset
context["ingredients"] = []
for ingredient in ingredients:
context["ingredients"].append({"name": ingredient})
return context
def get_success_url(self):
return reverse_lazy('food:recipe_detail', kwargs={"pk": self.object.pk})
class UseRecipeView(LoginRequiredMixin, UpdateView):
"""
Add ingredients to a TransformedFood using a Recipe
"""
model = TransformedFood
fields = '__all__'
template_name = 'food/use_recipe_form.html'
extra_context = {"title": _("Use a recipe:")}
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["form"] = UseRecipeForm()
return context
@require_GET
def get_ingredients_for_recipe(request):
recipe_id = request.GET.get('recipe_id')
if not recipe_id:
return JsonResponse({'error': 'Missing recipe_id'}, status=400)
try:
recipe = Recipe.objects.get(pk=recipe_id)
except Recipe.DoesNotExist:
return JsonResponse({'error': 'Recipe not found'}, status=404)
# 🔧 Supporte les deux cas : ManyToMany ou simple liste
ingredients_field = recipe.ingredients
if hasattr(ingredients_field, "values_list"):
# Cas ManyToManyField
ingredient_names = list(ingredients_field.values_list('name', flat=True))
elif isinstance(ingredients_field, (list, tuple)):
# Cas liste directe
ingredient_names = ingredients_field
else:
return JsonResponse({'error': 'Unsupported ingredients type'}, status=500)
# Union des Foods dont le nom commence par un nom dingrédient
query = Q()
for name in ingredient_names:
query |= Q(name__istartswith=name)
qs = Food.objects.filter(query).distinct()
data = [{'id': f.id, 'name': f.name, 'qr_code_numbers': ", ".join(str(q.qr_code_number) for q in f.QR_code.all())} for f in qs]
return JsonResponse({'ingredients': data})