# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later from django.db import transaction from django.contrib.auth.mixins import LoginRequiredMixin from django.http import HttpResponseRedirect from django_tables2.views import MultiTableMixin from django.urls import reverse from django.utils.translation import gettext_lazy as _ from django.utils import timezone from django.views.generic import DetailView, UpdateView from django.views.generic.list import ListView from django.forms import HiddenInput from permission.backends import PermissionBackend from permission.views import ProtectQuerysetMixin, ProtectedCreateView from .forms import AddIngredientForms, BasicFoodForms, QRCodeForms, TransformedFoodForms from .models import BasicFood, Food, QRCode, TransformedFood from .tables import TransformedFoodTable class AddIngredientView(ProtectQuerysetMixin, UpdateView): """ A view to add an ingredient """ model = Food template_name = 'food/add_ingredient_form.html' extra_context = {"title": _("Add the ingredient")} form_class = AddIngredientForms def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) context["pk"] = self.kwargs["pk"] return context @transaction.atomic def form_valid(self, form): form.instance.creater = self.request.user food = Food.objects.get(pk=self.kwargs['pk']) add_ingredient_form = AddIngredientForms(data=self.request.POST) if food.is_ready: form.add_error(None, _("The product is already prepared")) return self.form_invalid(form) if not add_ingredient_form.is_valid(): return self.form_invalid(form) # We flip logic ""fully used = not is_active"" food.is_active = not food.is_active # Save the aliment and the allergens associed for transformed_pk in self.request.POST.getlist('ingredient'): transformed = TransformedFood.objects.get(pk=transformed_pk) if not transformed.is_ready: transformed.ingredient.add(food) transformed.update() food.save() return HttpResponseRedirect(self.get_success_url()) def get_success_url(self, **kwargs): return reverse('food:food_list') class BasicFoodUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView): """ A view to update a basic food """ model = BasicFood form_class = BasicFoodForms template_name = 'food/basicfood_form.html' extra_context = {"title": _("Update an aliment")} @transaction.atomic def form_valid(self, form): form.instance.creater = self.request.user basic_food_form = BasicFoodForms(data=self.request.POST) if not basic_food_form.is_valid(): return self.form_invalid(form) ans = super().form_valid(form) form.instance.update() return ans def get_success_url(self, **kwargs): self.object.refresh_from_db() return reverse('food:food_view', kwargs={"pk": self.object.pk}) def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) return context class FoodView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView): """ A view to see a food """ model = Food extra_context = {"title": _("Details of:")} context_object_name = "food" def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) context["can_update"] = PermissionBackend.check_perm(self.request, "food.change_food") context["can_add_ingredient"] = PermissionBackend.check_perm(self.request, "food.change_transformedfood") return context class QRCodeBasicFoodCreateView(ProtectQuerysetMixin, ProtectedCreateView): ##################################################################### # TO DO # - this feature is very pratical for meat or fish, nevertheless we can implement this later # - fix picture save # - implement solution crop and convert image (reuse or recode ImageForm from members apps) ##################################################################### """ A view to add a basic food with a qrcode """ model = BasicFood form_class = BasicFoodForms template_name = 'food/basicfood_form.html' extra_context = {"title": _("Add a new basic food with QRCode")} @transaction.atomic def form_valid(self, form): form.instance.creater = self.request.user basic_food_form = BasicFoodForms(data=self.request.POST) 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.is_ready = False basic_food.is_active = True basic_food.was_eaten = False basic_food._force_save = True basic_food.save() basic_food.refresh_from_db() qrcode = QRCode() qrcode.qr_code_number = self.kwargs['slug'] qrcode.food_container = basic_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): # We choose a club which may work or BDE else owner_id = 1 for membership in self.request.user.memberships.all(): club_id = membership.club.id food = BasicFood(name="", expiry_date=timezone.now(), owner_id=club_id) if PermissionBackend.check_perm(self.request, "food.add_basicfood", food): owner_id = club_id return BasicFood( name="", expiry_date=timezone.now(), owner_id=owner_id, ) def get_context_data(self, **kwargs): # Some field are hidden on create context = super().get_context_data(**kwargs) form = context['form'] form.fields['is_active'].widget = HiddenInput() form.fields['was_eaten'].widget = HiddenInput() return context class QRCodeCreateView(ProtectQuerysetMixin, ProtectedCreateView): """ A view to add a new qrcode """ model = QRCode template_name = 'food/create_qrcode_form.html' form_class = QRCodeForms extra_context = {"title": _("Add a new QRCode")} def get(self, *args, **kwargs): qrcode = kwargs["slug"] if self.model.objects.filter(qr_code_number=qrcode).count() > 0: return HttpResponseRedirect(reverse("food:qrcode_view", kwargs=kwargs)) elif not TransformedFood.objects.filter(is_ready=False, was_eaten=False, is_active=True).count() > 0: return HttpResponseRedirect(reverse("food:qrcode_basic_create", kwargs=kwargs)) else: return super().get(*args, **kwargs) def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) context["slug"] = self.kwargs["slug"] return context @transaction.atomic def form_valid(self, form): form.instance.creater = self.request.user qrcode_food_form = QRCodeForms(data=self.request.POST) if not qrcode_food_form.is_valid(): return self.form_invalid(form) # Save the qrcode qrcode = form.save(commit=False) qrcode.qr_code_number = self.kwargs["slug"] qrcode._force_save = True qrcode.save() qrcode.refresh_from_db() qrcode.food_container.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 QRCode( qr_code_number=self.kwargs["slug"], food_container_id=1 ) class QRCodeView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView): """ A view to see a qrcode """ model = QRCode extra_context = {"title": _("QRCode")} context_object_name = "qrcode" slug_field = "qr_code_number" def get(self, *args, **kwargs): qrcode = kwargs["slug"] if self.model.objects.filter(qr_code_number=qrcode).count() > 0: return super().get(*args, **kwargs) else: return HttpResponseRedirect(reverse("food:qrcode_create", kwargs=kwargs)) def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) qr_code_number = self.kwargs['slug'] qrcode = self.model.objects.get(qr_code_number=qr_code_number) model = qrcode.food_container.polymorphic_ctype.model if model == "basicfood": context["can_update_basic"] = PermissionBackend.check_perm(self.request, "food.change_basicfood") context["can_view_detail"] = PermissionBackend.check_perm(self.request, "food.view_basicfood") if model == "transformedfood": context["can_update_transformed"] = PermissionBackend.check_perm(self.request, "food.change_transformedfood") context["can_view_detail"] = PermissionBackend.check_perm(self.request, "food.view_transformedfood") context["can_add_ingredient"] = PermissionBackend.check_perm(self.request, "food.change_transformedfood") return context class TransformedFoodCreateView(ProtectQuerysetMixin, ProtectedCreateView): """ A view to add a tranformed food """ model = TransformedFood template_name = 'food/transformedfood_form.html' form_class = TransformedFoodForms extra_context = {"title": _("Add a new meal")} @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) transformed_food.expiry_date = transformed_food.creation_date transformed_food.is_active = True transformed_food.is_ready = False transformed_food.was_eaten = False transformed_food._force_save = True transformed_food.save() transformed_food.refresh_from_db() ans = super().form_valid(form) transformed_food.update() return ans def get_success_url(self, **kwargs): self.object.refresh_from_db() return reverse('food:food_view', kwargs={"pk": self.object.pk}) def get_sample_object(self): # We choose a club which may work or BDE else owner_id = 1 for membership in self.request.user.memberships.all(): club_id = membership.club.id food = TransformedFood(name="", creation_date=timezone.now(), expiry_date=timezone.now(), owner_id=club_id) if PermissionBackend.check_perm(self.request, "food.add_transformedfood", food): owner_id = club_id break return TransformedFood( name="", owner_id=owner_id, creation_date=timezone.now(), expiry_date=timezone.now(), ) def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) # Some field are hidden on create form = context['form'] form.fields['is_active'].widget = HiddenInput() form.fields['is_ready'].widget = HiddenInput() form.fields['was_eaten'].widget = HiddenInput() form.fields['shelf_life'].widget = HiddenInput() return context class TransformedFoodUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView): """ A view to update transformed product """ model = TransformedFood template_name = 'food/transformedfood_form.html' form_class = TransformedFoodForms extra_context = {'title': _('Update a meal')} @transaction.atomic def form_valid(self, form): form.instance.creater = self.request.user transformedfood_form = TransformedFoodForms(data=self.request.POST) if not transformedfood_form.is_valid(): return self.form_invalid(form) ans = super().form_valid(form) form.instance.update() return ans def get_success_url(self, **kwargs): self.object.refresh_from_db() return reverse('food:food_view', kwargs={"pk": self.object.pk}) def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) return context class TransformedListView(ProtectQuerysetMixin, LoginRequiredMixin, MultiTableMixin, ListView): """ Displays ready TransformedFood """ model = TransformedFood tables = [TransformedFoodTable, TransformedFoodTable, TransformedFoodTable] extra_context = {"title": _("Transformed food")} def get_queryset(self, **kwargs): return super().get_queryset(**kwargs).distinct() def get_tables(self): tables = super().get_tables() tables[0].prefix = "all-" tables[1].prefix = "open-" tables[2].prefix = "served-" return tables def get_tables_data(self): # first table = all transformed food, second table = free, third = served return [ self.get_queryset().order_by("-creation_date"), TransformedFood.objects.filter(is_ready=True, is_active=True, was_eaten=False, expiry_date__lt=timezone.now()) .filter(PermissionBackend.filter_queryset(self.request, TransformedFood, "view")) .distinct() .order_by("-creation_date"), TransformedFood.objects.filter(is_ready=True, is_active=True, was_eaten=False, expiry_date__gte=timezone.now()) .filter(PermissionBackend.filter_queryset(self.request, TransformedFood, "view")) .distinct() .order_by("-creation_date") ] def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) # We choose a club which should work for membership in self.request.user.memberships.all(): club_id = membership.club.id food = TransformedFood( name="", owner_id=club_id, creation_date=timezone.now(), expiry_date=timezone.now(), ) if PermissionBackend.check_perm(self.request, "food.add_transformedfood", food): context['can_create_meal'] = True break tables = context["tables"] for name, table in zip(["table", "open", "served"], tables): context[name] = table return context