From 74de358953cd0d27d84630de4053fade8a309d15 Mon Sep 17 00:00:00 2001 From: quark Date: Fri, 17 May 2024 20:40:52 +0200 Subject: [PATCH 01/29] Update README.md --- README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/README.md b/README.md index 98fe3713..b825aa75 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,11 @@ +# Traçabilité de la bouffe + doc provisoire ici : https://pad.crans.org/p/tracabilite + et ici : https://pad.crans.org/p/noteBouffe + + + + + # NoteKfet 2020 [![License: GPL v3](https://img.shields.io/badge/License-GPL%20v3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0.txt) From 9e6342c929ee00c72a9c8bf611283baa8307580b Mon Sep 17 00:00:00 2001 From: quark Date: Fri, 17 May 2024 20:46:38 +0200 Subject: [PATCH 02/29] =?UTF-8?q?Cr=C3=A9ation=20de=20l'apps=20et=20de=20l?= =?UTF-8?q?a=20base=20de=20donn=C3=A9e?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/food/__init__.py | 0 apps/food/admin.py | 3 + apps/food/apps.py | 5 + apps/food/migrations/0001_initial.py | 96 ++++++++++ apps/food/migrations/__init__.py | 0 apps/food/models.py | 257 +++++++++++++++++++++++++++ apps/food/tests.py | 3 + apps/food/views.py | 3 + note_kfet/settings/base.py | 1 + 9 files changed, 368 insertions(+) create mode 100644 apps/food/__init__.py create mode 100644 apps/food/admin.py create mode 100644 apps/food/apps.py create mode 100644 apps/food/migrations/0001_initial.py create mode 100644 apps/food/migrations/__init__.py create mode 100644 apps/food/models.py create mode 100644 apps/food/tests.py create mode 100644 apps/food/views.py diff --git a/apps/food/__init__.py b/apps/food/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/apps/food/admin.py b/apps/food/admin.py new file mode 100644 index 00000000..8c38f3f3 --- /dev/null +++ b/apps/food/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/apps/food/apps.py b/apps/food/apps.py new file mode 100644 index 00000000..092961b7 --- /dev/null +++ b/apps/food/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class FoodkfetConfig(AppConfig): + name = 'foodkfet' diff --git a/apps/food/migrations/0001_initial.py b/apps/food/migrations/0001_initial.py new file mode 100644 index 00000000..46899dd6 --- /dev/null +++ b/apps/food/migrations/0001_initial.py @@ -0,0 +1,96 @@ +# Generated by Django 2.2.28 on 2024-05-17 18:44 + +from django.db import migrations, models +import django.db.models.deletion +import django.utils.timezone + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('member', '0011_profile_vss_charter_read'), + ] + + operations = [ + migrations.CreateModel( + name='Allergen', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('gluten', models.BooleanField(default=False, verbose_name='gluten')), + ('nut', models.BooleanField(default=False, verbose_name='nut')), + ('crustecean', models.BooleanField(default=False, verbose_name='crustacean')), + ('celery', models.BooleanField(default=False, verbose_name='celery')), + ('egg', models.BooleanField(default=False, verbose_name='egg')), + ('mustard', models.BooleanField(default=False, verbose_name='mustard')), + ('fish', models.BooleanField(default=False, verbose_name='fish')), + ('soy', models.BooleanField(default=False, verbose_name='soy')), + ('milk', models.BooleanField(default=False, verbose_name='milk')), + ('sulphite', models.BooleanField(default=False, verbose_name='sulphite')), + ('lupine', models.BooleanField(default=False, verbose_name='lupine')), + ('mollusc', models.BooleanField(default=False, verbose_name='mollusc')), + ('groundnut', models.BooleanField(default=False, verbose_name='groundnut')), + ('sesame', models.BooleanField(default=False, verbose_name='sesame')), + ('alcohol', models.BooleanField(default=False, verbose_name='alcohol')), + ], + options={ + 'verbose_name': 'Allergen', + 'verbose_name_plural': 'Allergens', + }, + ), + migrations.CreateModel( + name='Basic_food', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=255, verbose_name='name')), + ('is_DLC', models.BooleanField(default=False, verbose_name='is DLC')), + ('is_DDM', models.BooleanField(default=False, verbose_name='is DDM')), + ('expiry_date', models.DateTimeField(blank=True, default=django.utils.timezone.now, verbose_name='expiry date')), + ('label', models.ImageField(default='pic/default.png', max_length=255, upload_to='label/', verbose_name='food label')), + ('was_eaten', models.BooleanField(default=False, verbose_name='was eaten')), + ('allergen', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='+', to='food.Allergen', verbose_name='allergen')), + ('owner', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='+', to='member.Club', verbose_name='owner')), + ], + options={ + 'verbose_name': 'Basic food', + 'verbose_name_plural': 'Basic foods', + }, + ), + migrations.CreateModel( + name='QR_code', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('qr_code_number', models.PositiveIntegerField(verbose_name='QR-code number')), + ], + options={ + 'verbose_name': 'QR-code', + 'verbose_name_plural': 'QR-codes', + }, + ), + migrations.CreateModel( + name='Transformed_food', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=255, verbose_name='name')), + ('creation_date', models.DateTimeField(verbose_name='creation date')), + ('expiry_date', models.DateTimeField(verbose_name='expiry date')), + ('is_active', models.BooleanField(default=True, verbose_name='is active')), + ('was_eaten', models.BooleanField(default=False, verbose_name='was eaten')), + ('allergen', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='+', to='food.Allergen', verbose_name='allergen')), + ('basic_ingredient', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='+', to='food.Basic_food', verbose_name='basic ingredient')), + ('owner', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='+', to='member.Club', verbose_name='owner')), + ('qr_code', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='+', to='food.QR_code', verbose_name='QR code')), + ('transformed_ingredient', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='+', to='food.Transformed_food', verbose_name='transformed ingredient')), + ], + options={ + 'verbose_name': 'Transformed food', + 'verbose_name_plural': 'Transformed foods', + }, + ), + migrations.AddField( + model_name='basic_food', + name='qr_code', + field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='+', to='food.QR_code', verbose_name='QR code'), + ), + ] diff --git a/apps/food/migrations/__init__.py b/apps/food/migrations/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/apps/food/models.py b/apps/food/models.py new file mode 100644 index 00000000..acaa43f8 --- /dev/null +++ b/apps/food/models.py @@ -0,0 +1,257 @@ +# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# SPDX-License-Identifier: GPL-3.0-or-later + +from datetime import date + +from django.conf import settings +from django.contrib.auth.models import User +from django.core.exceptions import ValidationError +from django.core.validators import MinValueValidator +from django.db import models +from django.db.models import Q +from django.utils import timezone +from django.utils.translation import gettext_lazy as _ +from member.models import Club + + +class QR_code(models.Model): + """ + An QR_code model + """ + qr_code_number = models.PositiveIntegerField( + verbose_name=_("QR-code number"), + ) + + 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 + """ + + gluten = models.BooleanField( + default = False, + verbose_name = _('gluten'), + ) + + nut = models.BooleanField( + default = False, + verbose_name = _('nut'), + ) + + crustecean = models.BooleanField( + default = False, + verbose_name = _('crustacean'), + ) + + celery = models.BooleanField( + default = False, + verbose_name = _('celery'), + ) + + egg = models.BooleanField( + default = False, + verbose_name = _('egg'), + ) + + mustard = models.BooleanField( + default = False, + verbose_name = _('mustard'), + ) + + fish = models.BooleanField( + default = False, + verbose_name = _('fish'), + ) + + soy = models.BooleanField( + default = False, + verbose_name = _('soy'), + ) + + milk = models.BooleanField( + default = False, + verbose_name = _('milk'), + ) + + sulphite = models.BooleanField( + default = False, + verbose_name = _('sulphite'), + ) + + lupine = models.BooleanField( + default = False, + verbose_name = _('lupine'), + ) + + mollusc = models.BooleanField( + default = False, + verbose_name = _('mollusc'), + ) + + groundnut = models.BooleanField( + default = False, + verbose_name = _('groundnut'), + ) + + sesame = models.BooleanField( + default = False, + verbose_name = _('sesame'), + ) + + alcohol = models.BooleanField( + default = False, + verbose_name = _('alcohol'), + ) + + class Meta: + verbose_name = _('Allergen') + verbose_name_plural = _('Allergens') + + def __str__(self): + return _('Allergens of #{id}').format(id=self.id) + + +class Basic_food(models.Model): + """ + Food which has been directly buy on supermarket + """ + name = models.CharField( + verbose_name=_('name'), + max_length=255, + ) + + is_DLC = models.BooleanField( + verbose_name=_("is DLC"), + default=False, + ) + + is_DDM = models.BooleanField( + verbose_name=_("is DDM"), + default=False, + ) + + expiry_date = models.DateTimeField( + verbose_name=_('expiry date'), + default=timezone.now, + blank=True, + ) + + owner = models.ForeignKey( + Club, + on_delete=models.PROTECT, + related_name= '+', + verbose_name=_('owner'), + ) + + label = models.ImageField( + verbose_name=_('food label'), + max_length=255, + blank=False, + null=False, + upload_to='label/', + default= 'pic/default.png', + ) + + qr_code = models.ForeignKey( + QR_code, + on_delete=models.PROTECT, + related_name= '+', + verbose_name=_('QR code'), + ) + + was_eaten = models.BooleanField( + verbose_name=_('was eaten'), + default = False, + ) + + allergen = models.ForeignKey( + Allergen, + on_delete = models.PROTECT, + related_name = '+', + verbose_name = _('allergen'), + ) + + class Meta: + verbose_name=_('Basic food') + verbose_name_plural=_('Basic foods') + + def __str__(self): + return self.name + +class Transformed_food(models.Model): + """ + Transformed food are a mix between basic food and meal + """ + name = models.CharField( + max_length = 255, + verbose_name =_('name'), + ) + + creation_date = models.DateTimeField( + verbose_name =_('creation date'), + ) + + expiry_date = models.DateTimeField( + verbose_name =_('expiry date'), + ) + + owner = models.ForeignKey( + Club, + on_delete = models.PROTECT, + related_name = '+', + verbose_name =_('owner'), + ) + + transformed_ingredient = models.ForeignKey( + "self", + on_delete = models.CASCADE, + related_name = '+', + verbose_name = _('transformed ingredient'), + ) + + basic_ingredient = models.ForeignKey( + Basic_food, + on_delete = models.CASCADE, + related_name = '+', + verbose_name = _('basic ingredient'), + ) + + + is_active = models.BooleanField( + default = True, + verbose_name = _('is active'), + ) + + qr_code = models.ForeignKey( + QR_code, + on_delete = models.CASCADE, + related_name = '+', + verbose_name = _('QR code'), + ) + + was_eaten = models.BooleanField( + default = False, + verbose_name = _('was eaten'), + ) + + allergen = models.ForeignKey( + Allergen, + on_delete = models.PROTECT, + related_name= '+', + verbose_name = _('allergen'), + ) + + class Meta: + verbose_name = _('Transformed food') + verbose_name_plural = _('Transformed foods') + + def __str__(self): + return self.name + + diff --git a/apps/food/tests.py b/apps/food/tests.py new file mode 100644 index 00000000..7ce503c2 --- /dev/null +++ b/apps/food/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/apps/food/views.py b/apps/food/views.py new file mode 100644 index 00000000..91ea44a2 --- /dev/null +++ b/apps/food/views.py @@ -0,0 +1,3 @@ +from django.shortcuts import render + +# Create your views here. diff --git a/note_kfet/settings/base.py b/note_kfet/settings/base.py index 74fed818..93ae0afd 100644 --- a/note_kfet/settings/base.py +++ b/note_kfet/settings/base.py @@ -77,6 +77,7 @@ INSTALLED_APPS = [ 'scripts', 'treasury', 'wei', + 'food', ] MIDDLEWARE = [ From 4de2e987ef103e9fe8528bdcaf5ead07088b2c68 Mon Sep 17 00:00:00 2001 From: quark Date: Fri, 17 May 2024 21:33:30 +0200 Subject: [PATCH 03/29] Rajout de la pseudo-doc --- README.md | 8 -------- 1 file changed, 8 deletions(-) diff --git a/README.md b/README.md index b825aa75..98fe3713 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,3 @@ -# Traçabilité de la bouffe - doc provisoire ici : https://pad.crans.org/p/tracabilite - et ici : https://pad.crans.org/p/noteBouffe - - - - - # NoteKfet 2020 [![License: GPL v3](https://img.shields.io/badge/License-GPL%20v3-blue.svg)](https://www.gnu.org/licenses/gpl-3.0.txt) From a481adbae4fae09622a125c244608f3a5df51344 Mon Sep 17 00:00:00 2001 From: quark Date: Tue, 21 May 2024 11:21:13 +0200 Subject: [PATCH 04/29] =?UTF-8?q?cr=C3=A9ation=20de=20l'interface=20admin?= =?UTF-8?q?=20temporaire?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/food/admin.py | 28 ++++++++++++++++++- .../migrations/0002_auto_20240521_1103.py | 19 +++++++++++++ .../migrations/0003_auto_20240521_1110.py | 19 +++++++++++++ apps/food/models.py | 2 ++ apps/food/urls.py | 7 +++++ apps/food/views.py | 4 ++- note_kfet/urls.py | 1 + 7 files changed, 78 insertions(+), 2 deletions(-) create mode 100644 apps/food/migrations/0002_auto_20240521_1103.py create mode 100644 apps/food/migrations/0003_auto_20240521_1110.py create mode 100644 apps/food/urls.py diff --git a/apps/food/admin.py b/apps/food/admin.py index 8c38f3f3..820036c3 100644 --- a/apps/food/admin.py +++ b/apps/food/admin.py @@ -1,3 +1,29 @@ from django.contrib import admin +from note_kfet.admin import admin_site -# Register your models here. +from .models import QR_code, Basic_food, Transformed_food, Allergen + + +@admin.register(QR_code, site = admin_site) +class QR_codeAdmin(admin.ModelAdmin): + """ + TEMPORARY + """ + +@admin.register(Basic_food, site = admin_site) +class Basic_foodAdmin(admin.ModelAdmin): + """ + TEMPORARY + """ + +@admin.register(Transformed_food, site = admin_site) +class Transformed_foodAdmin(admin.ModelAdmin): + """ + TEMPORARY + """ + +@admin.register(Allergen, site = admin_site) +class AllergenAdmin(admin.ModelAdmin): + """ + TEMPORARY + """ diff --git a/apps/food/migrations/0002_auto_20240521_1103.py b/apps/food/migrations/0002_auto_20240521_1103.py new file mode 100644 index 00000000..6bdb8677 --- /dev/null +++ b/apps/food/migrations/0002_auto_20240521_1103.py @@ -0,0 +1,19 @@ +# Generated by Django 2.2.28 on 2024-05-21 09:03 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('food', '0001_initial'), + ] + + operations = [ + migrations.AlterField( + model_name='transformed_food', + name='transformed_ingredient', + field=models.ForeignKey(blank=True, on_delete=django.db.models.deletion.CASCADE, related_name='+', to='food.Transformed_food', verbose_name='transformed ingredient'), + ), + ] diff --git a/apps/food/migrations/0003_auto_20240521_1110.py b/apps/food/migrations/0003_auto_20240521_1110.py new file mode 100644 index 00000000..9f92f534 --- /dev/null +++ b/apps/food/migrations/0003_auto_20240521_1110.py @@ -0,0 +1,19 @@ +# Generated by Django 2.2.28 on 2024-05-21 09:10 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('food', '0002_auto_20240521_1103'), + ] + + operations = [ + migrations.AlterField( + model_name='transformed_food', + name='transformed_ingredient', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='+', to='food.Transformed_food', verbose_name='transformed ingredient'), + ), + ] diff --git a/apps/food/models.py b/apps/food/models.py index acaa43f8..5347abc5 100644 --- a/apps/food/models.py +++ b/apps/food/models.py @@ -211,6 +211,8 @@ class Transformed_food(models.Model): transformed_ingredient = models.ForeignKey( "self", on_delete = models.CASCADE, + blank = True, + null = True, related_name = '+', verbose_name = _('transformed ingredient'), ) diff --git a/apps/food/urls.py b/apps/food/urls.py new file mode 100644 index 00000000..2fcca0db --- /dev/null +++ b/apps/food/urls.py @@ -0,0 +1,7 @@ +from django.urls import path + +from . import views + +urlpatterns = [ + path('', views.index, name='index') +] diff --git a/apps/food/views.py b/apps/food/views.py index 91ea44a2..c4cfa086 100644 --- a/apps/food/views.py +++ b/apps/food/views.py @@ -1,3 +1,5 @@ from django.shortcuts import render +from django.http import HttpResponse -# Create your views here. +def index(request): + return HttpResponse('test') diff --git a/note_kfet/urls.py b/note_kfet/urls.py index d222c239..01bec0ba 100644 --- a/note_kfet/urls.py +++ b/note_kfet/urls.py @@ -21,6 +21,7 @@ urlpatterns = [ path('activity/', include('activity.urls')), path('treasury/', include('treasury.urls')), path('wei/', include('wei.urls')), + path('food/',include('food.urls')), # Include Django Contrib and Core routers path('i18n/', include('django.conf.urls.i18n')), From 968fa64d37dba598abb4331b0036bce84e6a01fb Mon Sep 17 00:00:00 2001 From: quark Date: Tue, 21 May 2024 14:07:35 +0200 Subject: [PATCH 05/29] =?UTF-8?q?R=C3=A9agencement=20des=20tables=20et=20d?= =?UTF-8?q?e=20leurs=20attributs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/food/migrations/0001_initial.py | 110 +++++++++--------- .../migrations/0002_auto_20240521_1103.py | 19 --- .../migrations/0003_auto_20240521_1110.py | 19 --- apps/food/models.py | 84 +++++++------ 4 files changed, 101 insertions(+), 131 deletions(-) delete mode 100644 apps/food/migrations/0002_auto_20240521_1103.py delete mode 100644 apps/food/migrations/0003_auto_20240521_1110.py diff --git a/apps/food/migrations/0001_initial.py b/apps/food/migrations/0001_initial.py index 46899dd6..49b7a329 100644 --- a/apps/food/migrations/0001_initial.py +++ b/apps/food/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 2.2.28 on 2024-05-17 18:44 +# Generated by Django 2.2.28 on 2024-05-21 12:05 from django.db import migrations, models import django.db.models.deletion @@ -14,6 +14,58 @@ class Migration(migrations.Migration): ] operations = [ + migrations.CreateModel( + name='Basic_food', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=255, verbose_name='name')), + ('is_DLC', models.BooleanField(default=False, verbose_name='is DLC')), + ('is_DDM', models.BooleanField(default=False, verbose_name='is DDM')), + ('expiry_date', models.DateTimeField(blank=True, default=django.utils.timezone.now, verbose_name='expiry date')), + ('label', models.ImageField(default='pic/default.png', max_length=255, upload_to='label/', verbose_name='food label')), + ('was_eaten', models.BooleanField(default=False, verbose_name='was eaten')), + ('owner', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='+', to='member.Club', verbose_name='owner')), + ], + options={ + 'verbose_name': 'Basic food', + 'verbose_name_plural': 'Basic foods', + }, + ), + migrations.CreateModel( + name='Transformed_food', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=255, verbose_name='name')), + ('creation_date', models.DateTimeField(verbose_name='creation date')), + ('expiry_date', models.DateTimeField(verbose_name='expiry date')), + ('is_active', models.BooleanField(default=True, verbose_name='is active')), + ('was_eaten', models.BooleanField(default=False, verbose_name='was eaten')), + ('owner', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='+', to='member.Club', verbose_name='owner')), + ('transformed_ingredient', models.ManyToManyField(blank=True, related_name='transformed_ingredient_inv', to='food.Transformed_food', verbose_name='transformed ingredient')), + ], + options={ + 'verbose_name': 'Transformed food', + 'verbose_name_plural': 'Transformed foods', + }, + ), + migrations.CreateModel( + name='QR_code', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('qr_code_number', models.PositiveIntegerField(verbose_name='QR-code number')), + ('basic_food', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='QR_code', to='food.Basic_food', verbose_name='basic food')), + ('transformed_food_container', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='QR_code', to='food.Transformed_food', verbose_name='transformed food container')), + ], + options={ + 'verbose_name': 'QR-code', + 'verbose_name_plural': 'QR-codes', + }, + ), + migrations.AddField( + model_name='basic_food', + name='transformed_food', + field=models.ManyToManyField(blank=True, related_name='Basic_food', to='food.Transformed_food', verbose_name='transformed food'), + ), migrations.CreateModel( name='Allergen', fields=[ @@ -33,64 +85,12 @@ class Migration(migrations.Migration): ('groundnut', models.BooleanField(default=False, verbose_name='groundnut')), ('sesame', models.BooleanField(default=False, verbose_name='sesame')), ('alcohol', models.BooleanField(default=False, verbose_name='alcohol')), + ('basic_food', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='Allergen', to='food.Basic_food', verbose_name='basic food')), + ('transformed_food', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='Allergen', to='food.Transformed_food', verbose_name='transformed food')), ], options={ 'verbose_name': 'Allergen', 'verbose_name_plural': 'Allergens', }, ), - migrations.CreateModel( - name='Basic_food', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=255, verbose_name='name')), - ('is_DLC', models.BooleanField(default=False, verbose_name='is DLC')), - ('is_DDM', models.BooleanField(default=False, verbose_name='is DDM')), - ('expiry_date', models.DateTimeField(blank=True, default=django.utils.timezone.now, verbose_name='expiry date')), - ('label', models.ImageField(default='pic/default.png', max_length=255, upload_to='label/', verbose_name='food label')), - ('was_eaten', models.BooleanField(default=False, verbose_name='was eaten')), - ('allergen', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='+', to='food.Allergen', verbose_name='allergen')), - ('owner', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='+', to='member.Club', verbose_name='owner')), - ], - options={ - 'verbose_name': 'Basic food', - 'verbose_name_plural': 'Basic foods', - }, - ), - migrations.CreateModel( - name='QR_code', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('qr_code_number', models.PositiveIntegerField(verbose_name='QR-code number')), - ], - options={ - 'verbose_name': 'QR-code', - 'verbose_name_plural': 'QR-codes', - }, - ), - migrations.CreateModel( - name='Transformed_food', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=255, verbose_name='name')), - ('creation_date', models.DateTimeField(verbose_name='creation date')), - ('expiry_date', models.DateTimeField(verbose_name='expiry date')), - ('is_active', models.BooleanField(default=True, verbose_name='is active')), - ('was_eaten', models.BooleanField(default=False, verbose_name='was eaten')), - ('allergen', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='+', to='food.Allergen', verbose_name='allergen')), - ('basic_ingredient', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='+', to='food.Basic_food', verbose_name='basic ingredient')), - ('owner', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='+', to='member.Club', verbose_name='owner')), - ('qr_code', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='+', to='food.QR_code', verbose_name='QR code')), - ('transformed_ingredient', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='+', to='food.Transformed_food', verbose_name='transformed ingredient')), - ], - options={ - 'verbose_name': 'Transformed food', - 'verbose_name_plural': 'Transformed foods', - }, - ), - migrations.AddField( - model_name='basic_food', - name='qr_code', - field=models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='+', to='food.QR_code', verbose_name='QR code'), - ), ] diff --git a/apps/food/migrations/0002_auto_20240521_1103.py b/apps/food/migrations/0002_auto_20240521_1103.py deleted file mode 100644 index 6bdb8677..00000000 --- a/apps/food/migrations/0002_auto_20240521_1103.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 2.2.28 on 2024-05-21 09:03 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('food', '0001_initial'), - ] - - operations = [ - migrations.AlterField( - model_name='transformed_food', - name='transformed_ingredient', - field=models.ForeignKey(blank=True, on_delete=django.db.models.deletion.CASCADE, related_name='+', to='food.Transformed_food', verbose_name='transformed ingredient'), - ), - ] diff --git a/apps/food/migrations/0003_auto_20240521_1110.py b/apps/food/migrations/0003_auto_20240521_1110.py deleted file mode 100644 index 9f92f534..00000000 --- a/apps/food/migrations/0003_auto_20240521_1110.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 2.2.28 on 2024-05-21 09:10 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('food', '0002_auto_20240521_1103'), - ] - - operations = [ - migrations.AlterField( - model_name='transformed_food', - name='transformed_ingredient', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='+', to='food.Transformed_food', verbose_name='transformed ingredient'), - ), - ] diff --git a/apps/food/models.py b/apps/food/models.py index 5347abc5..7deeb902 100644 --- a/apps/food/models.py +++ b/apps/food/models.py @@ -19,7 +19,25 @@ class QR_code(models.Model): An QR_code model """ qr_code_number = models.PositiveIntegerField( - verbose_name=_("QR-code number"), + verbose_name=_("QR-code number"), + ) + + transformed_food_container = models.ForeignKey( + 'Transformed_food', + on_delete = models.PROTECT, + related_name = 'QR_code', + null = True, + blank = True, + verbose_name = _('transformed food container'), + ) + + basic_food = models.ForeignKey( + 'Basic_food', + on_delete = models.PROTECT, + related_name = 'QR_code', + null = True, + blank = True, + verbose_name = _('basic food'), ) class Meta: @@ -109,6 +127,24 @@ class Allergen(models.Model): verbose_name = _('alcohol'), ) + transformed_food = models.ForeignKey( + 'Transformed_food', + on_delete = models.CASCADE, + related_name = 'Allergen', + blank = True, + null = True, + verbose_name = _('transformed food'), + ) + + basic_food = models.ForeignKey( + 'Basic_food', + on_delete = models.CASCADE, + related_name = 'Allergen', + blank = True, + null = True, + verbose_name = _('basic food'), + ) + class Meta: verbose_name = _('Allergen') verbose_name_plural = _('Allergens') @@ -158,25 +194,19 @@ class Basic_food(models.Model): default= 'pic/default.png', ) - qr_code = models.ForeignKey( - QR_code, - on_delete=models.PROTECT, - related_name= '+', - verbose_name=_('QR code'), - ) - was_eaten = models.BooleanField( verbose_name=_('was eaten'), default = False, ) - allergen = models.ForeignKey( - Allergen, - on_delete = models.PROTECT, - related_name = '+', - verbose_name = _('allergen'), + transformed_food = models.ManyToManyField( + 'Transformed_food', + related_name= 'Basic_food', + blank = True, + verbose_name = _('transformed food'), ) + class Meta: verbose_name=_('Basic food') verbose_name_plural=_('Basic foods') @@ -208,46 +238,24 @@ class Transformed_food(models.Model): verbose_name =_('owner'), ) - transformed_ingredient = models.ForeignKey( + transformed_ingredient = models.ManyToManyField( "self", - on_delete = models.CASCADE, blank = True, - null = True, - related_name = '+', + symmetrical = False, + related_name = 'transformed_ingredient_inv', verbose_name = _('transformed ingredient'), ) - basic_ingredient = models.ForeignKey( - Basic_food, - on_delete = models.CASCADE, - related_name = '+', - verbose_name = _('basic ingredient'), - ) - - is_active = models.BooleanField( default = True, verbose_name = _('is active'), ) - qr_code = models.ForeignKey( - QR_code, - on_delete = models.CASCADE, - related_name = '+', - verbose_name = _('QR code'), - ) - was_eaten = models.BooleanField( default = False, verbose_name = _('was eaten'), ) - allergen = models.ForeignKey( - Allergen, - on_delete = models.PROTECT, - related_name= '+', - verbose_name = _('allergen'), - ) class Meta: verbose_name = _('Transformed food') From adacc293f50bca3b4a76beecff0fb39936be2a85 Mon Sep 17 00:00:00 2001 From: quark Date: Thu, 23 May 2024 23:53:33 +0200 Subject: [PATCH 06/29] First forms --- apps/food/forms.py | 92 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 apps/food/forms.py diff --git a/apps/food/forms.py b/apps/food/forms.py new file mode 100644 index 00000000..e072c00a --- /dev/null +++ b/apps/food/forms.py @@ -0,0 +1,92 @@ +# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# SPDX-License-Identifier: GPL-3.0-or-later + +from random import shuffle + +from django import forms +from django.utils.translation import gettext_lazy as _ +from django.utils import timezone +from member.models import Club +from note_kfet.inputs import Autocomplete, DateTimePickerInput +from note_kfet.middlewares import get_current_request +from permission.backends import PermissionBackend + +from .models import QR_code, Allergen, Basic_food, Transformed_food + + +class Basic_foodForms(forms.ModelForm): + """ + Form for add non-transformed food + """ + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.fields['name'].widget.attrs.update({"autofocus": "autofocus"}) + self.fields['name'].required = True + self.fields['owner'].required = True + self.fields['label'].help_text = _('The lot number must be contained in the picture') + + # Some example + self.fields['name'].widget.attrs.update({"placeholder": _("pasta")}) + clubs = list(Club.objects.filter(PermissionBackend.filter_queryset(get_current_request(), Club, "change")).all()) + shuffle(clubs) + self.fields['owner'].widget.attrs["placeholder"] = ", ".join(club.name for club in clubs[:4]) + ", ..." + def clean_dlm_or_dlc(self): + is_dlc = self.cleaned_data["is_DLC"] + is_ddm = self.cleaned_data["is_DDM"] + if is_dlc and is_ddm: + self.add_error("is_ddm", _("the product cannot be a DLC and a DDM")) + return is_ddm + + class Meta: + model = Basic_food + fields = ('name', 'owner', 'is_DLC', 'is_DDM', 'expiry_date', 'label') + widget = { + "owner": Autocomplete( + model = Club, + attrs = {"api_url": "/api/members/club/"}, + ), + 'expiry_date': DateTimePickerInput(), + } + + + +class Transformed_foodForms(forms.ModelForm): + """ + Form for add transformed food + """ + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.fields['name'].widget.attrs.update({"autofocus": "autofocus"}) + self.fields['name'].required = True + self.fields['owner'].required = True + self.fields['creation_date'].required = True + self.fields['creation_date'].initial = timezone.now + self.fields['is_active'].initial = True + + # Some example + self.fields['name'].widget.attrs.update({"placeholder": _("lasagna")}) + clubs = list(Club.objects.filter(PermissionBackend.filter_queryset(get_current_request(), Club, "change")).all()) + shuffle(clubs) + self.fields['owner'].widget.attrs["placeholder"] = ", ".join(club.name for club in clubs[:4]) + ", ..." + + + class Meta: + model = Transformed_food + fields = ('name', 'creation_date', 'owner', 'is_active',) + widget = { + "owner": Autocomplete( + model = Club, + attrs = {"api_url": "/api/members/club/"}, + ), + 'creation_date': DateTimePickerInput(), + } +class AllergenForms(forms.ModelForm): + """ + Form for allergen + """ + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + + class Meta: + model = Allergen + exclude = ['basic_food', 'transformed_food'] From 4c390dce17c12c08451f609ee48f17b3ec77cdac Mon Sep 17 00:00:00 2001 From: quark Date: Fri, 24 May 2024 21:47:30 +0200 Subject: [PATCH 07/29] nom app --- apps/food/apps.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/apps/food/apps.py b/apps/food/apps.py index 092961b7..a423e253 100644 --- a/apps/food/apps.py +++ b/apps/food/apps.py @@ -1,5 +1,10 @@ +# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# SPDX-License-Identifier: GPL-3.0-or-later + + from django.apps import AppConfig class FoodkfetConfig(AppConfig): - name = 'foodkfet' + name = 'food' + verbose_name = _('food') From 64bd5ed546b5eef84409f97b0d34d0a4b15f7c03 Mon Sep 17 00:00:00 2001 From: quark Date: Fri, 24 May 2024 21:49:23 +0200 Subject: [PATCH 08/29] =?UTF-8?q?cr=C3=A9ation=20d'un=20form=20pour=20l'aj?= =?UTF-8?q?out=20d'aliments=20basiques?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/food/forms.py | 8 +-- .../migrations/0002_auto_20240524_2139.py | 19 ++++++ apps/food/models.py | 1 + apps/food/templates/food/basic_food_form.html | 20 ++++++ apps/food/urls.py | 11 +++- apps/food/views.py | 64 ++++++++++++++++++- 6 files changed, 115 insertions(+), 8 deletions(-) create mode 100644 apps/food/migrations/0002_auto_20240524_2139.py create mode 100644 apps/food/templates/food/basic_food_form.html diff --git a/apps/food/forms.py b/apps/food/forms.py index e072c00a..92887c32 100644 --- a/apps/food/forms.py +++ b/apps/food/forms.py @@ -7,7 +7,7 @@ from django import forms from django.utils.translation import gettext_lazy as _ from django.utils import timezone from member.models import Club -from note_kfet.inputs import Autocomplete, DateTimePickerInput +from note_kfet.inputs import Autocomplete, DatePickerInput from note_kfet.middlewares import get_current_request from permission.backends import PermissionBackend @@ -40,12 +40,12 @@ class Basic_foodForms(forms.ModelForm): class Meta: model = Basic_food fields = ('name', 'owner', 'is_DLC', 'is_DDM', 'expiry_date', 'label') - widget = { + widgets = { "owner": Autocomplete( model = Club, attrs = {"api_url": "/api/members/club/"}, ), - 'expiry_date': DateTimePickerInput(), + 'expiry_date': DatePickerInput(), } @@ -78,7 +78,7 @@ class Transformed_foodForms(forms.ModelForm): model = Club, attrs = {"api_url": "/api/members/club/"}, ), - 'creation_date': DateTimePickerInput(), + 'creation_date': DatePickerInput(), } class AllergenForms(forms.ModelForm): """ diff --git a/apps/food/migrations/0002_auto_20240524_2139.py b/apps/food/migrations/0002_auto_20240524_2139.py new file mode 100644 index 00000000..d4423976 --- /dev/null +++ b/apps/food/migrations/0002_auto_20240524_2139.py @@ -0,0 +1,19 @@ +# Generated by Django 2.2.28 on 2024-05-24 19:39 + +from django.db import migrations, models +import django.utils.timezone + + +class Migration(migrations.Migration): + + dependencies = [ + ('food', '0001_initial'), + ] + + operations = [ + migrations.AlterField( + model_name='basic_food', + name='expiry_date', + field=models.DateTimeField(blank=True, default=django.utils.timezone.now, null=True, verbose_name='expiry date'), + ), + ] diff --git a/apps/food/models.py b/apps/food/models.py index 7deeb902..e957ae99 100644 --- a/apps/food/models.py +++ b/apps/food/models.py @@ -176,6 +176,7 @@ class Basic_food(models.Model): verbose_name=_('expiry date'), default=timezone.now, blank=True, + null = True, ) owner = models.ForeignKey( diff --git a/apps/food/templates/food/basic_food_form.html b/apps/food/templates/food/basic_food_form.html new file mode 100644 index 00000000..a56aa063 --- /dev/null +++ b/apps/food/templates/food/basic_food_form.html @@ -0,0 +1,20 @@ +{% extends "base.html" %} +{% comment %} +SPDX-License-Identifier: GPL-3.0-or-later +{% endcomment %} +{% load i18n crispy_forms_tags %} + +{% block content %} +
+

+ {{ title }} +

+
+
+ {% csrf_token %} + {{ form|crispy }} + +
+
+
+{% endblock %} diff --git a/apps/food/urls.py b/apps/food/urls.py index 2fcca0db..aff8b8b5 100644 --- a/apps/food/urls.py +++ b/apps/food/urls.py @@ -1,7 +1,16 @@ +# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# SPDX-License-Identifier: GPL-3.0-or-later + from django.urls import path from . import views +app_name = 'food' + urlpatterns = [ - path('', views.index, name='index') + path('test_basic_food_form',views.test_basic_foodform), + path('test_transformed_food_form', views.test_transformed_foodform), + path('test_allergen_form', views.test_allergenform), + path('0', views.Basic_foodCreateView.as_view(), name = 'basic_food'), ] + diff --git a/apps/food/views.py b/apps/food/views.py index c4cfa086..43161b0d 100644 --- a/apps/food/views.py +++ b/apps/food/views.py @@ -1,5 +1,63 @@ -from django.shortcuts import render +# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# SPDX-License-Identifier: GPL-3.0-or-later +''' +from crispy_forms.helper import FormHelper +from django.contrib.auth.mixins import LoginRequiredMixin +from django.core.exceptions import ValidationError, PermissionDenied +from django.db.models import Q +from django.forms import Form from django.http import HttpResponse +from django.shortcuts import redirect +''' +from django.urls import reverse_lazy +from django.db import transaction +from django.utils.translation import gettext_lazy as _ +from django.utils import timezone +''' +from django.views.generic import UpdateView, DetailView +from django.views.generic.base import View, TemplateView +from django.views.generic.edit import BaseFormView, DeleteView +from django_tables2 import SingleTableView +''' +from permission.views import ProtectQuerysetMixin, ProtectedCreateView -def index(request): - return HttpResponse('test') +from django.shortcuts import render + +from .forms import Basic_foodForms, Transformed_foodForms, AllergenForms +from .models import Basic_food + +def test_basic_foodform(request): + return render(request,'food/test.html', {"form": Basic_foodForms()}) + +def test_transformed_foodform(request): + return render(request,'food/test.html', {"form": Transformed_foodForms()}) + +def test_allergenform(request): + return render(request,'food/test.html', {"form": AllergenForms()}) + + +class Basic_foodCreateView(ProtectQuerysetMixin, ProtectedCreateView): + """ + A view to add a basic food + """ + model = Basic_food + form_class = Basic_foodForms + extra_context = {"title": _("Add a new aliment")} + + def get_sample_object(self): + return Basic_food( + name="", + is_DLC=False, + is_DDM=False, + expiry_date=timezone.now(), + label='pic/default.png', + ) + + @transaction.atomic + def form_valid(self, form): + form.instance.creater = self.request.user + return super().form_valid(form) + + def get_success_url(self, **kwargs): + self.objects.refresh_from_db() + return reverse_lazy('food:basic_food', kwargs={"pk": self.object.pk}) From 0801ad64aea0a6db2a046469d954c59914b857db Mon Sep 17 00:00:00 2001 From: quark Date: Sat, 25 May 2024 15:27:26 +0200 Subject: [PATCH 09/29] =?UTF-8?q?cr=C3=A9ation=20de=20forms=20fonctionnel?= =?UTF-8?q?=20(form=20+=20views=20+=20url=20+=20html),=20few=20changes=20i?= =?UTF-8?q?n=20models.py?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/food/admin.py | 6 +- apps/food/forms.py | 16 ++-- .../migrations/0003_auto_20240525_1350.py | 55 ++++++++++++++ .../migrations/0004_auto_20240525_1352.py | 18 +++++ apps/food/models.py | 34 ++++++--- apps/food/templates/food/basic_food_form.html | 1 + .../templates/food/transformed_food_form.html | 20 +++++ apps/food/urls.py | 3 +- apps/food/views.py | 76 ++++++++++++++++--- 9 files changed, 196 insertions(+), 33 deletions(-) create mode 100644 apps/food/migrations/0003_auto_20240525_1350.py create mode 100644 apps/food/migrations/0004_auto_20240525_1352.py create mode 100644 apps/food/templates/food/transformed_food_form.html diff --git a/apps/food/admin.py b/apps/food/admin.py index 820036c3..7579aff5 100644 --- a/apps/food/admin.py +++ b/apps/food/admin.py @@ -1,7 +1,7 @@ from django.contrib import admin from note_kfet.admin import admin_site -from .models import QR_code, Basic_food, Transformed_food, Allergen +from .models import QR_code, BasicFood, TransformedFood, Allergen @admin.register(QR_code, site = admin_site) @@ -10,13 +10,13 @@ class QR_codeAdmin(admin.ModelAdmin): TEMPORARY """ -@admin.register(Basic_food, site = admin_site) +@admin.register(BasicFood, site = admin_site) class Basic_foodAdmin(admin.ModelAdmin): """ TEMPORARY """ -@admin.register(Transformed_food, site = admin_site) +@admin.register(TransformedFood, site = admin_site) class Transformed_foodAdmin(admin.ModelAdmin): """ TEMPORARY diff --git a/apps/food/forms.py b/apps/food/forms.py index 92887c32..5855c936 100644 --- a/apps/food/forms.py +++ b/apps/food/forms.py @@ -7,14 +7,14 @@ from django import forms from django.utils.translation import gettext_lazy as _ from django.utils import timezone from member.models import Club -from note_kfet.inputs import Autocomplete, DatePickerInput +from note_kfet.inputs import Autocomplete, DatePickerInput, DateTimePickerInput from note_kfet.middlewares import get_current_request from permission.backends import PermissionBackend -from .models import QR_code, Allergen, Basic_food, Transformed_food +from .models import QR_code, Allergen, BasicFood, TransformedFood -class Basic_foodForms(forms.ModelForm): +class BasicFoodForms(forms.ModelForm): """ Form for add non-transformed food """ @@ -38,7 +38,7 @@ class Basic_foodForms(forms.ModelForm): return is_ddm class Meta: - model = Basic_food + model = BasicFood fields = ('name', 'owner', 'is_DLC', 'is_DDM', 'expiry_date', 'label') widgets = { "owner": Autocomplete( @@ -50,7 +50,7 @@ class Basic_foodForms(forms.ModelForm): -class Transformed_foodForms(forms.ModelForm): +class TransformedFoodForms(forms.ModelForm): """ Form for add transformed food """ @@ -71,14 +71,14 @@ class Transformed_foodForms(forms.ModelForm): class Meta: - model = Transformed_food + model = TransformedFood fields = ('name', 'creation_date', 'owner', 'is_active',) - widget = { + widgets = { "owner": Autocomplete( model = Club, attrs = {"api_url": "/api/members/club/"}, ), - 'creation_date': DatePickerInput(), + 'creation_date': DateTimePickerInput(), } class AllergenForms(forms.ModelForm): """ diff --git a/apps/food/migrations/0003_auto_20240525_1350.py b/apps/food/migrations/0003_auto_20240525_1350.py new file mode 100644 index 00000000..23321d64 --- /dev/null +++ b/apps/food/migrations/0003_auto_20240525_1350.py @@ -0,0 +1,55 @@ +# Generated by Django 2.2.28 on 2024-05-25 11:50 + +from django.db import migrations, models +import django.db.models.deletion +import django.utils.timezone + + +class Migration(migrations.Migration): + + dependencies = [ + ('member', '0011_profile_vss_charter_read'), + ('food', '0002_auto_20240524_2139'), + ] + + operations = [ + migrations.CreateModel( + name='BasicFood', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=255, verbose_name='name')), + ('is_DLC', models.BooleanField(default=False, verbose_name='is DLC')), + ('is_DDM', models.BooleanField(default=False, verbose_name='is DDM')), + ('expiry_date', models.DateTimeField(blank=True, default=django.utils.timezone.now, null=True, verbose_name='expiry date')), + ('label', models.ImageField(default='pic/default.png', max_length=255, upload_to='label/', verbose_name='food label')), + ('was_eaten', models.BooleanField(default=False, verbose_name='was eaten')), + ('owner', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='+', to='member.Club', verbose_name='owner')), + ], + options={ + 'verbose_name': 'Basic food', + 'verbose_name_plural': 'Basic foods', + }, + ), + migrations.AlterField( + model_name='allergen', + name='basic_food', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='Allergen', to='food.BasicFood', verbose_name='basic food'), + ), + migrations.AlterField( + model_name='qr_code', + name='basic_food', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='QR_code', to='food.BasicFood', verbose_name='basic food'), + ), + migrations.RenameModel( + old_name='Transformed_food', + new_name='TransformedFood', + ), + migrations.DeleteModel( + name='Basic_food', + ), + migrations.AddField( + model_name='basicfood', + name='transformed_food', + field=models.ManyToManyField(blank=True, null=True, related_name='BasicFood', to='food.TransformedFood', verbose_name='transformed food'), + ), + ] diff --git a/apps/food/migrations/0004_auto_20240525_1352.py b/apps/food/migrations/0004_auto_20240525_1352.py new file mode 100644 index 00000000..5c7932c4 --- /dev/null +++ b/apps/food/migrations/0004_auto_20240525_1352.py @@ -0,0 +1,18 @@ +# Generated by Django 2.2.28 on 2024-05-25 11:52 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('food', '0003_auto_20240525_1350'), + ] + + operations = [ + migrations.AlterField( + model_name='basicfood', + name='transformed_food', + field=models.ManyToManyField(blank=True, related_name='BasicFood', to='food.TransformedFood', verbose_name='transformed food'), + ), + ] diff --git a/apps/food/models.py b/apps/food/models.py index e957ae99..b0a222f4 100644 --- a/apps/food/models.py +++ b/apps/food/models.py @@ -7,7 +7,7 @@ from django.conf import settings from django.contrib.auth.models import User from django.core.exceptions import ValidationError from django.core.validators import MinValueValidator -from django.db import models +from django.db import models, transaction from django.db.models import Q from django.utils import timezone from django.utils.translation import gettext_lazy as _ @@ -23,7 +23,7 @@ class QR_code(models.Model): ) transformed_food_container = models.ForeignKey( - 'Transformed_food', + 'TransformedFood', on_delete = models.PROTECT, related_name = 'QR_code', null = True, @@ -32,7 +32,7 @@ class QR_code(models.Model): ) basic_food = models.ForeignKey( - 'Basic_food', + 'BasicFood', on_delete = models.PROTECT, related_name = 'QR_code', null = True, @@ -128,7 +128,7 @@ class Allergen(models.Model): ) transformed_food = models.ForeignKey( - 'Transformed_food', + 'TransformedFood', on_delete = models.CASCADE, related_name = 'Allergen', blank = True, @@ -137,7 +137,7 @@ class Allergen(models.Model): ) basic_food = models.ForeignKey( - 'Basic_food', + 'BasicFood', on_delete = models.CASCADE, related_name = 'Allergen', blank = True, @@ -150,10 +150,12 @@ class Allergen(models.Model): verbose_name_plural = _('Allergens') def __str__(self): - return _('Allergens of #{id}').format(id=self.id) + return _('Allergens of #{id}').format(id=self.id) + + -class Basic_food(models.Model): +class BasicFood(models.Model): """ Food which has been directly buy on supermarket """ @@ -201,8 +203,8 @@ class Basic_food(models.Model): ) transformed_food = models.ManyToManyField( - 'Transformed_food', - related_name= 'Basic_food', + 'TransformedFood', + related_name= 'BasicFood', blank = True, verbose_name = _('transformed food'), ) @@ -215,7 +217,15 @@ class Basic_food(models.Model): def __str__(self): return self.name -class Transformed_food(models.Model): + @transaction.atomic + def save(self, force_insert=False, force_update=False, using= None, update_fields=None): + # Check if is_DLC and is DDM are not both True + if self.is_DLC and self.is_DDM: + raise ValidationError("The product cannot be a DLC and a DDM") + return super().save(force_insert, force_update, using, update_fields) + + +class TransformedFood(models.Model): """ Transformed food are a mix between basic food and meal """ @@ -265,4 +275,6 @@ class Transformed_food(models.Model): 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) diff --git a/apps/food/templates/food/basic_food_form.html b/apps/food/templates/food/basic_food_form.html index a56aa063..abce7e9b 100644 --- a/apps/food/templates/food/basic_food_form.html +++ b/apps/food/templates/food/basic_food_form.html @@ -13,6 +13,7 @@ SPDX-License-Identifier: GPL-3.0-or-later
{% csrf_token %} {{ form|crispy }} + {{ allergenform|crispy }}
diff --git a/apps/food/templates/food/transformed_food_form.html b/apps/food/templates/food/transformed_food_form.html new file mode 100644 index 00000000..917654db --- /dev/null +++ b/apps/food/templates/food/transformed_food_form.html @@ -0,0 +1,20 @@ +{% extends "base.html" %} +{% comment %} +SPDX-License-Identifier: GPL-3.0-or-later +{% endcomment %} +{% load i18n crispy_forms_tags %} + +{% block content %} +
+

+ {{ title }} +

+
+
+ {% csrf_token %} + {{ form|crispy }} + +
+
+
+{% endblock %} diff --git a/apps/food/urls.py b/apps/food/urls.py index aff8b8b5..b81bc8fa 100644 --- a/apps/food/urls.py +++ b/apps/food/urls.py @@ -11,6 +11,7 @@ urlpatterns = [ path('test_basic_food_form',views.test_basic_foodform), path('test_transformed_food_form', views.test_transformed_foodform), path('test_allergen_form', views.test_allergenform), - path('0', views.Basic_foodCreateView.as_view(), name = 'basic_food'), + path('0', views.BasicFoodCreateView.as_view(), name = 'basic_food'), + path('1', views.TransformedFoodCreateView.as_view(), name = 'transformed_food'), ] diff --git a/apps/food/views.py b/apps/food/views.py index 43161b0d..1768fd38 100644 --- a/apps/food/views.py +++ b/apps/food/views.py @@ -13,6 +13,7 @@ from django.urls import reverse_lazy from django.db import transaction from django.utils.translation import gettext_lazy as _ from django.utils import timezone +from datetime import timedelta ''' from django.views.generic import UpdateView, DetailView from django.views.generic.base import View, TemplateView @@ -23,41 +24,96 @@ from permission.views import ProtectQuerysetMixin, ProtectedCreateView from django.shortcuts import render -from .forms import Basic_foodForms, Transformed_foodForms, AllergenForms -from .models import Basic_food +from .forms import BasicFoodForms, TransformedFoodForms, AllergenForms +from .models import BasicFood, TransformedFood, Allergen def test_basic_foodform(request): - return render(request,'food/test.html', {"form": Basic_foodForms()}) + return render(request,'food/test.html', {"form": BasicFoodForms()}) def test_transformed_foodform(request): - return render(request,'food/test.html', {"form": Transformed_foodForms()}) + return render(request,'food/transformed_food_form.html', {"form": TransformedFoodForms()}) def test_allergenform(request): return render(request,'food/test.html', {"form": AllergenForms()}) -class Basic_foodCreateView(ProtectQuerysetMixin, ProtectedCreateView): +class BasicFoodCreateView(ProtectQuerysetMixin, ProtectedCreateView): """ A view to add a basic food """ - model = Basic_food - form_class = Basic_foodForms + model = BasicFood + form_class = BasicFoodForms + template_name = 'food/basic_food_form.html' + second_form = AllergenForms extra_context = {"title": _("Add a new aliment")} def get_sample_object(self): - return Basic_food( + return Allergen( + groundnut = "False", + ) + ''' + return BasicFood( name="", is_DLC=False, is_DDM=False, expiry_date=timezone.now(), label='pic/default.png', ) + ''' @transaction.atomic def form_valid(self, form): form.instance.creater = self.request.user + basic_food_form = BasicFoodForms(data=self.request.POST) + allergen_form = AllergenForms(data=self.request.POST) + if not basic_food_form.is_valid() or not allergen_form.is_valid(): + return self.form_invalid(form) + + # Save the aliment and the allergens associed + basic_food = form.save(commit=False) + basic_food._force_save = True + basic_food.save() + basic_food.refresh_from_db() return super().form_valid(form) def get_success_url(self, **kwargs): - self.objects.refresh_from_db() - return reverse_lazy('food:basic_food', kwargs={"pk": self.object.pk}) + self.object.refresh_from_db() + # return reverse_lazy('food:basicfood', kwargs={"pk": self.object.pk}) + return '0' + +class TransformedFoodCreateView(ProtectQuerysetMixin, ProtectedCreateView): + """ + A view to add a tranformed food + """ + model = TransformedFood + template_name = 'food/transformed_food_form.html' + form_class = TransformedFoodForms + extra_context = {"title": _("Add a new meal")} + + def get_sample_object(self): + return TransformedFood( + name="", + creation_date=timezone.now(), + ) + + @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 microbiologic analysis, the time of conservation is 3 days + transformed_food.expiry_date = transformed_food.creation_date + timedelta(days = 3) + transformed_food._force_save = True + transformed_food.save() + transformed_food.refresh_from_db() + return super().form_valid(form) + + + def get_success_url(self, **kwargs): + self.object.refresh_from_db() + # return reverse_lazy('food:tranformed_food', kwargs={"pk": self.object.pk}) + return '1' From 3f997f94fa1777b6e07b667536669bd301646890 Mon Sep 17 00:00:00 2001 From: quark Date: Sat, 25 May 2024 16:47:24 +0200 Subject: [PATCH 10/29] few changes in models, delete default label --- .../food/migrations/0005_auto_20240525_1559.py | 18 ++++++++++++++++++ apps/food/models.py | 1 - 2 files changed, 18 insertions(+), 1 deletion(-) create mode 100644 apps/food/migrations/0005_auto_20240525_1559.py diff --git a/apps/food/migrations/0005_auto_20240525_1559.py b/apps/food/migrations/0005_auto_20240525_1559.py new file mode 100644 index 00000000..88d794b2 --- /dev/null +++ b/apps/food/migrations/0005_auto_20240525_1559.py @@ -0,0 +1,18 @@ +# Generated by Django 2.2.28 on 2024-05-25 13:59 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('food', '0004_auto_20240525_1352'), + ] + + operations = [ + migrations.AlterField( + model_name='basicfood', + name='label', + field=models.ImageField(max_length=255, upload_to='label/', verbose_name='food label'), + ), + ] diff --git a/apps/food/models.py b/apps/food/models.py index b0a222f4..7bc47da1 100644 --- a/apps/food/models.py +++ b/apps/food/models.py @@ -194,7 +194,6 @@ class BasicFood(models.Model): blank=False, null=False, upload_to='label/', - default= 'pic/default.png', ) was_eaten = models.BooleanField( From 896095a44ccc235bc51865744fa41326ea6fc528 Mon Sep 17 00:00:00 2001 From: quark Date: Sat, 25 May 2024 22:34:59 +0200 Subject: [PATCH 11/29] Un peu de nettoyage, rajout de commentaires --- apps/food/admin.py | 3 + apps/food/migrations/0001_initial.py | 25 +++++---- .../migrations/0002_auto_20240524_2139.py | 19 ------- .../migrations/0003_auto_20240525_1350.py | 55 ------------------- .../migrations/0004_auto_20240525_1352.py | 18 ------ .../migrations/0005_auto_20240525_1559.py | 18 ------ apps/food/models.py | 12 +++- apps/food/templates/food/basic_food_form.html | 1 + .../templates/food/transformed_food_form.html | 1 + apps/food/urls.py | 8 ++- apps/food/views.py | 49 ++++++----------- 11 files changed, 51 insertions(+), 158 deletions(-) delete mode 100644 apps/food/migrations/0002_auto_20240524_2139.py delete mode 100644 apps/food/migrations/0003_auto_20240525_1350.py delete mode 100644 apps/food/migrations/0004_auto_20240525_1352.py delete mode 100644 apps/food/migrations/0005_auto_20240525_1559.py diff --git a/apps/food/admin.py b/apps/food/admin.py index 7579aff5..d5005adc 100644 --- a/apps/food/admin.py +++ b/apps/food/admin.py @@ -1,3 +1,6 @@ +# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# SPDX-License-Identifier: GPL-3.0-or-later + from django.contrib import admin from note_kfet.admin import admin_site diff --git a/apps/food/migrations/0001_initial.py b/apps/food/migrations/0001_initial.py index 49b7a329..b51c8a59 100644 --- a/apps/food/migrations/0001_initial.py +++ b/apps/food/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 2.2.28 on 2024-05-21 12:05 +# Generated by Django 2.2.28 on 2024-05-25 20:32 from django.db import migrations, models import django.db.models.deletion @@ -15,14 +15,15 @@ class Migration(migrations.Migration): operations = [ migrations.CreateModel( - name='Basic_food', + name='BasicFood', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('name', models.CharField(max_length=255, verbose_name='name')), ('is_DLC', models.BooleanField(default=False, verbose_name='is DLC')), ('is_DDM', models.BooleanField(default=False, verbose_name='is DDM')), - ('expiry_date', models.DateTimeField(blank=True, default=django.utils.timezone.now, verbose_name='expiry date')), - ('label', models.ImageField(default='pic/default.png', max_length=255, upload_to='label/', verbose_name='food label')), + ('arrival_date', models.DateTimeField(default=django.utils.timezone.now, verbose_name='arrival date')), + ('expiry_date', models.DateTimeField(blank=True, null=True, verbose_name='expiry date')), + ('label', models.ImageField(max_length=255, upload_to='label/', verbose_name='food label')), ('was_eaten', models.BooleanField(default=False, verbose_name='was eaten')), ('owner', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='+', to='member.Club', verbose_name='owner')), ], @@ -32,7 +33,7 @@ class Migration(migrations.Migration): }, ), migrations.CreateModel( - name='Transformed_food', + name='TransformedFood', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('name', models.CharField(max_length=255, verbose_name='name')), @@ -41,7 +42,7 @@ class Migration(migrations.Migration): ('is_active', models.BooleanField(default=True, verbose_name='is active')), ('was_eaten', models.BooleanField(default=False, verbose_name='was eaten')), ('owner', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='+', to='member.Club', verbose_name='owner')), - ('transformed_ingredient', models.ManyToManyField(blank=True, related_name='transformed_ingredient_inv', to='food.Transformed_food', verbose_name='transformed ingredient')), + ('transformed_ingredient', models.ManyToManyField(blank=True, related_name='transformed_ingredient_inv', to='food.TransformedFood', verbose_name='transformed ingredient')), ], options={ 'verbose_name': 'Transformed food', @@ -53,8 +54,8 @@ class Migration(migrations.Migration): fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('qr_code_number', models.PositiveIntegerField(verbose_name='QR-code number')), - ('basic_food', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='QR_code', to='food.Basic_food', verbose_name='basic food')), - ('transformed_food_container', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='QR_code', to='food.Transformed_food', verbose_name='transformed food container')), + ('basic_food', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='QR_code', to='food.BasicFood', verbose_name='basic food')), + ('transformed_food_container', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='QR_code', to='food.TransformedFood', verbose_name='transformed food container')), ], options={ 'verbose_name': 'QR-code', @@ -62,9 +63,9 @@ class Migration(migrations.Migration): }, ), migrations.AddField( - model_name='basic_food', + model_name='basicfood', name='transformed_food', - field=models.ManyToManyField(blank=True, related_name='Basic_food', to='food.Transformed_food', verbose_name='transformed food'), + field=models.ManyToManyField(blank=True, related_name='BasicFood', to='food.TransformedFood', verbose_name='transformed food'), ), migrations.CreateModel( name='Allergen', @@ -85,8 +86,8 @@ class Migration(migrations.Migration): ('groundnut', models.BooleanField(default=False, verbose_name='groundnut')), ('sesame', models.BooleanField(default=False, verbose_name='sesame')), ('alcohol', models.BooleanField(default=False, verbose_name='alcohol')), - ('basic_food', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='Allergen', to='food.Basic_food', verbose_name='basic food')), - ('transformed_food', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='Allergen', to='food.Transformed_food', verbose_name='transformed food')), + ('basic_food', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='Allergen', to='food.BasicFood', verbose_name='basic food')), + ('transformed_food', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='Allergen', to='food.TransformedFood', verbose_name='transformed food')), ], options={ 'verbose_name': 'Allergen', diff --git a/apps/food/migrations/0002_auto_20240524_2139.py b/apps/food/migrations/0002_auto_20240524_2139.py deleted file mode 100644 index d4423976..00000000 --- a/apps/food/migrations/0002_auto_20240524_2139.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 2.2.28 on 2024-05-24 19:39 - -from django.db import migrations, models -import django.utils.timezone - - -class Migration(migrations.Migration): - - dependencies = [ - ('food', '0001_initial'), - ] - - operations = [ - migrations.AlterField( - model_name='basic_food', - name='expiry_date', - field=models.DateTimeField(blank=True, default=django.utils.timezone.now, null=True, verbose_name='expiry date'), - ), - ] diff --git a/apps/food/migrations/0003_auto_20240525_1350.py b/apps/food/migrations/0003_auto_20240525_1350.py deleted file mode 100644 index 23321d64..00000000 --- a/apps/food/migrations/0003_auto_20240525_1350.py +++ /dev/null @@ -1,55 +0,0 @@ -# Generated by Django 2.2.28 on 2024-05-25 11:50 - -from django.db import migrations, models -import django.db.models.deletion -import django.utils.timezone - - -class Migration(migrations.Migration): - - dependencies = [ - ('member', '0011_profile_vss_charter_read'), - ('food', '0002_auto_20240524_2139'), - ] - - operations = [ - migrations.CreateModel( - name='BasicFood', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=255, verbose_name='name')), - ('is_DLC', models.BooleanField(default=False, verbose_name='is DLC')), - ('is_DDM', models.BooleanField(default=False, verbose_name='is DDM')), - ('expiry_date', models.DateTimeField(blank=True, default=django.utils.timezone.now, null=True, verbose_name='expiry date')), - ('label', models.ImageField(default='pic/default.png', max_length=255, upload_to='label/', verbose_name='food label')), - ('was_eaten', models.BooleanField(default=False, verbose_name='was eaten')), - ('owner', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='+', to='member.Club', verbose_name='owner')), - ], - options={ - 'verbose_name': 'Basic food', - 'verbose_name_plural': 'Basic foods', - }, - ), - migrations.AlterField( - model_name='allergen', - name='basic_food', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='Allergen', to='food.BasicFood', verbose_name='basic food'), - ), - migrations.AlterField( - model_name='qr_code', - name='basic_food', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='QR_code', to='food.BasicFood', verbose_name='basic food'), - ), - migrations.RenameModel( - old_name='Transformed_food', - new_name='TransformedFood', - ), - migrations.DeleteModel( - name='Basic_food', - ), - migrations.AddField( - model_name='basicfood', - name='transformed_food', - field=models.ManyToManyField(blank=True, null=True, related_name='BasicFood', to='food.TransformedFood', verbose_name='transformed food'), - ), - ] diff --git a/apps/food/migrations/0004_auto_20240525_1352.py b/apps/food/migrations/0004_auto_20240525_1352.py deleted file mode 100644 index 5c7932c4..00000000 --- a/apps/food/migrations/0004_auto_20240525_1352.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 2.2.28 on 2024-05-25 11:52 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('food', '0003_auto_20240525_1350'), - ] - - operations = [ - migrations.AlterField( - model_name='basicfood', - name='transformed_food', - field=models.ManyToManyField(blank=True, related_name='BasicFood', to='food.TransformedFood', verbose_name='transformed food'), - ), - ] diff --git a/apps/food/migrations/0005_auto_20240525_1559.py b/apps/food/migrations/0005_auto_20240525_1559.py deleted file mode 100644 index 88d794b2..00000000 --- a/apps/food/migrations/0005_auto_20240525_1559.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 2.2.28 on 2024-05-25 13:59 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('food', '0004_auto_20240525_1352'), - ] - - operations = [ - migrations.AlterField( - model_name='basicfood', - name='label', - field=models.ImageField(max_length=255, upload_to='label/', verbose_name='food label'), - ), - ] diff --git a/apps/food/models.py b/apps/food/models.py index 7bc47da1..44a9f595 100644 --- a/apps/food/models.py +++ b/apps/food/models.py @@ -13,6 +13,12 @@ from django.utils import timezone from django.utils.translation import gettext_lazy as _ from member.models import Club +################################################################# +# TO DO +# - link allergen with one food (basic or transformed) with check +# - check on basic food +# - check on transformed food +################################################################# class QR_code(models.Model): """ @@ -173,10 +179,14 @@ class BasicFood(models.Model): verbose_name=_("is DDM"), default=False, ) + + arrival_date = models.DateTimeField( + verbose_name=_('arrival date'), + default=timezone.now, + ) expiry_date = models.DateTimeField( verbose_name=_('expiry date'), - default=timezone.now, blank=True, null = True, ) diff --git a/apps/food/templates/food/basic_food_form.html b/apps/food/templates/food/basic_food_form.html index abce7e9b..43cf1c43 100644 --- a/apps/food/templates/food/basic_food_form.html +++ b/apps/food/templates/food/basic_food_form.html @@ -7,6 +7,7 @@ SPDX-License-Identifier: GPL-3.0-or-later {% block content %}

+ HTML not finished
{{ title }}

diff --git a/apps/food/templates/food/transformed_food_form.html b/apps/food/templates/food/transformed_food_form.html index 917654db..fd18a5f8 100644 --- a/apps/food/templates/food/transformed_food_form.html +++ b/apps/food/templates/food/transformed_food_form.html @@ -7,6 +7,7 @@ SPDX-License-Identifier: GPL-3.0-or-later {% block content %}

+ HTML not finished
{{ title }}

diff --git a/apps/food/urls.py b/apps/food/urls.py index b81bc8fa..7d4f54a3 100644 --- a/apps/food/urls.py +++ b/apps/food/urls.py @@ -5,12 +5,14 @@ from django.urls import path from . import views +############################### +# TO DO +# - name url correctly, thinking about the scheme of the app +############################### + app_name = 'food' urlpatterns = [ - path('test_basic_food_form',views.test_basic_foodform), - path('test_transformed_food_form', views.test_transformed_foodform), - path('test_allergen_form', views.test_allergenform), path('0', views.BasicFoodCreateView.as_view(), name = 'basic_food'), path('1', views.TransformedFoodCreateView.as_view(), name = 'transformed_food'), ] diff --git a/apps/food/views.py b/apps/food/views.py index 1768fd38..edd00e74 100644 --- a/apps/food/views.py +++ b/apps/food/views.py @@ -1,57 +1,35 @@ # Copyright (C) 2018-2024 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later -''' -from crispy_forms.helper import FormHelper -from django.contrib.auth.mixins import LoginRequiredMixin -from django.core.exceptions import ValidationError, PermissionDenied -from django.db.models import Q -from django.forms import Form -from django.http import HttpResponse -from django.shortcuts import redirect -''' + from django.urls import reverse_lazy from django.db import transaction from django.utils.translation import gettext_lazy as _ from django.utils import timezone from datetime import timedelta -''' -from django.views.generic import UpdateView, DetailView -from django.views.generic.base import View, TemplateView -from django.views.generic.edit import BaseFormView, DeleteView -from django_tables2 import SingleTableView -''' from permission.views import ProtectQuerysetMixin, ProtectedCreateView - from django.shortcuts import render from .forms import BasicFoodForms, TransformedFoodForms, AllergenForms from .models import BasicFood, TransformedFood, Allergen -def test_basic_foodform(request): - return render(request,'food/test.html', {"form": BasicFoodForms()}) - -def test_transformed_foodform(request): - return render(request,'food/transformed_food_form.html', {"form": TransformedFoodForms()}) - -def test_allergenform(request): - return render(request,'food/test.html', {"form": AllergenForms()}) - class BasicFoodCreateView(ProtectQuerysetMixin, ProtectedCreateView): +##################################################################### +# TO DO +# - fix picture save +# - implement solution crop and convert image (reuse or recode ImageForm from members apps +# - implement AllergenForms +# - redirect to another view after the poll is submitted +##################################################################### """ A view to add a basic food """ model = BasicFood form_class = BasicFoodForms template_name = 'food/basic_food_form.html' - second_form = AllergenForms extra_context = {"title": _("Add a new aliment")} def get_sample_object(self): - return Allergen( - groundnut = "False", - ) - ''' return BasicFood( name="", is_DLC=False, @@ -59,7 +37,6 @@ class BasicFoodCreateView(ProtectQuerysetMixin, ProtectedCreateView): expiry_date=timezone.now(), label='pic/default.png', ) - ''' @transaction.atomic def form_valid(self, form): @@ -71,6 +48,8 @@ class BasicFoodCreateView(ProtectQuerysetMixin, ProtectedCreateView): # 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() @@ -78,10 +57,15 @@ class BasicFoodCreateView(ProtectQuerysetMixin, ProtectedCreateView): def get_success_url(self, **kwargs): self.object.refresh_from_db() + # TEMPORARY, I create a fonctionnal view before # return reverse_lazy('food:basicfood', kwargs={"pk": self.object.pk}) return '0' class TransformedFoodCreateView(ProtectQuerysetMixin, ProtectedCreateView): +############################################### +# TO DO +# -redirect to another view after submit +############################################### """ A view to add a tranformed food """ @@ -105,7 +89,7 @@ class TransformedFoodCreateView(ProtectQuerysetMixin, ProtectedCreateView): # Save the aliment and allergens associated transformed_food = form.save(commit=False) - # Without microbiologic analysis, the time of conservation is 3 days + # Without microbiological analyzes, the storage time is 3 days transformed_food.expiry_date = transformed_food.creation_date + timedelta(days = 3) transformed_food._force_save = True transformed_food.save() @@ -115,5 +99,6 @@ class TransformedFoodCreateView(ProtectQuerysetMixin, ProtectedCreateView): def get_success_url(self, **kwargs): self.object.refresh_from_db() + # TEMPORARY, I create a fonctionnal view before # return reverse_lazy('food:tranformed_food', kwargs={"pk": self.object.pk}) return '1' From 210a3cc93c8b15eded326e6bbee1a3b382327584 Mon Sep 17 00:00:00 2001 From: korenstin Date: Wed, 3 Jul 2024 19:20:01 +0200 Subject: [PATCH 12/29] Implementing QRcode creation, modifying Allergen model and creating of few views --- apps/food/admin.py | 19 +- apps/food/apps.py | 1 + apps/food/fixtures/initial.json | 107 +++++ apps/food/forms.py | 37 +- apps/food/migrations/0001_initial.py | 117 ++--- .../migrations/0002_auto_20240703_1549.py | 30 ++ apps/food/models.py | 308 ++++--------- apps/food/static/food/js/food.js | 422 ++++++++++++++++++ apps/food/templates/food/basic_food_form.html | 3 +- .../food/templates/food/basicfood_detail.html | 18 + .../food/templates/food/create_food_form.html | 21 + .../templates/food/create_qrcode_form.html | 21 + apps/food/templates/food/qrcode_detail.html | 23 + .../templates/food/transformed_food_form.html | 2 +- .../food/transformedfood_detail.html | 19 + apps/food/tests.py | 2 +- apps/food/urls.py | 20 +- apps/food/views.py | 239 ++++++++-- 18 files changed, 1022 insertions(+), 387 deletions(-) create mode 100644 apps/food/fixtures/initial.json create mode 100644 apps/food/migrations/0002_auto_20240703_1549.py create mode 100644 apps/food/static/food/js/food.js create mode 100644 apps/food/templates/food/basicfood_detail.html create mode 100644 apps/food/templates/food/create_food_form.html create mode 100644 apps/food/templates/food/create_qrcode_form.html create mode 100644 apps/food/templates/food/qrcode_detail.html create mode 100644 apps/food/templates/food/transformedfood_detail.html diff --git a/apps/food/admin.py b/apps/food/admin.py index d5005adc..2bb1e302 100644 --- a/apps/food/admin.py +++ b/apps/food/admin.py @@ -4,28 +4,31 @@ from django.contrib import admin from note_kfet.admin import admin_site -from .models import QR_code, BasicFood, TransformedFood, Allergen +from .models import Allergen, BasicFood, QRCode, TransformedFood -@admin.register(QR_code, site = admin_site) -class QR_codeAdmin(admin.ModelAdmin): +@admin.register(QRCode, site=admin_site) +class QRCodeAdmin(admin.ModelAdmin): """ TEMPORARY """ -@admin.register(BasicFood, site = admin_site) -class Basic_foodAdmin(admin.ModelAdmin): + +@admin.register(BasicFood, site=admin_site) +class BasicFoodAdmin(admin.ModelAdmin): """ TEMPORARY """ -@admin.register(TransformedFood, site = admin_site) -class Transformed_foodAdmin(admin.ModelAdmin): + +@admin.register(TransformedFood, site=admin_site) +class TransformedFoodAdmin(admin.ModelAdmin): """ TEMPORARY """ -@admin.register(Allergen, site = admin_site) + +@admin.register(Allergen, site=admin_site) class AllergenAdmin(admin.ModelAdmin): """ TEMPORARY diff --git a/apps/food/apps.py b/apps/food/apps.py index a423e253..62ede85f 100644 --- a/apps/food/apps.py +++ b/apps/food/apps.py @@ -2,6 +2,7 @@ # SPDX-License-Identifier: GPL-3.0-or-later +from django.utils.translation import gettext_lazy as _ from django.apps import AppConfig diff --git a/apps/food/fixtures/initial.json b/apps/food/fixtures/initial.json new file mode 100644 index 00000000..c91a2dec --- /dev/null +++ b/apps/food/fixtures/initial.json @@ -0,0 +1,107 @@ +[ + { + "model": "food.allergen", + "pk": 1, + "fields": { + "name": "alcohol" + } + }, + { + "model": "food.allergen", + "pk": 2, + "fields": { + "name": "celery" + } + }, + { + "model": "food.allergen", + "pk": 3, + "fields": { + "name": "crustecean" + } + }, + { + "model": "food.allergen", + "pk": 4, + "fields": { + "name": "egg" + } + }, + { + "model": "food.allergen", + "pk": 5, + "fields": { + "name": "fish" + } + }, + { + "model": "food.allergen", + "pk": 6, + "fields": { + "name": "gluten" + } + }, + { + "model": "food.allergen", + "pk": 7, + "fields": { + "name": "groundnut" + } + }, + { + "model": "food.allergen", + "pk": 8, + "fields": { + "name": "lupine" + } + }, + { + "model": "food.allergen", + "pk": 9, + "fields": { + "name": "milk" + } + }, + { + "model": "food.allergen", + "pk": 10, + "fields": { + "name": "mollusc" + } + }, + { + "model": "food.allergen", + "pk": 11, + "fields": { + "name": "mustard" + } + }, + { + "model": "food.allergen", + "pk": 12, + "fields": { + "name": "nut" + } + }, + { + "model": "food.allergen", + "pk": 13, + "fields": { + "name": "sesame" + } + }, + { + "model": "food.allergen", + "pk": 14, + "fields": { + "name": "soy" + } + }, + { + "model": "food.allergen", + "pk": 15, + "fields": { + "name": "sulphite" + } + } +] diff --git a/apps/food/forms.py b/apps/food/forms.py index 5855c936..ad308727 100644 --- a/apps/food/forms.py +++ b/apps/food/forms.py @@ -7,11 +7,11 @@ from django import forms from django.utils.translation import gettext_lazy as _ from django.utils import timezone from member.models import Club -from note_kfet.inputs import Autocomplete, DatePickerInput, DateTimePickerInput +from note_kfet.inputs import Autocomplete, DateTimePickerInput from note_kfet.middlewares import get_current_request from permission.backends import PermissionBackend -from .models import QR_code, Allergen, BasicFood, TransformedFood +from .models import BasicFood, TransformedFood class BasicFoodForms(forms.ModelForm): @@ -23,33 +23,25 @@ class BasicFoodForms(forms.ModelForm): self.fields['name'].widget.attrs.update({"autofocus": "autofocus"}) self.fields['name'].required = True self.fields['owner'].required = True - self.fields['label'].help_text = _('The lot number must be contained in the picture') # Some example self.fields['name'].widget.attrs.update({"placeholder": _("pasta")}) clubs = list(Club.objects.filter(PermissionBackend.filter_queryset(get_current_request(), Club, "change")).all()) shuffle(clubs) self.fields['owner'].widget.attrs["placeholder"] = ", ".join(club.name for club in clubs[:4]) + ", ..." - def clean_dlm_or_dlc(self): - is_dlc = self.cleaned_data["is_DLC"] - is_ddm = self.cleaned_data["is_DDM"] - if is_dlc and is_ddm: - self.add_error("is_ddm", _("the product cannot be a DLC and a DDM")) - return is_ddm class Meta: model = BasicFood - fields = ('name', 'owner', 'is_DLC', 'is_DDM', 'expiry_date', 'label') + fields = ('name', 'owner', 'date_type', 'expiry_date', 'allergens') widgets = { "owner": Autocomplete( - model = Club, - attrs = {"api_url": "/api/members/club/"}, + model=Club, + attrs={"api_url": "/api/members/club/"}, ), - 'expiry_date': DatePickerInput(), + 'expiry_date': DateTimePickerInput(), } - class TransformedFoodForms(forms.ModelForm): """ Form for add transformed food @@ -69,24 +61,13 @@ class TransformedFoodForms(forms.ModelForm): shuffle(clubs) self.fields['owner'].widget.attrs["placeholder"] = ", ".join(club.name for club in clubs[:4]) + ", ..." - class Meta: model = TransformedFood - fields = ('name', 'creation_date', 'owner', 'is_active',) + fields = ('name', 'creation_date', 'owner', 'is_active', 'allergens') widgets = { "owner": Autocomplete( - model = Club, - attrs = {"api_url": "/api/members/club/"}, + model=Club, + attrs={"api_url": "/api/members/club/"}, ), 'creation_date': DateTimePickerInput(), } -class AllergenForms(forms.ModelForm): - """ - Form for allergen - """ - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - - class Meta: - model = Allergen - exclude = ['basic_food', 'transformed_food'] diff --git a/apps/food/migrations/0001_initial.py b/apps/food/migrations/0001_initial.py index b51c8a59..7d7d6745 100644 --- a/apps/food/migrations/0001_initial.py +++ b/apps/food/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 2.2.28 on 2024-05-25 20:32 +# Generated by Django 2.2.28 on 2024-07-03 07:40 from django.db import migrations, models import django.db.models.deletion @@ -11,87 +11,62 @@ class Migration(migrations.Migration): dependencies = [ ('member', '0011_profile_vss_charter_read'), + ('contenttypes', '0002_remove_content_type_name'), ] operations = [ - migrations.CreateModel( - name='BasicFood', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=255, verbose_name='name')), - ('is_DLC', models.BooleanField(default=False, verbose_name='is DLC')), - ('is_DDM', models.BooleanField(default=False, verbose_name='is DDM')), - ('arrival_date', models.DateTimeField(default=django.utils.timezone.now, verbose_name='arrival date')), - ('expiry_date', models.DateTimeField(blank=True, null=True, verbose_name='expiry date')), - ('label', models.ImageField(max_length=255, upload_to='label/', verbose_name='food label')), - ('was_eaten', models.BooleanField(default=False, verbose_name='was eaten')), - ('owner', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='+', to='member.Club', verbose_name='owner')), - ], - options={ - 'verbose_name': 'Basic food', - 'verbose_name_plural': 'Basic foods', - }, - ), - migrations.CreateModel( - name='TransformedFood', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=255, verbose_name='name')), - ('creation_date', models.DateTimeField(verbose_name='creation date')), - ('expiry_date', models.DateTimeField(verbose_name='expiry date')), - ('is_active', models.BooleanField(default=True, verbose_name='is active')), - ('was_eaten', models.BooleanField(default=False, verbose_name='was eaten')), - ('owner', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='+', to='member.Club', verbose_name='owner')), - ('transformed_ingredient', models.ManyToManyField(blank=True, related_name='transformed_ingredient_inv', to='food.TransformedFood', verbose_name='transformed ingredient')), - ], - options={ - 'verbose_name': 'Transformed food', - 'verbose_name_plural': 'Transformed foods', - }, - ), - migrations.CreateModel( - name='QR_code', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('qr_code_number', models.PositiveIntegerField(verbose_name='QR-code number')), - ('basic_food', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='QR_code', to='food.BasicFood', verbose_name='basic food')), - ('transformed_food_container', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='QR_code', to='food.TransformedFood', verbose_name='transformed food container')), - ], - options={ - 'verbose_name': 'QR-code', - 'verbose_name_plural': 'QR-codes', - }, - ), - migrations.AddField( - model_name='basicfood', - name='transformed_food', - field=models.ManyToManyField(blank=True, related_name='BasicFood', to='food.TransformedFood', verbose_name='transformed food'), - ), migrations.CreateModel( name='Allergen', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('gluten', models.BooleanField(default=False, verbose_name='gluten')), - ('nut', models.BooleanField(default=False, verbose_name='nut')), - ('crustecean', models.BooleanField(default=False, verbose_name='crustacean')), - ('celery', models.BooleanField(default=False, verbose_name='celery')), - ('egg', models.BooleanField(default=False, verbose_name='egg')), - ('mustard', models.BooleanField(default=False, verbose_name='mustard')), - ('fish', models.BooleanField(default=False, verbose_name='fish')), - ('soy', models.BooleanField(default=False, verbose_name='soy')), - ('milk', models.BooleanField(default=False, verbose_name='milk')), - ('sulphite', models.BooleanField(default=False, verbose_name='sulphite')), - ('lupine', models.BooleanField(default=False, verbose_name='lupine')), - ('mollusc', models.BooleanField(default=False, verbose_name='mollusc')), - ('groundnut', models.BooleanField(default=False, verbose_name='groundnut')), - ('sesame', models.BooleanField(default=False, verbose_name='sesame')), - ('alcohol', models.BooleanField(default=False, verbose_name='alcohol')), - ('basic_food', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='Allergen', to='food.BasicFood', verbose_name='basic food')), - ('transformed_food', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='Allergen', to='food.TransformedFood', verbose_name='transformed food')), + ('name', models.CharField(max_length=255, null=True, verbose_name='name')), ], options={ 'verbose_name': 'Allergen', 'verbose_name_plural': 'Allergens', }, ), + migrations.CreateModel( + name='Food', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=255, verbose_name='name')), + ('expiry_date', models.DateTimeField(verbose_name='expiry date')), + ('was_eaten', models.BooleanField(default=False, verbose_name='was eaten')), + ('code', models.IntegerField(unique=True, verbose_name='code')), + ('allergens', models.ManyToManyField(blank=True, to='food.Allergen', verbose_name='allergen')), + ('owner', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='+', to='member.Club', verbose_name='owner')), + ('polymorphic_ctype', models.ForeignKey(editable=False, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='polymorphic_food.food_set+', to='contenttypes.ContentType')), + ], + options={ + 'verbose_name': 'foods', + }, + ), + migrations.CreateModel( + name='BasicFood', + fields=[ + ('food_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='food.Food')), + ('date_type', models.CharField(choices=[('DLC', 'DLC'), ('DDM', 'DDM')], max_length=255)), + ('arrival_date', models.DateTimeField(blank=True, default=django.utils.timezone.now, verbose_name='arrival date')), + ], + options={ + 'verbose_name': 'Basic food', + 'verbose_name_plural': 'Basic foods', + }, + bases=('food.food',), + ), + migrations.CreateModel( + name='TransformedFood', + fields=[ + ('food_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='food.Food')), + ('creation_date', models.DateTimeField(verbose_name='creation date')), + ('is_active', models.BooleanField(default=True, verbose_name='is active')), + ('ingredient', models.ManyToManyField(blank=True, related_name='transformed_ingredient_inv', to='food.Food', verbose_name='transformed ingredient')), + ], + options={ + 'verbose_name': 'Transformed food', + 'verbose_name_plural': 'Transformed foods', + }, + bases=('food.food',), + ), ] diff --git a/apps/food/migrations/0002_auto_20240703_1549.py b/apps/food/migrations/0002_auto_20240703_1549.py new file mode 100644 index 00000000..fd3732c2 --- /dev/null +++ b/apps/food/migrations/0002_auto_20240703_1549.py @@ -0,0 +1,30 @@ +# Generated by Django 2.2.28 on 2024-07-03 13:49 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('food', '0001_initial'), + ] + + operations = [ + migrations.RemoveField( + model_name='food', + name='code', + ), + migrations.CreateModel( + name='QRCode', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('qr_code_number', models.PositiveIntegerField(verbose_name='QR-code number')), + ('food_container', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='QR_code', to='food.Food', unique=True, verbose_name='food container')), + ], + options={ + 'verbose_name': 'QR-code', + 'verbose_name_plural': 'QR-codes', + }, + ), + ] diff --git a/apps/food/models.py b/apps/food/models.py index 44a9f595..367e47f9 100644 --- a/apps/food/models.py +++ b/apps/food/models.py @@ -1,17 +1,11 @@ # Copyright (C) 2018-2024 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later -from datetime import date - -from django.conf import settings -from django.contrib.auth.models import User -from django.core.exceptions import ValidationError -from django.core.validators import MinValueValidator from django.db import models, transaction -from django.db.models import Q from django.utils import timezone from django.utils.translation import gettext_lazy as _ from member.models import Club +from polymorphic.models import PolymorphicModel ################################################################# # TO DO @@ -20,30 +14,21 @@ from member.models import Club # - check on transformed food ################################################################# -class QR_code(models.Model): + +class QRCode(models.Model): """ - An QR_code model + An QRCode model """ qr_code_number = models.PositiveIntegerField( verbose_name=_("QR-code number"), ) - transformed_food_container = models.ForeignKey( - 'TransformedFood', - on_delete = models.PROTECT, - related_name = 'QR_code', - null = True, - blank = True, - verbose_name = _('transformed food container'), - ) - - basic_food = models.ForeignKey( - 'BasicFood', - on_delete = models.PROTECT, - related_name = 'QR_code', - null = True, - blank = True, - verbose_name = _('basic food'), + food_container = models.ForeignKey( + 'Food', + on_delete=models.PROTECT, + related_name='QR_code', + unique=True, + verbose_name=_('food container'), ) class Meta: @@ -53,102 +38,14 @@ class QR_code(models.Model): 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 """ - - gluten = models.BooleanField( - default = False, - verbose_name = _('gluten'), - ) - - nut = models.BooleanField( - default = False, - verbose_name = _('nut'), - ) - - crustecean = models.BooleanField( - default = False, - verbose_name = _('crustacean'), - ) - - celery = models.BooleanField( - default = False, - verbose_name = _('celery'), - ) - - egg = models.BooleanField( - default = False, - verbose_name = _('egg'), - ) - - mustard = models.BooleanField( - default = False, - verbose_name = _('mustard'), - ) - - fish = models.BooleanField( - default = False, - verbose_name = _('fish'), - ) - - soy = models.BooleanField( - default = False, - verbose_name = _('soy'), - ) - - milk = models.BooleanField( - default = False, - verbose_name = _('milk'), - ) - - sulphite = models.BooleanField( - default = False, - verbose_name = _('sulphite'), - ) - - lupine = models.BooleanField( - default = False, - verbose_name = _('lupine'), - ) - - mollusc = models.BooleanField( - default = False, - verbose_name = _('mollusc'), - ) - - groundnut = models.BooleanField( - default = False, - verbose_name = _('groundnut'), - ) - - sesame = models.BooleanField( - default = False, - verbose_name = _('sesame'), - ) - - alcohol = models.BooleanField( - default = False, - verbose_name = _('alcohol'), - ) - - transformed_food = models.ForeignKey( - 'TransformedFood', - on_delete = models.CASCADE, - related_name = 'Allergen', - blank = True, - null = True, - verbose_name = _('transformed food'), - ) - - basic_food = models.ForeignKey( - 'BasicFood', - on_delete = models.CASCADE, - related_name = 'Allergen', - blank = True, - null = True, - verbose_name = _('basic food'), + name = models.CharField( + verbose_name=_('name'), + max_length=255, ) class Meta: @@ -156,134 +53,101 @@ class Allergen(models.Model): verbose_name_plural = _('Allergens') def __str__(self): - return _('Allergens of #{id}').format(id=self.id) - - + return self.name -class BasicFood(models.Model): - """ - Food which has been directly buy on supermarket - """ +class Food(PolymorphicModel): name = models.CharField( verbose_name=_('name'), max_length=255, ) - is_DLC = models.BooleanField( - verbose_name=_("is DLC"), - default=False, - ) - - is_DDM = models.BooleanField( - verbose_name=_("is DDM"), - default=False, - ) - - arrival_date = models.DateTimeField( - verbose_name=_('arrival date'), - default=timezone.now, - ) - - expiry_date = models.DateTimeField( - verbose_name=_('expiry date'), - blank=True, - null = True, - ) - owner = models.ForeignKey( Club, on_delete=models.PROTECT, - related_name= '+', + related_name='+', verbose_name=_('owner'), ) - label = models.ImageField( - verbose_name=_('food label'), - max_length=255, - blank=False, - null=False, - upload_to='label/', - ) - - was_eaten = models.BooleanField( - verbose_name=_('was eaten'), - default = False, - ) - - transformed_food = models.ManyToManyField( - 'TransformedFood', - related_name= 'BasicFood', - blank = True, - verbose_name = _('transformed food'), - ) - - - class Meta: - verbose_name=_('Basic food') - verbose_name_plural=_('Basic foods') - - def __str__(self): - return self.name - - @transaction.atomic - def save(self, force_insert=False, force_update=False, using= None, update_fields=None): - # Check if is_DLC and is DDM are not both True - if self.is_DLC and self.is_DDM: - raise ValidationError("The product cannot be a DLC and a DDM") - return super().save(force_insert, force_update, using, update_fields) - - -class TransformedFood(models.Model): - """ - Transformed food are a mix between basic food and meal - """ - name = models.CharField( - max_length = 255, - verbose_name =_('name'), - ) - - creation_date = models.DateTimeField( - verbose_name =_('creation date'), + allergens = models.ManyToManyField( + Allergen, + blank=True, + verbose_name=_('allergen'), ) expiry_date = models.DateTimeField( - verbose_name =_('expiry date'), - ) - - owner = models.ForeignKey( - Club, - on_delete = models.PROTECT, - related_name = '+', - verbose_name =_('owner'), - ) - - transformed_ingredient = models.ManyToManyField( - "self", - blank = True, - symmetrical = False, - related_name = 'transformed_ingredient_inv', - verbose_name = _('transformed ingredient'), - ) - - is_active = models.BooleanField( - default = True, - verbose_name = _('is active'), + verbose_name=_('expiry date'), ) was_eaten = models.BooleanField( - default = False, - verbose_name = _('was eaten'), + default=False, + verbose_name=_('was eaten'), ) - - class Meta: - verbose_name = _('Transformed food') - verbose_name_plural = _('Transformed foods') - def __str__(self): return self.name @transaction.atomic - def save(self, force_insert=False, force_update=False, using= None, update_fields=None): + 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, + blank=True, # TEMPORARY + ) + + # label = models.ImageField( + # verbose_name=_('food label'), + # max_length=255, + # blank=False, + # null=False, + # upload_to='label/', + # ) + + 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'), + ) + + is_active = models.BooleanField( + default=True, + verbose_name=_('is active'), + ) + + class Meta: + verbose_name = _('Transformed food') + verbose_name_plural = _('Transformed foods') diff --git a/apps/food/static/food/js/food.js b/apps/food/static/food/js/food.js new file mode 100644 index 00000000..509d9b48 --- /dev/null +++ b/apps/food/static/food/js/food.js @@ -0,0 +1,422 @@ +var LOCK = false + +sources = [] +sources_notes_display = [] +dests = [] +dests_notes_display = [] + +function refreshHistory () { + $('#history').load('/note/transfer/ #history') +} + +function reset (refresh = true) { + sources_notes_display.length = 0 + sources.length = 0 + dests_notes_display.length = 0 + dests.length = 0 + $('#source_note_list').html('') + $('#dest_note_list').html('') + const source_field = $('#source_note') + source_field.val('') + const event = jQuery.Event('keyup') + event.originalEvent = { charCode: 97 } + source_field.trigger(event) + source_field.removeClass('is-invalid') + source_field.attr('data-original-title', '').tooltip('hide') + const dest_field = $('#dest_note') + dest_field.val('') + dest_field.trigger(event) + dest_field.removeClass('is-invalid') + dest_field.attr('data-original-title', '').tooltip('hide') + const amount_field = $('#amount') + amount_field.val('') + amount_field.removeClass('is-invalid') + $('#amount-required').html('') + const reason_field = $('#reason') + reason_field.val('') + reason_field.removeClass('is-invalid') + $('#reason-required').html('') + $('#last_name').val('') + $('#first_name').val('') + $('#bank').val('') + $('#user_note').val('') + $('#profile_pic').attr('src', '/static/member/img/default_picture.png') + $('#profile_pic_link').attr('href', '#') + if (refresh) { + refreshBalance() + refreshHistory() + } + + LOCK = false +} + +$(document).ready(function () { + /** + * If we are in credit/debit mode, check that only one note is entered. + * More over, get first name and last name to autocomplete fields. + */ + function checkUniqueNote () { + if ($('#type_credit').is(':checked') || $('#type_debit').is(':checked')) { + const arr = $('#type_credit').is(':checked') ? dests_notes_display : sources_notes_display + + if (arr.length === 0) { return } + + const last = arr[arr.length - 1] + arr.length = 0 + arr.push(last) + + last.quantity = 1 + + if (last.note.club) { + $('#last_name').val(last.note.name) + $('#first_name').val(last.note.name) + } + else if (!last.note.user) { + $.getJSON('/api/note/note/' + last.note.id + '/?format=json', function (note) { + last.note.user = note.user + $.getJSON('/api/user/' + last.note.user + '/', function (user) { + $('#last_name').val(user.last_name) + $('#first_name').val(user.first_name) + }) + }) + } else { + $.getJSON('/api/user/' + last.note.user + '/', function (user) { + $('#last_name').val(user.last_name) + $('#first_name').val(user.first_name) + }) + } + } + + return true + } + + autoCompleteNote('source_note', 'source_note_list', sources, sources_notes_display, + 'source_alias', 'source_note', 'user_note', 'profile_pic', checkUniqueNote) + autoCompleteNote('dest_note', 'dest_note_list', dests, dests_notes_display, + 'dest_alias', 'dest_note', 'user_note', 'profile_pic', checkUniqueNote) + + const source = $('#source_note') + const dest = $('#dest_note') + + $('#type_transfer').change(function () { + if (LOCK) { return } + + $('#source_me_div').removeClass('d-none') + $('#source_note').removeClass('is-invalid') + $('#dest_note').removeClass('is-invalid') + $('#special_transaction_div').addClass('d-none') + source.removeClass('d-none') + $('#source_note_list').removeClass('d-none') + $('#credit_type').addClass('d-none') + dest.removeClass('d-none') + $('#dest_note_list').removeClass('d-none') + $('#debit_type').addClass('d-none') + + $('#source_note_label').text(select_emitters_label) + $('#dest_note_label').text(select_receveirs_label) + + location.hash = 'transfer' + }) + + $('#type_credit').change(function () { + if (LOCK) { return } + + $('#source_me_div').addClass('d-none') + $('#source_note').removeClass('is-invalid') + $('#dest_note').removeClass('is-invalid') + $('#special_transaction_div').removeClass('d-none') + $('#source_note_list').addClass('d-none') + $('#dest_note_list').removeClass('d-none') + source.addClass('d-none') + source.tooltip('hide') + $('#credit_type').removeClass('d-none') + dest.removeClass('d-none') + dest.val('') + dest.tooltip('hide') + $('#debit_type').addClass('d-none') + + $('#source_note_label').text(transfer_type_label) + $('#dest_note_label').text(select_receveir_label) + + if (dests_notes_display.length > 1) { + $('#dest_note_list').html('') + dests_notes_display.length = 0 + } + + location.hash = 'credit' + }) + + $('#type_debit').change(function () { + if (LOCK) { return } + + $('#source_me_div').addClass('d-none') + $('#source_note').removeClass('is-invalid') + $('#dest_note').removeClass('is-invalid') + $('#special_transaction_div').removeClass('d-none') + $('#source_note_list').removeClass('d-none') + $('#dest_note_list').addClass('d-none') + source.removeClass('d-none') + source.val('') + source.tooltip('hide') + $('#credit_type').addClass('d-none') + dest.addClass('d-none') + dest.tooltip('hide') + $('#debit_type').removeClass('d-none') + + $('#source_note_label').text(select_emitter_label) + $('#dest_note_label').text(transfer_type_label) + + if (sources_notes_display.length > 1) { + $('#source_note_list').html('') + sources_notes_display.length = 0 + } + + location.hash = 'debit' + }) + + $('#credit_type').change(function () { + const type = $('#credit_type option:selected').text() + if ($('#type_credit').is(':checked')) { source.val(type) } else { dest.val(type) } + }) + + // Ensure we begin in transfer mode. Removing these lines may cause problems when reloading. + const type_transfer = $('#type_transfer') // Default mode + type_transfer.removeAttr('checked') + $('#type_credit').removeAttr('checked') + $('#type_debit').removeAttr('checked') + + if (location.hash) { $('#type_' + location.hash.substr(1)).click() } else { type_transfer.click() } + + $('#source_me').click(function () { + if (LOCK) { return } + + // Shortcut to set the current user as the only emitter + sources_notes_display.length = 0 + sources.length = 0 + $('#source_note_list').html('') + + const source_note = $('#source_note') + source_note.focus() + source_note.val('') + let event = jQuery.Event('keyup') + event.originalEvent = { charCode: 97 } + source_note.trigger(event) + source_note.val(username) + event = jQuery.Event('keyup') + event.originalEvent = { charCode: 97 } + source_note.trigger(event) + const fill_note = function () { + if (sources.length === 0) { + setTimeout(fill_note, 100) + return + } + event = jQuery.Event('keypress') + event.originalEvent = { charCode: 13 } + source_note.trigger(event) + + source_note.tooltip('hide') + source_note.val('') + $('#dest_note').focus() + } + fill_note() + }) +}) + +// Make transfer when pressing Enter on the amount section +$('#amount, #reason, #last_name, #first_name, #bank').keypress((event) => { + if (event.originalEvent.charCode === 13) { + $('#btn_transfer').click() + } +}) + +$('#btn_transfer').click(function () { + if (LOCK) { return } + + LOCK = true + + let error = false + + const amount_field = $('#amount') + amount_field.removeClass('is-invalid') + $('#amount-required').html('') + + const reason_field = $('#reason') + reason_field.removeClass('is-invalid') + $('#reason-required').html('') + + if (!amount_field.val() || isNaN(amount_field.val()) || amount_field.val() <= 0) { + amount_field.addClass('is-invalid') + $('#amount-required').html('' + gettext('This field is required and must contain a decimal positive number.') + '') + error = true + } + + const amount = Math.round(100 * amount_field.val()) + if (amount > 2147483647) { + amount_field.addClass('is-invalid') + $('#amount-required').html('' + gettext('The amount must stay under 21,474,836.47 €.') + '') + error = true + } + + if (!reason_field.val() && $('#type_transfer').is(':checked')) { + reason_field.addClass('is-invalid') + $('#reason-required').html('' + gettext('This field is required.') + '') + error = true + } + + if (!sources_notes_display.length && !$('#type_credit').is(':checked')) { + $('#source_note').addClass('is-invalid') + error = true + } + + if (!dests_notes_display.length && !$('#type_debit').is(':checked')) { + $('#dest_note').addClass('is-invalid') + error = true + } + + if (error) { + LOCK = false + return + } + + let reason = reason_field.val() + + if ($('#type_transfer').is(':checked')) { + // We copy the arrays to ensure that transactions are well-processed even if the form is reset + [...sources_notes_display].forEach(function (source) { + [...dests_notes_display].forEach(function (dest) { + if (source.note.id === dest.note.id) { + addMsg(interpolate(gettext('Warning: the transaction of %s from %s to %s was not made because ' + + 'it is the same source and destination note.'), [pretty_money(amount), source.name, dest.name]), 'warning', 10000) + LOCK = false + return + } + + $.post('/api/note/transaction/transaction/', + { + csrfmiddlewaretoken: CSRF_TOKEN, + quantity: source.quantity * dest.quantity, + amount: amount, + reason: reason, + valid: true, + polymorphic_ctype: TRANSFER_POLYMORPHIC_CTYPE, + resourcetype: 'Transaction', + source: source.note.id, + source_alias: source.name, + destination: dest.note.id, + destination_alias: dest.name + }).done(function () { + if (source.note.membership && source.note.membership.date_end < new Date().toISOString()) { + addMsg(interpolate(gettext('Warning, the emitter note %s is no more a BDE member.'), [source.name]), 'danger', 30000) + } + if (dest.note.membership && dest.note.membership.date_end < new Date().toISOString()) { + addMsg(interpolate(gettext('Warning, the destination note %s is no more a BDE member.'), [dest.name]), 'danger', 30000) + } + + if (!isNaN(source.note.balance)) { + const newBalance = source.note.balance - source.quantity * dest.quantity * amount + if (newBalance <= -2000) { + addMsg(interpolate(gettext('Warning, the transaction of %s from the note %s to the note %s succeed, but the emitter note %s is very negative.'), + [pretty_money(source.quantity * dest.quantity * amount), source.name, dest.name, source.name]), 'danger', 10000) + reset() + return + } else if (newBalance < 0) { + addMsg(interpolate(gettext('Warning, the transaction of %s from the note %s to the note %s succeed, but the emitter note %s is negative.'), + [pretty_money(source.quantity * dest.quantity * amount), source.name, dest.name, source.name]), 'danger', 10000) + reset() + return + } + } + addMsg(interpolate(gettext('Transfer of %s from %s to %s succeed!'), + [pretty_money(source.quantity * dest.quantity * amount), source.name, dest.name]), 'success', 10000) + + reset() + }).fail(function (err) { // do it again but valid = false + const errObj = JSON.parse(err.responseText) + if (errObj.non_field_errors) { + addMsg(interpolate(gettext('Transfer of %s from %s to %s failed: %s'), + [pretty_money(source.quantity * dest.quantity * amount), source.name, dest.name, errObj.non_field_errors]), 'danger') + LOCK = false + return + } + + $.post('/api/note/transaction/transaction/', + { + csrfmiddlewaretoken: CSRF_TOKEN, + quantity: source.quantity * dest.quantity, + amount: amount, + reason: reason, + valid: false, + invalidity_reason: 'Solde insuffisant', + polymorphic_ctype: TRANSFER_POLYMORPHIC_CTYPE, + resourcetype: 'Transaction', + source: source.note.id, + source_alias: source.name, + destination: dest.note.id, + destination_alias: dest.name + }).done(function () { + addMsg(interpolate(gettext('Transfer of %s from %s to %s failed: %s'), + [pretty_money(source.quantity * dest.quantity * amount), source.name, dest.name, gettext('insufficient funds')]), 'danger', 10000) + reset() + }).fail(function (err) { + const errObj = JSON.parse(err.responseText) + let error = errObj.detail ? errObj.detail : errObj.non_field_errors + if (!error) { error = err.responseText } + addMsg(interpolate(gettext('Transfer of %s from %s to %s failed: %s'), + [pretty_money(source.quantity * dest.quantity * amount), source.name, dest.name, error]), 'danger') + LOCK = false + }) + }) + }) + }) + } else if ($('#type_credit').is(':checked') || $('#type_debit').is(':checked')) { + let special_note + let user_note + let alias + const given_reason = reason + let source_id, dest_id + if ($('#type_credit').is(':checked')) { + special_note = $('#credit_type').val() + user_note = dests_notes_display[0].note + alias = dests_notes_display[0].name + source_id = special_note + dest_id = user_note.id + reason = 'Crédit ' + $('#credit_type option:selected').text().toLowerCase() + if (given_reason.length > 0) { reason += ' (' + given_reason + ')' } + } else { + special_note = $('#debit_type').val() + user_note = sources_notes_display[0].note + alias = sources_notes_display[0].name + source_id = user_note.id + dest_id = special_note + reason = 'Retrait ' + $('#debit_type option:selected').text().toLowerCase() + if (given_reason.length > 0) { reason += ' (' + given_reason + ')' } + } + $.post('/api/note/transaction/transaction/', + { + csrfmiddlewaretoken: CSRF_TOKEN, + quantity: 1, + amount: amount, + reason: reason, + valid: true, + polymorphic_ctype: SPECIAL_TRANSFER_POLYMORPHIC_CTYPE, + resourcetype: 'SpecialTransaction', + source: source_id, + source_alias: sources_notes_display.length ? alias : null, + destination: dest_id, + destination_alias: dests_notes_display.length ? alias : null, + last_name: $('#last_name').val(), + first_name: $('#first_name').val(), + bank: $('#bank').val() + }).done(function () { + addMsg(gettext('Credit/debit succeed!'), 'success', 10000) + if (user_note.membership && user_note.membership.date_end < new Date().toISOString()) { addMsg(gettext('Warning, the emitter note %s is no more a BDE member.'), 'danger', 10000) } + reset() + }).fail(function (err) { + const errObj = JSON.parse(err.responseText) + let error = errObj.detail ? errObj.detail : errObj.non_field_errors + if (!error) { error = err.responseText } + addMsg(interpolate(gettext('Credit/debit failed: %s'), [error]), 'danger', 10000) + LOCK = false + }) + } +}) diff --git a/apps/food/templates/food/basic_food_form.html b/apps/food/templates/food/basic_food_form.html index 43cf1c43..dbfb49e3 100644 --- a/apps/food/templates/food/basic_food_form.html +++ b/apps/food/templates/food/basic_food_form.html @@ -10,11 +10,10 @@ SPDX-License-Identifier: GPL-3.0-or-later HTML not finished
{{ title }} -
+
{% csrf_token %} {{ form|crispy }} - {{ allergenform|crispy }}
diff --git a/apps/food/templates/food/basicfood_detail.html b/apps/food/templates/food/basicfood_detail.html new file mode 100644 index 00000000..86b7ac9e --- /dev/null +++ b/apps/food/templates/food/basicfood_detail.html @@ -0,0 +1,18 @@ +{% extends "base.html" %} +{% comment %} +SPDX-License-Identifier: GPL-3.0-or-later +{% endcomment %} +{% load i18n crispy_forms_tags %} + +{% block content %} +
+

+ HTML not finished
+ {{ title }} +

+
+

name : {{ food.name }}

+ Update +
+
+{% endblock %} diff --git a/apps/food/templates/food/create_food_form.html b/apps/food/templates/food/create_food_form.html new file mode 100644 index 00000000..137c1880 --- /dev/null +++ b/apps/food/templates/food/create_food_form.html @@ -0,0 +1,21 @@ +{% extends "base.html" %} +{% comment %} +SPDX-License-Identifier: GPL-3.0-or-later +{% endcomment %} + +{% block content %} +
+

+ HTML not finished
+ {{ title }} +

+
+
+ +
+
+
+{% endblock %} diff --git a/apps/food/templates/food/create_qrcode_form.html b/apps/food/templates/food/create_qrcode_form.html new file mode 100644 index 00000000..6af507ca --- /dev/null +++ b/apps/food/templates/food/create_qrcode_form.html @@ -0,0 +1,21 @@ +{% extends "base.html" %} +{% comment %} +SPDX-License-Identifier: GPL-3.0-or-later +{% endcomment %} + +{% block content %} +
+

+ HTML not finished
+ {{ title }} +

+
+
+ +
+
+
+{% endblock %} diff --git a/apps/food/templates/food/qrcode_detail.html b/apps/food/templates/food/qrcode_detail.html new file mode 100644 index 00000000..5e11ecf4 --- /dev/null +++ b/apps/food/templates/food/qrcode_detail.html @@ -0,0 +1,23 @@ +{% extends "base.html" %} +{% comment %} +SPDX-License-Identifier: GPL-3.0-or-later +{% endcomment %} +{% load i18n crispy_forms_tags %} + +{% block content %} +
+

+ HTML not finished
+ {{ title }} +

+
+

qrcode : {{ qrcode.qr_code_number }}

+

name : {{ qrcode.food_container.name }}

+ {% if qrcode.food_container.polymorphic_ctype.name == 'Basic food' %} + Update + {% else %} + Update + {% endif %} +
+
+{% endblock %} diff --git a/apps/food/templates/food/transformed_food_form.html b/apps/food/templates/food/transformed_food_form.html index fd18a5f8..86e3b03e 100644 --- a/apps/food/templates/food/transformed_food_form.html +++ b/apps/food/templates/food/transformed_food_form.html @@ -10,7 +10,7 @@ SPDX-License-Identifier: GPL-3.0-or-later HTML not finished
{{ title }} -
+
{% csrf_token %} {{ form|crispy }} diff --git a/apps/food/templates/food/transformedfood_detail.html b/apps/food/templates/food/transformedfood_detail.html new file mode 100644 index 00000000..d73b0d09 --- /dev/null +++ b/apps/food/templates/food/transformedfood_detail.html @@ -0,0 +1,19 @@ +{% extends "base.html" %} +{% comment %} +SPDX-License-Identifier: GPL-3.0-or-later +{% endcomment %} +{% load i18n crispy_forms_tags %} + +{% block content %} +
+

+ HTML not finished
+ {{ title }} +

+
+

name : {{ food.name }}

+

owner : {{ food.owner }}

+ Update +
+
+{% endblock %} diff --git a/apps/food/tests.py b/apps/food/tests.py index 7ce503c2..a79ca8be 100644 --- a/apps/food/tests.py +++ b/apps/food/tests.py @@ -1,3 +1,3 @@ -from django.test import TestCase +# from django.test import TestCase # Create your tests here. diff --git a/apps/food/urls.py b/apps/food/urls.py index 7d4f54a3..c422e514 100644 --- a/apps/food/urls.py +++ b/apps/food/urls.py @@ -5,15 +5,19 @@ from django.urls import path from . import views -############################### -# TO DO -# - name url correctly, thinking about the scheme of the app -############################### - app_name = 'food' urlpatterns = [ - path('0', views.BasicFoodCreateView.as_view(), name = 'basic_food'), - path('1', views.TransformedFoodCreateView.as_view(), name = 'transformed_food'), -] + path('', views.QRCodeView.as_view(), name='qrcode_view'), + path('detail/', views.FoodView.as_view(), name='food_view'), + path('/create_qrcode', views.QRCodeCreateView.as_view(), name='qrcode_create'), + path('create', views.FoodCreateView.as_view(), name='food_create'), + path('/create_qrcode/basic', views.QRCodeBasicFoodCreateView.as_view(), name='qrcode_basic_create'), + path('/create_qrcode/transformed', views.QRCodeTransformedFoodCreateView.as_view(), name='qrcode_transformed_create'), + path('create/basic', views.BasicFoodCreateView.as_view(), name='basic_create'), + path('create/transformed', views.TransformedFoodCreateView.as_view(), name='transformed_create'), + + path('update/basic/', views.BasicFoodUpdateView.as_view(), name='basic_update'), + path('update/transformed/', views.TransformedFoodUpdateView.as_view(), name='transformed_update'), +] diff --git a/apps/food/views.py b/apps/food/views.py index edd00e74..5964f3f7 100644 --- a/apps/food/views.py +++ b/apps/food/views.py @@ -1,26 +1,74 @@ # Copyright (C) 2018-2024 by BDE ENS Paris-Saclay # SPDX-License-Identifier: GPL-3.0-or-later -from django.urls import reverse_lazy +from datetime import timedelta + from django.db import transaction +from django.contrib.auth.mixins import LoginRequiredMixin +from django.http import HttpResponseRedirect +from django.urls import reverse from django.utils.translation import gettext_lazy as _ from django.utils import timezone -from datetime import timedelta +from django.views.generic import DetailView, UpdateView, TemplateView from permission.views import ProtectQuerysetMixin, ProtectedCreateView -from django.shortcuts import render -from .forms import BasicFoodForms, TransformedFoodForms, AllergenForms -from .models import BasicFood, TransformedFood, Allergen +from .forms import BasicFoodForms, TransformedFoodForms +from .models import BasicFood, Food, QRCode, TransformedFood -class BasicFoodCreateView(ProtectQuerysetMixin, ProtectedCreateView): -##################################################################### -# TO DO -# - fix picture save -# - implement solution crop and convert image (reuse or recode ImageForm from members apps -# - implement AllergenForms -# - redirect to another view after the poll is submitted -##################################################################### +class QRCodeView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView): + """ + A view to add a basic food + """ + model = QRCode + extra_context = {"title": _("Add a new meal")} + 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)) + + +class QRCodeCreateView(ProtectQuerysetMixin, LoginRequiredMixin, TemplateView): + """ + A view to add a basic food + """ + template_name = 'food/create_qrcode_form.html' + extra_context = {"title": _("Add a new aliment")} + + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + context["slug"] = kwargs["slug"] + return context + + +class FoodView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView): + """ + A view to add a basic food + """ + model = Food + extra_context = {"title": _("Add a new meal")} + context_object_name = "food" + + +class FoodCreateView(ProtectQuerysetMixin, LoginRequiredMixin, TemplateView): + """ + A view to add a basic food + """ + template_name = 'food/create_food_form.html' + extra_context = {"title": _("Add a new aliment")} + + +class BasicFoodFormView(ProtectQuerysetMixin): + ##################################################################### + # TO DO + # - fix picture save + # - implement solution crop and convert image (reuse or recode ImageForm from members apps) + ##################################################################### """ A view to add a basic food """ @@ -28,28 +76,18 @@ class BasicFoodCreateView(ProtectQuerysetMixin, ProtectedCreateView): form_class = BasicFoodForms template_name = 'food/basic_food_form.html' extra_context = {"title": _("Add a new aliment")} - - def get_sample_object(self): - return BasicFood( - name="", - is_DLC=False, - is_DDM=False, - expiry_date=timezone.now(), - label='pic/default.png', - ) - + @transaction.atomic def form_valid(self, form): form.instance.creater = self.request.user basic_food_form = BasicFoodForms(data=self.request.POST) - allergen_form = AllergenForms(data=self.request.POST) - if not basic_food_form.is_valid() or not allergen_form.is_valid(): + 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.arrival_date = timezone.now() basic_food._force_save = True basic_food.save() basic_food.refresh_from_db() @@ -57,15 +95,74 @@ class BasicFoodCreateView(ProtectQuerysetMixin, ProtectedCreateView): def get_success_url(self, **kwargs): self.object.refresh_from_db() - # TEMPORARY, I create a fonctionnal view before - # return reverse_lazy('food:basicfood', kwargs={"pk": self.object.pk}) - return '0' + return reverse('food:food_view', kwargs={"pk": self.object.pk}) -class TransformedFoodCreateView(ProtectQuerysetMixin, ProtectedCreateView): -############################################### -# TO DO -# -redirect to another view after submit -############################################### + +class BasicFoodUpdateView(BasicFoodFormView, LoginRequiredMixin, UpdateView): + pass + + +class BasicFoodCreateView(BasicFoodFormView, ProtectedCreateView): + def get_sample_object(self): + return BasicFood( + name="", + expiry_date=timezone.now(), + ) + + +class QRCodeBasicFoodCreateView(ProtectQuerysetMixin, ProtectedCreateView): + ##################################################################### + # TO DO + # - fix picture save + # - implement solution crop and convert image (reuse or recode ImageForm from members apps) + ##################################################################### + """ + A view to add a basic food + """ + model = BasicFood + form_class = BasicFoodForms + template_name = 'food/basic_food_form.html' + extra_context = {"title": _("Add a new 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) + + # 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() + + 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:food_view', kwargs={"pk": self.object.pk}) + + def get_sample_object(self): + return BasicFood( + name="", + expiry_date=timezone.now(), + ) + + +class TransformedFoodFormView(ProtectQuerysetMixin): + ##################################################################### + # TO DO + # - fix picture save + # - implement solution crop and convert image (reuse or recode ImageForm from members apps) + ##################################################################### """ A view to add a tranformed food """ @@ -73,13 +170,7 @@ class TransformedFoodCreateView(ProtectQuerysetMixin, ProtectedCreateView): template_name = 'food/transformed_food_form.html' form_class = TransformedFoodForms extra_context = {"title": _("Add a new meal")} - - def get_sample_object(self): - return TransformedFood( - name="", - creation_date=timezone.now(), - ) - + @transaction.atomic def form_valid(self, form): form.instance.creater = self.request.user @@ -90,15 +181,71 @@ class TransformedFoodCreateView(ProtectQuerysetMixin, ProtectedCreateView): # 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._force_save = True + transformed_food.expiry_date = transformed_food.creation_date + timedelta(days=3) + transformed_food._force_save = True transformed_food.save() transformed_food.refresh_from_db() return super().form_valid(form) + def get_success_url(self, **kwargs): + self.object.refresh_from_db() + return reverse('food:food_view', kwargs={"pk": self.object.pk}) + + +class TransformedFoodUpdateView(TransformedFoodFormView, LoginRequiredMixin, UpdateView): + pass + + +class TransformedFoodCreateView(TransformedFoodFormView, ProtectedCreateView): + def get_sample_object(self): + return TransformedFood( + name="", + creation_date=timezone.now(), + ) + + +class QRCodeTransformedFoodCreateView(ProtectQuerysetMixin, ProtectedCreateView): + ##################################################################### + # TO DO + # - fix picture save + # - implement solution crop and convert image (reuse or recode ImageForm from members apps) + ##################################################################### + """ + A view to add a basic food + """ + model = TransformedFood + template_name = 'food/transformed_food_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) + # Without microbiological analyzes, the storage time is 3 days + transformed_food.expiry_date = transformed_food.creation_date + timedelta(days=3) + 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() - # TEMPORARY, I create a fonctionnal view before - # return reverse_lazy('food:tranformed_food', kwargs={"pk": self.object.pk}) - return '1' + return reverse('food:food_view', kwargs={"pk": self.object.pk}) + + def get_sample_object(self): + return BasicFood( + name="", + expiry_date=timezone.now(), + ) From 260513ae3b9145cd1ad94d89013ae51293cb568e Mon Sep 17 00:00:00 2001 From: korenstin Date: Thu, 4 Jul 2024 17:11:38 +0200 Subject: [PATCH 13/29] Migration fixes --- apps/food/static/food/js/food.js | 422 ------------------ .../migrations/0002_create_special_notes.py | 1 + 2 files changed, 1 insertion(+), 422 deletions(-) delete mode 100644 apps/food/static/food/js/food.js diff --git a/apps/food/static/food/js/food.js b/apps/food/static/food/js/food.js deleted file mode 100644 index 509d9b48..00000000 --- a/apps/food/static/food/js/food.js +++ /dev/null @@ -1,422 +0,0 @@ -var LOCK = false - -sources = [] -sources_notes_display = [] -dests = [] -dests_notes_display = [] - -function refreshHistory () { - $('#history').load('/note/transfer/ #history') -} - -function reset (refresh = true) { - sources_notes_display.length = 0 - sources.length = 0 - dests_notes_display.length = 0 - dests.length = 0 - $('#source_note_list').html('') - $('#dest_note_list').html('') - const source_field = $('#source_note') - source_field.val('') - const event = jQuery.Event('keyup') - event.originalEvent = { charCode: 97 } - source_field.trigger(event) - source_field.removeClass('is-invalid') - source_field.attr('data-original-title', '').tooltip('hide') - const dest_field = $('#dest_note') - dest_field.val('') - dest_field.trigger(event) - dest_field.removeClass('is-invalid') - dest_field.attr('data-original-title', '').tooltip('hide') - const amount_field = $('#amount') - amount_field.val('') - amount_field.removeClass('is-invalid') - $('#amount-required').html('') - const reason_field = $('#reason') - reason_field.val('') - reason_field.removeClass('is-invalid') - $('#reason-required').html('') - $('#last_name').val('') - $('#first_name').val('') - $('#bank').val('') - $('#user_note').val('') - $('#profile_pic').attr('src', '/static/member/img/default_picture.png') - $('#profile_pic_link').attr('href', '#') - if (refresh) { - refreshBalance() - refreshHistory() - } - - LOCK = false -} - -$(document).ready(function () { - /** - * If we are in credit/debit mode, check that only one note is entered. - * More over, get first name and last name to autocomplete fields. - */ - function checkUniqueNote () { - if ($('#type_credit').is(':checked') || $('#type_debit').is(':checked')) { - const arr = $('#type_credit').is(':checked') ? dests_notes_display : sources_notes_display - - if (arr.length === 0) { return } - - const last = arr[arr.length - 1] - arr.length = 0 - arr.push(last) - - last.quantity = 1 - - if (last.note.club) { - $('#last_name').val(last.note.name) - $('#first_name').val(last.note.name) - } - else if (!last.note.user) { - $.getJSON('/api/note/note/' + last.note.id + '/?format=json', function (note) { - last.note.user = note.user - $.getJSON('/api/user/' + last.note.user + '/', function (user) { - $('#last_name').val(user.last_name) - $('#first_name').val(user.first_name) - }) - }) - } else { - $.getJSON('/api/user/' + last.note.user + '/', function (user) { - $('#last_name').val(user.last_name) - $('#first_name').val(user.first_name) - }) - } - } - - return true - } - - autoCompleteNote('source_note', 'source_note_list', sources, sources_notes_display, - 'source_alias', 'source_note', 'user_note', 'profile_pic', checkUniqueNote) - autoCompleteNote('dest_note', 'dest_note_list', dests, dests_notes_display, - 'dest_alias', 'dest_note', 'user_note', 'profile_pic', checkUniqueNote) - - const source = $('#source_note') - const dest = $('#dest_note') - - $('#type_transfer').change(function () { - if (LOCK) { return } - - $('#source_me_div').removeClass('d-none') - $('#source_note').removeClass('is-invalid') - $('#dest_note').removeClass('is-invalid') - $('#special_transaction_div').addClass('d-none') - source.removeClass('d-none') - $('#source_note_list').removeClass('d-none') - $('#credit_type').addClass('d-none') - dest.removeClass('d-none') - $('#dest_note_list').removeClass('d-none') - $('#debit_type').addClass('d-none') - - $('#source_note_label').text(select_emitters_label) - $('#dest_note_label').text(select_receveirs_label) - - location.hash = 'transfer' - }) - - $('#type_credit').change(function () { - if (LOCK) { return } - - $('#source_me_div').addClass('d-none') - $('#source_note').removeClass('is-invalid') - $('#dest_note').removeClass('is-invalid') - $('#special_transaction_div').removeClass('d-none') - $('#source_note_list').addClass('d-none') - $('#dest_note_list').removeClass('d-none') - source.addClass('d-none') - source.tooltip('hide') - $('#credit_type').removeClass('d-none') - dest.removeClass('d-none') - dest.val('') - dest.tooltip('hide') - $('#debit_type').addClass('d-none') - - $('#source_note_label').text(transfer_type_label) - $('#dest_note_label').text(select_receveir_label) - - if (dests_notes_display.length > 1) { - $('#dest_note_list').html('') - dests_notes_display.length = 0 - } - - location.hash = 'credit' - }) - - $('#type_debit').change(function () { - if (LOCK) { return } - - $('#source_me_div').addClass('d-none') - $('#source_note').removeClass('is-invalid') - $('#dest_note').removeClass('is-invalid') - $('#special_transaction_div').removeClass('d-none') - $('#source_note_list').removeClass('d-none') - $('#dest_note_list').addClass('d-none') - source.removeClass('d-none') - source.val('') - source.tooltip('hide') - $('#credit_type').addClass('d-none') - dest.addClass('d-none') - dest.tooltip('hide') - $('#debit_type').removeClass('d-none') - - $('#source_note_label').text(select_emitter_label) - $('#dest_note_label').text(transfer_type_label) - - if (sources_notes_display.length > 1) { - $('#source_note_list').html('') - sources_notes_display.length = 0 - } - - location.hash = 'debit' - }) - - $('#credit_type').change(function () { - const type = $('#credit_type option:selected').text() - if ($('#type_credit').is(':checked')) { source.val(type) } else { dest.val(type) } - }) - - // Ensure we begin in transfer mode. Removing these lines may cause problems when reloading. - const type_transfer = $('#type_transfer') // Default mode - type_transfer.removeAttr('checked') - $('#type_credit').removeAttr('checked') - $('#type_debit').removeAttr('checked') - - if (location.hash) { $('#type_' + location.hash.substr(1)).click() } else { type_transfer.click() } - - $('#source_me').click(function () { - if (LOCK) { return } - - // Shortcut to set the current user as the only emitter - sources_notes_display.length = 0 - sources.length = 0 - $('#source_note_list').html('') - - const source_note = $('#source_note') - source_note.focus() - source_note.val('') - let event = jQuery.Event('keyup') - event.originalEvent = { charCode: 97 } - source_note.trigger(event) - source_note.val(username) - event = jQuery.Event('keyup') - event.originalEvent = { charCode: 97 } - source_note.trigger(event) - const fill_note = function () { - if (sources.length === 0) { - setTimeout(fill_note, 100) - return - } - event = jQuery.Event('keypress') - event.originalEvent = { charCode: 13 } - source_note.trigger(event) - - source_note.tooltip('hide') - source_note.val('') - $('#dest_note').focus() - } - fill_note() - }) -}) - -// Make transfer when pressing Enter on the amount section -$('#amount, #reason, #last_name, #first_name, #bank').keypress((event) => { - if (event.originalEvent.charCode === 13) { - $('#btn_transfer').click() - } -}) - -$('#btn_transfer').click(function () { - if (LOCK) { return } - - LOCK = true - - let error = false - - const amount_field = $('#amount') - amount_field.removeClass('is-invalid') - $('#amount-required').html('') - - const reason_field = $('#reason') - reason_field.removeClass('is-invalid') - $('#reason-required').html('') - - if (!amount_field.val() || isNaN(amount_field.val()) || amount_field.val() <= 0) { - amount_field.addClass('is-invalid') - $('#amount-required').html('' + gettext('This field is required and must contain a decimal positive number.') + '') - error = true - } - - const amount = Math.round(100 * amount_field.val()) - if (amount > 2147483647) { - amount_field.addClass('is-invalid') - $('#amount-required').html('' + gettext('The amount must stay under 21,474,836.47 €.') + '') - error = true - } - - if (!reason_field.val() && $('#type_transfer').is(':checked')) { - reason_field.addClass('is-invalid') - $('#reason-required').html('' + gettext('This field is required.') + '') - error = true - } - - if (!sources_notes_display.length && !$('#type_credit').is(':checked')) { - $('#source_note').addClass('is-invalid') - error = true - } - - if (!dests_notes_display.length && !$('#type_debit').is(':checked')) { - $('#dest_note').addClass('is-invalid') - error = true - } - - if (error) { - LOCK = false - return - } - - let reason = reason_field.val() - - if ($('#type_transfer').is(':checked')) { - // We copy the arrays to ensure that transactions are well-processed even if the form is reset - [...sources_notes_display].forEach(function (source) { - [...dests_notes_display].forEach(function (dest) { - if (source.note.id === dest.note.id) { - addMsg(interpolate(gettext('Warning: the transaction of %s from %s to %s was not made because ' + - 'it is the same source and destination note.'), [pretty_money(amount), source.name, dest.name]), 'warning', 10000) - LOCK = false - return - } - - $.post('/api/note/transaction/transaction/', - { - csrfmiddlewaretoken: CSRF_TOKEN, - quantity: source.quantity * dest.quantity, - amount: amount, - reason: reason, - valid: true, - polymorphic_ctype: TRANSFER_POLYMORPHIC_CTYPE, - resourcetype: 'Transaction', - source: source.note.id, - source_alias: source.name, - destination: dest.note.id, - destination_alias: dest.name - }).done(function () { - if (source.note.membership && source.note.membership.date_end < new Date().toISOString()) { - addMsg(interpolate(gettext('Warning, the emitter note %s is no more a BDE member.'), [source.name]), 'danger', 30000) - } - if (dest.note.membership && dest.note.membership.date_end < new Date().toISOString()) { - addMsg(interpolate(gettext('Warning, the destination note %s is no more a BDE member.'), [dest.name]), 'danger', 30000) - } - - if (!isNaN(source.note.balance)) { - const newBalance = source.note.balance - source.quantity * dest.quantity * amount - if (newBalance <= -2000) { - addMsg(interpolate(gettext('Warning, the transaction of %s from the note %s to the note %s succeed, but the emitter note %s is very negative.'), - [pretty_money(source.quantity * dest.quantity * amount), source.name, dest.name, source.name]), 'danger', 10000) - reset() - return - } else if (newBalance < 0) { - addMsg(interpolate(gettext('Warning, the transaction of %s from the note %s to the note %s succeed, but the emitter note %s is negative.'), - [pretty_money(source.quantity * dest.quantity * amount), source.name, dest.name, source.name]), 'danger', 10000) - reset() - return - } - } - addMsg(interpolate(gettext('Transfer of %s from %s to %s succeed!'), - [pretty_money(source.quantity * dest.quantity * amount), source.name, dest.name]), 'success', 10000) - - reset() - }).fail(function (err) { // do it again but valid = false - const errObj = JSON.parse(err.responseText) - if (errObj.non_field_errors) { - addMsg(interpolate(gettext('Transfer of %s from %s to %s failed: %s'), - [pretty_money(source.quantity * dest.quantity * amount), source.name, dest.name, errObj.non_field_errors]), 'danger') - LOCK = false - return - } - - $.post('/api/note/transaction/transaction/', - { - csrfmiddlewaretoken: CSRF_TOKEN, - quantity: source.quantity * dest.quantity, - amount: amount, - reason: reason, - valid: false, - invalidity_reason: 'Solde insuffisant', - polymorphic_ctype: TRANSFER_POLYMORPHIC_CTYPE, - resourcetype: 'Transaction', - source: source.note.id, - source_alias: source.name, - destination: dest.note.id, - destination_alias: dest.name - }).done(function () { - addMsg(interpolate(gettext('Transfer of %s from %s to %s failed: %s'), - [pretty_money(source.quantity * dest.quantity * amount), source.name, dest.name, gettext('insufficient funds')]), 'danger', 10000) - reset() - }).fail(function (err) { - const errObj = JSON.parse(err.responseText) - let error = errObj.detail ? errObj.detail : errObj.non_field_errors - if (!error) { error = err.responseText } - addMsg(interpolate(gettext('Transfer of %s from %s to %s failed: %s'), - [pretty_money(source.quantity * dest.quantity * amount), source.name, dest.name, error]), 'danger') - LOCK = false - }) - }) - }) - }) - } else if ($('#type_credit').is(':checked') || $('#type_debit').is(':checked')) { - let special_note - let user_note - let alias - const given_reason = reason - let source_id, dest_id - if ($('#type_credit').is(':checked')) { - special_note = $('#credit_type').val() - user_note = dests_notes_display[0].note - alias = dests_notes_display[0].name - source_id = special_note - dest_id = user_note.id - reason = 'Crédit ' + $('#credit_type option:selected').text().toLowerCase() - if (given_reason.length > 0) { reason += ' (' + given_reason + ')' } - } else { - special_note = $('#debit_type').val() - user_note = sources_notes_display[0].note - alias = sources_notes_display[0].name - source_id = user_note.id - dest_id = special_note - reason = 'Retrait ' + $('#debit_type option:selected').text().toLowerCase() - if (given_reason.length > 0) { reason += ' (' + given_reason + ')' } - } - $.post('/api/note/transaction/transaction/', - { - csrfmiddlewaretoken: CSRF_TOKEN, - quantity: 1, - amount: amount, - reason: reason, - valid: true, - polymorphic_ctype: SPECIAL_TRANSFER_POLYMORPHIC_CTYPE, - resourcetype: 'SpecialTransaction', - source: source_id, - source_alias: sources_notes_display.length ? alias : null, - destination: dest_id, - destination_alias: dests_notes_display.length ? alias : null, - last_name: $('#last_name').val(), - first_name: $('#first_name').val(), - bank: $('#bank').val() - }).done(function () { - addMsg(gettext('Credit/debit succeed!'), 'success', 10000) - if (user_note.membership && user_note.membership.date_end < new Date().toISOString()) { addMsg(gettext('Warning, the emitter note %s is no more a BDE member.'), 'danger', 10000) } - reset() - }).fail(function (err) { - const errObj = JSON.parse(err.responseText) - let error = errObj.detail ? errObj.detail : errObj.non_field_errors - if (!error) { error = err.responseText } - addMsg(interpolate(gettext('Credit/debit failed: %s'), [error]), 'danger', 10000) - LOCK = false - }) - } -}) diff --git a/apps/note/migrations/0002_create_special_notes.py b/apps/note/migrations/0002_create_special_notes.py index 12fa8583..07935d54 100644 --- a/apps/note/migrations/0002_create_special_notes.py +++ b/apps/note/migrations/0002_create_special_notes.py @@ -18,6 +18,7 @@ def create_special_notes(apps, schema_editor): class Migration(migrations.Migration): dependencies = [ ('note', '0001_initial'), + ('logs', '0001_initial'), ] operations = [ From 48462f2ffcf1820d797735ceba20a3fc7b2bed4a Mon Sep 17 00:00:00 2001 From: korenstin Date: Fri, 5 Jul 2024 11:57:44 +0200 Subject: [PATCH 14/29] Adding ingredients to a preparation --- apps/food/forms.py | 28 +- apps/food/migrations/0001_initial.py | 22 +- .../migrations/0002_auto_20240703_1549.py | 30 -- apps/food/models.py | 18 +- apps/food/tables.py | 19 ++ .../templates/food/add_ingredient_form.html | 21 ++ .../food/templates/food/basicfood_detail.html | 4 +- .../food/templates/food/create_food_form.html | 10 +- .../templates/food/create_qrcode_form.html | 22 +- apps/food/templates/food/qrcode_detail.html | 15 +- .../food/transformedfood_detail.html | 12 + .../templates/food/transformedfood_list.html | 20 ++ apps/food/urls.py | 4 +- apps/food/views.py | 274 +++++++++++------- note_kfet/settings/base.py | 2 +- note_kfet/templates/base.html | 4 + 16 files changed, 335 insertions(+), 170 deletions(-) delete mode 100644 apps/food/migrations/0002_auto_20240703_1549.py create mode 100644 apps/food/tables.py create mode 100644 apps/food/templates/food/add_ingredient_form.html create mode 100644 apps/food/templates/food/transformedfood_list.html diff --git a/apps/food/forms.py b/apps/food/forms.py index ad308727..34a4f5f0 100644 --- a/apps/food/forms.py +++ b/apps/food/forms.py @@ -11,7 +11,20 @@ from note_kfet.inputs import Autocomplete, DateTimePickerInput from note_kfet.middlewares import get_current_request from permission.backends import PermissionBackend -from .models import BasicFood, TransformedFood +from .models import BasicFood, QRCode, TransformedFood + + +class AddIngredientForms(forms.ModelForm): + """ + Form for add an ingredient + """ + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.fields['ingredient'].queryset = self.fields['ingredient'].queryset.filter(is_ready=False) + + class Meta: + model = TransformedFood + fields = ('ingredient',) class BasicFoodForms(forms.ModelForm): @@ -42,6 +55,19 @@ class BasicFoodForms(forms.ModelForm): } +class QRCodeForms(forms.ModelForm): + """ + Form for create QRCode + """ + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.fields['food_container'].queryset = self.fields['food_container'].queryset.filter(is_ready=False) + + class Meta: + model = QRCode + fields = ('food_container',) + + class TransformedFoodForms(forms.ModelForm): """ Form for add transformed food diff --git a/apps/food/migrations/0001_initial.py b/apps/food/migrations/0001_initial.py index 7d7d6745..011d0f3f 100644 --- a/apps/food/migrations/0001_initial.py +++ b/apps/food/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 2.2.28 on 2024-07-03 07:40 +# Generated by Django 2.2.28 on 2024-07-05 08:57 from django.db import migrations, models import django.db.models.deletion @@ -10,8 +10,8 @@ class Migration(migrations.Migration): initial = True dependencies = [ - ('member', '0011_profile_vss_charter_read'), ('contenttypes', '0002_remove_content_type_name'), + ('member', '0011_profile_vss_charter_read'), ] operations = [ @@ -19,7 +19,7 @@ class Migration(migrations.Migration): name='Allergen', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=255, null=True, verbose_name='name')), + ('name', models.CharField(max_length=255, verbose_name='name')), ], options={ 'verbose_name': 'Allergen', @@ -33,7 +33,7 @@ class Migration(migrations.Migration): ('name', models.CharField(max_length=255, verbose_name='name')), ('expiry_date', models.DateTimeField(verbose_name='expiry date')), ('was_eaten', models.BooleanField(default=False, verbose_name='was eaten')), - ('code', models.IntegerField(unique=True, verbose_name='code')), + ('is_ready', models.BooleanField(default=False, verbose_name='is ready')), ('allergens', models.ManyToManyField(blank=True, to='food.Allergen', verbose_name='allergen')), ('owner', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='+', to='member.Club', verbose_name='owner')), ('polymorphic_ctype', models.ForeignKey(editable=False, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='polymorphic_food.food_set+', to='contenttypes.ContentType')), @@ -47,7 +47,7 @@ class Migration(migrations.Migration): fields=[ ('food_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='food.Food')), ('date_type', models.CharField(choices=[('DLC', 'DLC'), ('DDM', 'DDM')], max_length=255)), - ('arrival_date', models.DateTimeField(blank=True, default=django.utils.timezone.now, verbose_name='arrival date')), + ('arrival_date', models.DateTimeField(default=django.utils.timezone.now, verbose_name='arrival date')), ], options={ 'verbose_name': 'Basic food', @@ -55,6 +55,18 @@ class Migration(migrations.Migration): }, bases=('food.food',), ), + migrations.CreateModel( + name='QRCode', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('qr_code_number', models.PositiveIntegerField(unique=True, verbose_name='QR-code number')), + ('food_container', models.OneToOneField(on_delete=django.db.models.deletion.PROTECT, related_name='QR_code', to='food.Food', verbose_name='food container')), + ], + options={ + 'verbose_name': 'QR-code', + 'verbose_name_plural': 'QR-codes', + }, + ), migrations.CreateModel( name='TransformedFood', fields=[ diff --git a/apps/food/migrations/0002_auto_20240703_1549.py b/apps/food/migrations/0002_auto_20240703_1549.py deleted file mode 100644 index fd3732c2..00000000 --- a/apps/food/migrations/0002_auto_20240703_1549.py +++ /dev/null @@ -1,30 +0,0 @@ -# Generated by Django 2.2.28 on 2024-07-03 13:49 - -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - dependencies = [ - ('food', '0001_initial'), - ] - - operations = [ - migrations.RemoveField( - model_name='food', - name='code', - ), - migrations.CreateModel( - name='QRCode', - fields=[ - ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('qr_code_number', models.PositiveIntegerField(verbose_name='QR-code number')), - ('food_container', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='QR_code', to='food.Food', unique=True, verbose_name='food container')), - ], - options={ - 'verbose_name': 'QR-code', - 'verbose_name_plural': 'QR-codes', - }, - ), - ] diff --git a/apps/food/models.py b/apps/food/models.py index 367e47f9..bc5103b5 100644 --- a/apps/food/models.py +++ b/apps/food/models.py @@ -7,13 +7,6 @@ from django.utils.translation import gettext_lazy as _ from member.models import Club from polymorphic.models import PolymorphicModel -################################################################# -# TO DO -# - link allergen with one food (basic or transformed) with check -# - check on basic food -# - check on transformed food -################################################################# - class QRCode(models.Model): """ @@ -21,13 +14,13 @@ class QRCode(models.Model): """ qr_code_number = models.PositiveIntegerField( verbose_name=_("QR-code number"), + unique=True, ) - food_container = models.ForeignKey( + food_container = models.OneToOneField( 'Food', on_delete=models.PROTECT, related_name='QR_code', - unique=True, verbose_name=_('food container'), ) @@ -77,6 +70,7 @@ class Food(PolymorphicModel): expiry_date = models.DateTimeField( verbose_name=_('expiry date'), + null=False, ) was_eaten = models.BooleanField( @@ -84,6 +78,11 @@ class Food(PolymorphicModel): verbose_name=_('was eaten'), ) + is_ready = models.BooleanField( + default=False, + verbose_name=_('is ready'), + ) + def __str__(self): return self.name @@ -111,7 +110,6 @@ class BasicFood(Food): arrival_date = models.DateTimeField( verbose_name=_('arrival date'), default=timezone.now, - blank=True, # TEMPORARY ) # label = models.ImageField( diff --git a/apps/food/tables.py b/apps/food/tables.py new file mode 100644 index 00000000..c824e42e --- /dev/null +++ b/apps/food/tables.py @@ -0,0 +1,19 @@ +# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# SPDX-License-Identifier: GPL-3.0-or-later + +import django_tables2 as tables +from django_tables2 import A + +from .models import TransformedFood + + +class TransformedFoodTable(tables.Table): + name = tables.LinkColumn( + 'food:food_view', + args=[A('pk'), ], + ) + + class Meta: + model = TransformedFood + template_name = 'django_tables2/bootstrap4.html' + fields = ('name', ) diff --git a/apps/food/templates/food/add_ingredient_form.html b/apps/food/templates/food/add_ingredient_form.html new file mode 100644 index 00000000..86e3b03e --- /dev/null +++ b/apps/food/templates/food/add_ingredient_form.html @@ -0,0 +1,21 @@ +{% extends "base.html" %} +{% comment %} +SPDX-License-Identifier: GPL-3.0-or-later +{% endcomment %} +{% load i18n crispy_forms_tags %} + +{% block content %} +
+

+ HTML not finished
+ {{ title }} +

+
+ + {% csrf_token %} + {{ form|crispy }} + + +
+
+{% endblock %} diff --git a/apps/food/templates/food/basicfood_detail.html b/apps/food/templates/food/basicfood_detail.html index 86b7ac9e..b0629f58 100644 --- a/apps/food/templates/food/basicfood_detail.html +++ b/apps/food/templates/food/basicfood_detail.html @@ -11,8 +11,8 @@ SPDX-License-Identifier: GPL-3.0-or-later {{ title }}
-

name : {{ food.name }}

- Update +

name : {{ food.name }}

+ Update
{% endblock %} diff --git a/apps/food/templates/food/create_food_form.html b/apps/food/templates/food/create_food_form.html index 137c1880..5728dd25 100644 --- a/apps/food/templates/food/create_food_form.html +++ b/apps/food/templates/food/create_food_form.html @@ -10,12 +10,12 @@ SPDX-License-Identifier: GPL-3.0-or-later {{ title }}
-
- +
+ +
{% endblock %} diff --git a/apps/food/templates/food/create_qrcode_form.html b/apps/food/templates/food/create_qrcode_form.html index 6af507ca..74f3ec29 100644 --- a/apps/food/templates/food/create_qrcode_form.html +++ b/apps/food/templates/food/create_qrcode_form.html @@ -2,6 +2,7 @@ {% comment %} SPDX-License-Identifier: GPL-3.0-or-later {% endcomment %} +{% load i18n crispy_forms_tags %} {% block content %}
@@ -9,13 +10,20 @@ SPDX-License-Identifier: GPL-3.0-or-later HTML not finished
{{ title }} -
-
- +
+
+ -
+
+
+
+
+ {% csrf_token %} + {{ form|crispy }} + +
+
{% endblock %} diff --git a/apps/food/templates/food/qrcode_detail.html b/apps/food/templates/food/qrcode_detail.html index 5e11ecf4..c74dd0e3 100644 --- a/apps/food/templates/food/qrcode_detail.html +++ b/apps/food/templates/food/qrcode_detail.html @@ -11,13 +11,14 @@ SPDX-License-Identifier: GPL-3.0-or-later {{ title }}
-

qrcode : {{ qrcode.qr_code_number }}

-

name : {{ qrcode.food_container.name }}

- {% if qrcode.food_container.polymorphic_ctype.name == 'Basic food' %} - Update - {% else %} - Update - {% endif %} +

qrcode : {{ qrcode.qr_code_number }}

+

name : {{ qrcode.food_container.name }}

+ {% if qrcode.food_container.polymorphic_ctype.name == 'Basic food' %} + Update + {% else %} + Update + {% endif %} + Add the ingrdient
{% endblock %} diff --git a/apps/food/templates/food/transformedfood_detail.html b/apps/food/templates/food/transformedfood_detail.html index d73b0d09..f2604c65 100644 --- a/apps/food/templates/food/transformedfood_detail.html +++ b/apps/food/templates/food/transformedfood_detail.html @@ -13,6 +13,18 @@ SPDX-License-Identifier: GPL-3.0-or-later

name : {{ food.name }}

owner : {{ food.owner }}

+

allergens :

+
    + {% for allergen in food.allergens.iterator %} +
  • {{ allergen.name }}
  • + {% endfor %} +
+

ingredients :

+ Update
diff --git a/apps/food/templates/food/transformedfood_list.html b/apps/food/templates/food/transformedfood_list.html new file mode 100644 index 00000000..a7b276fc --- /dev/null +++ b/apps/food/templates/food/transformedfood_list.html @@ -0,0 +1,20 @@ +{% extends "base.html" %} +{% comment %} +SPDX-License-Identifier: GPL-3.0-or-later +{% endcomment %} +{% load render_table from django_tables2 %} +{% load i18n %} + +{% block content %} +
+

+ Transformed food +

+ + {% render_table table %} +
+{% endblock %} diff --git a/apps/food/urls.py b/apps/food/urls.py index c422e514..f47f5fa8 100644 --- a/apps/food/urls.py +++ b/apps/food/urls.py @@ -8,6 +8,7 @@ from . import views app_name = 'food' urlpatterns = [ + path('', views.TransfomedListView.as_view(), name='food_list'), path('', views.QRCodeView.as_view(), name='qrcode_view'), path('detail/', views.FoodView.as_view(), name='food_view'), @@ -15,9 +16,10 @@ urlpatterns = [ path('create', views.FoodCreateView.as_view(), name='food_create'), path('/create_qrcode/basic', views.QRCodeBasicFoodCreateView.as_view(), name='qrcode_basic_create'), path('/create_qrcode/transformed', views.QRCodeTransformedFoodCreateView.as_view(), name='qrcode_transformed_create'), - path('create/basic', views.BasicFoodCreateView.as_view(), name='basic_create'), path('create/transformed', views.TransformedFoodCreateView.as_view(), name='transformed_create'), path('update/basic/', views.BasicFoodUpdateView.as_view(), name='basic_update'), path('update/transformed/', views.TransformedFoodUpdateView.as_view(), name='transformed_update'), + + path('add/', views.AddIngredientView.as_view(), name='add_ingredient'), ] diff --git a/apps/food/views.py b/apps/food/views.py index 5964f3f7..9e79689d 100644 --- a/apps/food/views.py +++ b/apps/food/views.py @@ -6,69 +6,62 @@ from datetime import timedelta from django.db import transaction from django.contrib.auth.mixins import LoginRequiredMixin from django.http import HttpResponseRedirect +from django_tables2.views import SingleTableView 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, TemplateView +from django.views.generic.edit import FormView from permission.views import ProtectQuerysetMixin, ProtectedCreateView -from .forms import BasicFoodForms, TransformedFoodForms +from .forms import AddIngredientForms, BasicFoodForms, QRCodeForms, TransformedFoodForms from .models import BasicFood, Food, QRCode, TransformedFood +from .tables import TransformedFoodTable -class QRCodeView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView): +class AddIngredientView(ProtectQuerysetMixin, FormView): """ - A view to add a basic food + A view to see a qrcode """ - model = QRCode - extra_context = {"title": _("Add a new meal")} - 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)) - - -class QRCodeCreateView(ProtectQuerysetMixin, LoginRequiredMixin, TemplateView): - """ - A view to add a basic food - """ - template_name = 'food/create_qrcode_form.html' - extra_context = {"title": _("Add a new aliment")} + 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["slug"] = kwargs["slug"] + context["pk"] = self.kwargs["pk"] return context + @transaction.atomic + def form_valid(self, form): + form.instance.creater = self.request.user + add_ingredient_form = AddIngredientForms(data=self.request.POST) + if not add_ingredient_form.is_valid(): + return self.form_invalid(form) -class FoodView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView): - """ - A view to add a basic food - """ - model = Food - extra_context = {"title": _("Add a new meal")} - context_object_name = "food" + food = Food.objects.get(pk=self.kwargs['pk']) + # Save the aliment and the allergens associed + 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() + + return super().form_valid(form) + + def get_success_url(self, **kwargs): + return reverse('food:food_list') + + def get_sample_object(self): + return TransformedFood( + name="", + creation_date=timezone.now(), + ) -class FoodCreateView(ProtectQuerysetMixin, LoginRequiredMixin, TemplateView): - """ - A view to add a basic food - """ - template_name = 'food/create_food_form.html' - extra_context = {"title": _("Add a new aliment")} - - -class BasicFoodFormView(ProtectQuerysetMixin): - ##################################################################### - # TO DO - # - fix picture save - # - implement solution crop and convert image (reuse or recode ImageForm from members apps) - ##################################################################### +class BasicFoodUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView): """ A view to add a basic food """ @@ -98,16 +91,21 @@ class BasicFoodFormView(ProtectQuerysetMixin): return reverse('food:food_view', kwargs={"pk": self.object.pk}) -class BasicFoodUpdateView(BasicFoodFormView, LoginRequiredMixin, UpdateView): - pass +class FoodCreateView(ProtectQuerysetMixin, LoginRequiredMixin, TemplateView): + """ + A view to add a new aliment + """ + template_name = 'food/create_food_form.html' + extra_context = {"title": _("Add a new aliment")} -class BasicFoodCreateView(BasicFoodFormView, ProtectedCreateView): - def get_sample_object(self): - return BasicFood( - name="", - expiry_date=timezone.now(), - ) +class FoodView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView): + """ + A view to see a food + """ + model = Food + extra_context = {"title": _("Details")} + context_object_name = "food" class QRCodeBasicFoodCreateView(ProtectQuerysetMixin, ProtectedCreateView): @@ -117,12 +115,12 @@ class QRCodeBasicFoodCreateView(ProtectQuerysetMixin, ProtectedCreateView): # - implement solution crop and convert image (reuse or recode ImageForm from members apps) ##################################################################### """ - A view to add a basic food + A view to add a basic food with a qrcode """ model = BasicFood form_class = BasicFoodForms template_name = 'food/basic_food_form.html' - extra_context = {"title": _("Add a new aliment")} + extra_context = {"title": _("Add a new basic food with QRCode")} @transaction.atomic def form_valid(self, form): @@ -135,6 +133,7 @@ class QRCodeBasicFoodCreateView(ProtectQuerysetMixin, ProtectedCreateView): 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 = True basic_food._force_save = True basic_food.save() basic_food.refresh_from_db() @@ -148,7 +147,7 @@ class QRCodeBasicFoodCreateView(ProtectQuerysetMixin, ProtectedCreateView): def get_success_url(self, **kwargs): self.object.refresh_from_db() - return reverse('food:food_view', kwargs={"pk": self.object.pk}) + return reverse('food:qrcode_view', kwargs={"slug": self.kwargs['slug']}) def get_sample_object(self): return BasicFood( @@ -157,12 +156,117 @@ class QRCodeBasicFoodCreateView(ProtectQuerysetMixin, ProtectedCreateView): ) +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)) + 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.is_ready = True + 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"], + ) + + +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 + """ + 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)) + + class TransformedFoodFormView(ProtectQuerysetMixin): - ##################################################################### - # TO DO - # - fix picture save - # - implement solution crop and convert image (reuse or recode ImageForm from members apps) - ##################################################################### """ A view to add a tranformed food """ @@ -204,48 +308,16 @@ class TransformedFoodCreateView(TransformedFoodFormView, ProtectedCreateView): ) -class QRCodeTransformedFoodCreateView(ProtectQuerysetMixin, ProtectedCreateView): - ##################################################################### - # TO DO - # - fix picture save - # - implement solution crop and convert image (reuse or recode ImageForm from members apps) - ##################################################################### +class TransfomedListView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableView): """ - A view to add a basic food + Displays all Activities, and classify if they are on-going or upcoming ones. """ model = TransformedFood - template_name = 'food/transformed_food_form.html' - form_class = TransformedFoodForms - extra_context = {"title": _("Add a new meal")} + table_class = TransformedFoodTable + ordering = ('-name',) + extra_context = {"title": _("Transformed food")} - @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._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:food_view', kwargs={"pk": self.object.pk}) - - def get_sample_object(self): - return BasicFood( - name="", - expiry_date=timezone.now(), - ) + def get_queryset(self, **kwargs): + return super().get_queryset(**kwargs)\ + .filter(is_ready=False)\ + .distinct() diff --git a/note_kfet/settings/base.py b/note_kfet/settings/base.py index 93ae0afd..91702559 100644 --- a/note_kfet/settings/base.py +++ b/note_kfet/settings/base.py @@ -69,6 +69,7 @@ INSTALLED_APPS = [ # Note apps 'api', 'activity', + 'food', 'logs', 'member', 'note', @@ -77,7 +78,6 @@ INSTALLED_APPS = [ 'scripts', 'treasury', 'wei', - 'food', ] MIDDLEWARE = [ diff --git a/note_kfet/templates/base.html b/note_kfet/templates/base.html index 63d0ddfe..6a7721b2 100644 --- a/note_kfet/templates/base.html +++ b/note_kfet/templates/base.html @@ -66,6 +66,10 @@ SPDX-License-Identifier: GPL-3.0-or-later {% trans 'Consumptions' %} {% endif %} + {% if user.is_authenticated and user|is_member:"Kfet" %}
diff --git a/apps/food/templates/food/create_qrcode_form.html b/apps/food/templates/food/create_qrcode_form.html index 74f3ec29..69909b7f 100644 --- a/apps/food/templates/food/create_qrcode_form.html +++ b/apps/food/templates/food/create_qrcode_form.html @@ -10,15 +10,10 @@ SPDX-License-Identifier: GPL-3.0-or-later HTML not finished
{{ title }} -
-
- -
-
+ + New basic food +
{% csrf_token %} {{ form|crispy }} diff --git a/apps/food/templates/food/transformedfood_detail.html b/apps/food/templates/food/transformedfood_detail.html index f2604c65..2ac87989 100644 --- a/apps/food/templates/food/transformedfood_detail.html +++ b/apps/food/templates/food/transformedfood_detail.html @@ -13,6 +13,8 @@ SPDX-License-Identifier: GPL-3.0-or-later

name : {{ food.name }}

owner : {{ food.owner }}

+

creation_date : {{ food.creation_date }}

+

expiry_date : {{ food.expiry_date }}

allergens :

    {% for allergen in food.allergens.iterator %} diff --git a/apps/food/urls.py b/apps/food/urls.py index f47f5fa8..76527033 100644 --- a/apps/food/urls.py +++ b/apps/food/urls.py @@ -15,7 +15,6 @@ urlpatterns = [ path('/create_qrcode', views.QRCodeCreateView.as_view(), name='qrcode_create'), path('create', views.FoodCreateView.as_view(), name='food_create'), path('/create_qrcode/basic', views.QRCodeBasicFoodCreateView.as_view(), name='qrcode_basic_create'), - path('/create_qrcode/transformed', views.QRCodeTransformedFoodCreateView.as_view(), name='qrcode_transformed_create'), path('create/transformed', views.TransformedFoodCreateView.as_view(), name='transformed_create'), path('update/basic/', views.BasicFoodUpdateView.as_view(), name='basic_update'), diff --git a/apps/food/views.py b/apps/food/views.py index 9e79689d..b6fb4bb5 100644 --- a/apps/food/views.py +++ b/apps/food/views.py @@ -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 From 50a680eed2d1b49118abea1b79b76de41cfd184a Mon Sep 17 00:00:00 2001 From: korenstin Date: Sun, 7 Jul 2024 21:25:26 +0200 Subject: [PATCH 16/29] Open table and shelf life --- apps/food/forms.py | 2 +- .../0002_transformedfood_shelf_life.py | 19 +++++++++++++++ apps/food/models.py | 8 ++++++- .../templates/food/transformedfood_list.html | 10 +++++--- apps/food/views.py | 23 +++++++++++++------ 5 files changed, 50 insertions(+), 12 deletions(-) create mode 100644 apps/food/migrations/0002_transformedfood_shelf_life.py diff --git a/apps/food/forms.py b/apps/food/forms.py index fe0775a0..59226a52 100644 --- a/apps/food/forms.py +++ b/apps/food/forms.py @@ -89,7 +89,7 @@ class TransformedFoodForms(forms.ModelForm): class Meta: model = TransformedFood - fields = ('name', 'creation_date', 'owner', 'is_active') + fields = ('name', 'creation_date', 'owner', 'is_active', 'shelf_life') widgets = { "owner": Autocomplete( model=Club, diff --git a/apps/food/migrations/0002_transformedfood_shelf_life.py b/apps/food/migrations/0002_transformedfood_shelf_life.py new file mode 100644 index 00000000..46673643 --- /dev/null +++ b/apps/food/migrations/0002_transformedfood_shelf_life.py @@ -0,0 +1,19 @@ +# Generated by Django 2.2.28 on 2024-07-06 20:37 + +import datetime +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('food', '0001_initial'), + ] + + operations = [ + migrations.AddField( + model_name='transformedfood', + name='shelf_life', + field=models.DurationField(default=datetime.timedelta(days=3), verbose_name='shelf life'), + ), + ] diff --git a/apps/food/models.py b/apps/food/models.py index e68a5c65..50db24f3 100644 --- a/apps/food/models.py +++ b/apps/food/models.py @@ -165,6 +165,12 @@ class TransformedFood(Food): verbose_name=_('is active'), ) + # Without microbiological analyzes, the storage time is 3 days + shelf_life = models.DurationField( + verbose_name=_("shelf life"), + default=timedelta(days=3), + ) + @transaction.atomic def update_allergens(self): # When allergens are changed, simply update the parents' allergens @@ -185,7 +191,7 @@ class TransformedFood(Food): 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) + 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) diff --git a/apps/food/templates/food/transformedfood_list.html b/apps/food/templates/food/transformedfood_list.html index a7b276fc..98d89aa4 100644 --- a/apps/food/templates/food/transformedfood_list.html +++ b/apps/food/templates/food/transformedfood_list.html @@ -7,14 +7,18 @@ SPDX-License-Identifier: GPL-3.0-or-later {% block content %}
    -

    - Transformed food -

    +

    + In preparation +

    {% render_table table %} +

    + Open +

    + {% render_table open_table %}
    {% endblock %} diff --git a/apps/food/views.py b/apps/food/views.py index b6fb4bb5..ec7b7429 100644 --- a/apps/food/views.py +++ b/apps/food/views.py @@ -1,8 +1,6 @@ # 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 transaction from django.contrib.auth.mixins import LoginRequiredMixin from django.http import HttpResponseRedirect @@ -234,12 +232,13 @@ class TransformedFoodFormView(ProtectQuerysetMixin): # 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.expiry_date = transformed_food.creation_date transformed_food._force_save = True transformed_food.save() transformed_food.refresh_from_db() - return super().form_valid(form) + ans = super().form_valid(form) + transformed_food.update() + return ans def get_success_url(self, **kwargs): self.object.refresh_from_db() @@ -260,14 +259,24 @@ class TransformedFoodCreateView(TransformedFoodFormView, ProtectedCreateView): class TransfomedListView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableView): """ - Displays all Activities, and classify if they are on-going or upcoming ones. + Displays not ready TransformedFood """ model = TransformedFood table_class = TransformedFoodTable - ordering = ('-name',) + ordering = ('name',) extra_context = {"title": _("Transformed food")} def get_queryset(self, **kwargs): return super().get_queryset(**kwargs)\ .filter(is_ready=False)\ .distinct() + + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + context['open_table'] = TransformedFoodTable( + TransformedFood.objects.filter( + was_eaten=False, + expiry_date__lt=timezone.now() + ), + prefix="open-") + return context From dcfd0167e7b9b6505e8c13442cfff89f33113e07 Mon Sep 17 00:00:00 2001 From: korenstin Date: Mon, 8 Jul 2024 17:44:09 +0200 Subject: [PATCH 17/29] Security against the cycles --- apps/food/admin.py | 7 +++---- apps/food/views.py | 23 ++++++++++------------- 2 files changed, 13 insertions(+), 17 deletions(-) diff --git a/apps/food/admin.py b/apps/food/admin.py index 23384316..fa32755a 100644 --- a/apps/food/admin.py +++ b/apps/food/admin.py @@ -27,10 +27,9 @@ class TransformedFoodAdmin(admin.ModelAdmin): exclude = ["allergens", "expiry_date"] @transaction.atomic - def save_related(self, *args, **kwargs): - ans = super().save_related(*args, **kwargs) - args[1].instance.update() - return ans + def save_related(self, request, form, *args, **kwargs): + super().save_related(request, form, *args, **kwargs) + form.instance.update() @admin.register(Allergen, site=admin_site) diff --git a/apps/food/views.py b/apps/food/views.py index ec7b7429..13ecfd20 100644 --- a/apps/food/views.py +++ b/apps/food/views.py @@ -17,9 +17,9 @@ from .models import BasicFood, Food, QRCode, TransformedFood from .tables import TransformedFoodTable -class AddIngredientView(ProtectQuerysetMixin, FormView): +class AddIngredientView(ProtectQuerysetMixin, UpdateView): """ - A view to see a qrcode + A view to add an ingredient """ model = Food template_name = 'food/add_ingredient_form.html' @@ -34,28 +34,25 @@ class AddIngredientView(ProtectQuerysetMixin, FormView): @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 not food.is_ready: + form.add_error(None, _("The product isn't ready")) + return self.form_invalid(form) if not add_ingredient_form.is_valid(): return self.form_invalid(form) - food = Food.objects.get(pk=self.kwargs['pk']) # Save the aliment and the allergens associed for transformed_pk in self.request.POST.getlist('ingredient'): transformed = TransformedFood.objects.get(pk=transformed_pk) - transformed.ingredient.add(food) - transformed.update() - - return super().form_valid(form) + if not transformed.is_ready: + transformed.ingredient.add(food) + transformed.update() + return HttpResponseRedirect(self.get_success_url()) def get_success_url(self, **kwargs): return reverse('food:food_list') - def get_sample_object(self): - return TransformedFood( - name="", - creation_date=timezone.now(), - ) - class BasicFoodUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView): """ From 4b97ab2e2a888787cffac9a28d75eba4d0b91a5d Mon Sep 17 00:00:00 2001 From: korenstin Date: Thu, 11 Jul 2024 13:38:22 +0200 Subject: [PATCH 18/29] linters --- apps/food/views.py | 1 - 1 file changed, 1 deletion(-) diff --git a/apps/food/views.py b/apps/food/views.py index 13ecfd20..15050d9e 100644 --- a/apps/food/views.py +++ b/apps/food/views.py @@ -9,7 +9,6 @@ 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, TemplateView -from django.views.generic.edit import FormView from permission.views import ProtectQuerysetMixin, ProtectedCreateView from .forms import AddIngredientForms, BasicFoodForms, QRCodeForms, TransformedFoodForms From 6f67d2c629cc3493224007d6fb2d05b31f30eacb Mon Sep 17 00:00:00 2001 From: korenstin Date: Mon, 22 Jul 2024 15:49:32 +0200 Subject: [PATCH 19/29] Documentation --- docs/apps/food.rst | 83 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) create mode 100644 docs/apps/food.rst diff --git a/docs/apps/food.rst b/docs/apps/food.rst new file mode 100644 index 00000000..e34ad40f --- /dev/null +++ b/docs/apps/food.rst @@ -0,0 +1,83 @@ +Application Food +================ + +L'application ``food`` s'occupe de la traçabilité et permet notamment l'obtention de la liste des allergènes. + +Modèles +------- + +L'application comporte 5 modèles : Allergen, QRCode, Food, BasicFood, TransformedFood. + +Food +~~~~ + +Ce modèle est un PolymorphicModel et ne sert uniquement à créer BasicFood et TransformedFood. + +Le modèle regroupe : + +* Nom du produit +* Propriétaire (doit-être un Club) +* Allergènes (ManyToManyField) +* date d'expiration +* a été mangé (booléen) +* est prêt (booléen) + +BasicFood +~~~~~~~~~ + +Les BasicFood correspondent aux produits non modifiés à la Kfet. Ils peuvent correspondre à la fois à des produits achetés en magasin ou à des produits Terre à Terre. Ces produits seront les ingrédients de tous les plats préparés et en conséquent sont les seuls produits à nécessité une saisie manuelle des allergènes. + +Le modèle regroupe : + +* Type de date (DLC = date limite de consommation, DDM = date de durabilité minimale) +* Date d'arrivée +* Champs de Food + +TransformedFood +~~~~~~~~~~~~~~~ + +Les TransformedFood correspondent aux produits préparés à la Kfet. Ils peuvent être composés de BasicFood et/ou de TransformedFood. La date d'expiration et les allergènes sont automatiquement mis à jour par update (qui doit être exécuté après modification des ingrédients dans les forms par exemple). + +Le modèle regroupe : + +* Durée de consommation (par défaut 3 jours) +* Ingrédients (ManyToManyField vers Food) +* Date de création +* Champs de Food + +Allergen +~~~~~~~~ + +Le modèle regroupe : + +* Nom + +QRCode +~~~~~~ + +Le modèle regroupe : + +* nombre (unique, entier positif) +* food (OneToOneField vers Food) + +Création de BasicFood +~~~~~~~~~~~~~~~~~~~~~ + +Un BasicFood a toujours besoin d'un QRCode (depuis l'interface web). Il convient donc de coller le QRCode puis de le scanner et de compléter le formulaire. + +Création de TransformedFood +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Pour créer un TransformedFood, il suffit d'aller dans l'onglet ``traçabilité`` et de cliquer sur l'onglet. + +Ajouter un ingrédient +~~~~~~~~~~~~~~~~~~~~~ + +Un ingrédient a forcément un QRCode. Il convient donc de scanner le QRCode de l'ingrédient et de sélectionner le produit auquel il doit être ajouté. + +Remarque : Un produit fini doit avoir un QRCode et inversement. + +Terminer un plat +~~~~~~~~~~~~~~~~ + +Il suffit de coller le QRCode sur le plat, de le scanner et de sélectionner le produit. From a7e87ea639b7bb398409c984376b4b5d1cd7e00f Mon Sep 17 00:00:00 2001 From: korenstin Date: Sun, 4 Aug 2024 23:38:21 +0200 Subject: [PATCH 20/29] API Food --- apps/api/urls.py | 26 ++++++++------- apps/food/api/__init__.py | 0 apps/food/api/serializers.py | 50 +++++++++++++++++++++++++++++ apps/food/api/urls.py | 14 +++++++++ apps/food/api/views.py | 61 ++++++++++++++++++++++++++++++++++++ 5 files changed, 140 insertions(+), 11 deletions(-) create mode 100644 apps/food/api/__init__.py create mode 100644 apps/food/api/serializers.py create mode 100644 apps/food/api/urls.py create mode 100644 apps/food/api/views.py diff --git a/apps/api/urls.py b/apps/api/urls.py index 7c73093b..6a09411f 100644 --- a/apps/api/urls.py +++ b/apps/api/urls.py @@ -14,29 +14,33 @@ router = routers.DefaultRouter() router.register('models', ContentTypeViewSet) router.register('user', UserViewSet) +if "activity" in settings.INSTALLED_APPS: + from activity.api.urls import register_activity_urls + register_activity_urls(router, 'activity') + +if "food" in settings.INSTALLED_APPS: + from food.api.urls import register_food_urls + register_food_urls(router, 'food') + +if "logs" in settings.INSTALLED_APPS: + from logs.api.urls import register_logs_urls + register_logs_urls(router, 'logs') + if "member" in settings.INSTALLED_APPS: from member.api.urls import register_members_urls register_members_urls(router, 'members') -if "member" in settings.INSTALLED_APPS: - from activity.api.urls import register_activity_urls - register_activity_urls(router, 'activity') - if "note" in settings.INSTALLED_APPS: from note.api.urls import register_note_urls register_note_urls(router, 'note') -if "treasury" in settings.INSTALLED_APPS: - from treasury.api.urls import register_treasury_urls - register_treasury_urls(router, 'treasury') - if "permission" in settings.INSTALLED_APPS: from permission.api.urls import register_permission_urls register_permission_urls(router, 'permission') -if "logs" in settings.INSTALLED_APPS: - from logs.api.urls import register_logs_urls - register_logs_urls(router, 'logs') +if "treasury" in settings.INSTALLED_APPS: + from treasury.api.urls import register_treasury_urls + register_treasury_urls(router, 'treasury') if "wei" in settings.INSTALLED_APPS: from wei.api.urls import register_wei_urls diff --git a/apps/food/api/__init__.py b/apps/food/api/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/apps/food/api/serializers.py b/apps/food/api/serializers.py new file mode 100644 index 00000000..9c42013f --- /dev/null +++ b/apps/food/api/serializers.py @@ -0,0 +1,50 @@ +# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# SPDX-License-Identifier: GPL-3.0-or-later + +from rest_framework import serializers + +from ..models import Allergen, BasicFood, QRCode, TransformedFood + + +class AllergenSerializer(serializers.ModelSerializer): + """ + REST API Serializer for Allergen. + The djangorestframework plugin will analyse the model `Allergen` and parse all fields in the API. + """ + + class Meta: + model = Allergen + fields = '__all__' + + +class BasicFoodSerializer(serializers.ModelSerializer): + """ + REST API Serializer for BasicFood. + The djangorestframework plugin will analyse the model `BasicFood` and parse all fields in the API. + """ + + class Meta: + model = BasicFood + fields = '__all__' + + +class QRCodeSerializer(serializers.ModelSerializer): + """ + REST API Serializer for QRCode. + The djangorestframework plugin will analyse the model `QRCode` and parse all fields in the API. + """ + + class Meta: + model = QRCode + fields = '__all__' + + +class TransformedFoodSerializer(serializers.ModelSerializer): + """ + REST API Serializer for TransformedFood. + The djangorestframework plugin will analyse the model `TransformedFood` and parse all fields in the API. + """ + + class Meta: + model = TransformedFood + fields = '__all__' diff --git a/apps/food/api/urls.py b/apps/food/api/urls.py new file mode 100644 index 00000000..acfb635d --- /dev/null +++ b/apps/food/api/urls.py @@ -0,0 +1,14 @@ +# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# SPDX-License-Identifier: GPL-3.0-or-later + +from .views import AllergenViewSet, BasicFoodViewSet, QRCodeViewSet, TransformedFoodViewSet + + +def register_food_urls(router, path): + """ + Configure router for Food REST API. + """ + router.register(path + '/allergen', AllergenViewSet) + router.register(path + '/basic_food', BasicFoodViewSet) + router.register(path + '/qrcode', QRCodeViewSet) + router.register(path + '/transformed_food', TransformedFoodViewSet) diff --git a/apps/food/api/views.py b/apps/food/api/views.py new file mode 100644 index 00000000..824ff809 --- /dev/null +++ b/apps/food/api/views.py @@ -0,0 +1,61 @@ +# Copyright (C) 2018-2024 by BDE ENS Paris-Saclay +# SPDX-License-Identifier: GPL-3.0-or-later + +from api.viewsets import ReadProtectedModelViewSet +from django_filters.rest_framework import DjangoFilterBackend +from rest_framework.filters import SearchFilter + +from .serializers import AllergenSerializer, BasicFoodSerializer, QRCodeSerializer, TransformedFoodSerializer +from ..models import Allergen, BasicFood, QRCode, TransformedFood + + +class AllergenViewSet(ReadProtectedModelViewSet): + """ + REST API View set. + The djangorestframework plugin will get all `Allergen` objects, serialize it to JSON with the given serializer, + then render it on /api/food/allergen/ + """ + queryset = Allergen.objects.order_by('id') + serializer_class = AllergenSerializer + filter_backends = [DjangoFilterBackend, SearchFilter] + filterset_fields = ['name', ] + search_fields = ['$name', ] + + +class BasicFoodViewSet(ReadProtectedModelViewSet): + """ + REST API View set. + The djangorestframework plugin will get all `BasicFood` objects, serialize it to JSON with the given serializer, + then render it on /api/food/basic_food/ + """ + queryset = BasicFood.objects.order_by('id') + serializer_class = BasicFoodSerializer + filter_backends = [DjangoFilterBackend, SearchFilter] + filterset_fields = ['name', ] + search_fields = ['$name', ] + + +class QRCodeViewSet(ReadProtectedModelViewSet): + """ + REST API View set. + The djangorestframework plugin will get all `QRCode` objects, serialize it to JSON with the given serializer, + then render it on /api/food/qrcode/ + """ + queryset = QRCode.objects.order_by('id') + serializer_class = QRCodeSerializer + filter_backends = [DjangoFilterBackend, SearchFilter] + filterset_fields = ['qr_code_number', ] + search_fields = ['$qr_code_number', ] + + +class TransformedFoodViewSet(ReadProtectedModelViewSet): + """ + REST API View set. + The djangorestframework plugin will get all `TransformedFood` objects, serialize it to JSON with the given serializer, + then render it on /api/food/transformed_food/ + """ + queryset = TransformedFood.objects.order_by('id') + serializer_class = TransformedFoodSerializer + filter_backends = [DjangoFilterBackend, SearchFilter] + filterset_fields = ['name', ] + search_fields = ['$name', ] From 1c5ed2bd3f47126ff0a604232a9e520c80c6dbf6 Mon Sep 17 00:00:00 2001 From: quark Date: Tue, 6 Aug 2024 13:59:30 +0200 Subject: [PATCH 21/29] Edit base.html and few translations --- locale/de/LC_MESSAGES/django.po | 433 ++++++++++++++++++++++---------- locale/es/LC_MESSAGES/django.po | 433 ++++++++++++++++++++++---------- locale/fr/LC_MESSAGES/django.po | 422 +++++++++++++++++++++---------- note_kfet/templates/base.html | 12 +- 4 files changed, 898 insertions(+), 402 deletions(-) diff --git a/locale/de/LC_MESSAGES/django.po b/locale/de/LC_MESSAGES/django.po index 9ce95379..538aff43 100644 --- a/locale/de/LC_MESSAGES/django.po +++ b/locale/de/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-03-22 00:33+0100\n" +"POT-Creation-Date: 2024-08-06 13:39+0200\n" "PO-Revision-Date: 2020-11-16 20:02+0000\n" "Last-Translator: bleizi \n" "Language-Team: German \n" @@ -18,8 +18,8 @@ msgstr "" "Plural-Forms: nplurals=2; plural=n != 1;\n" "X-Generator: Weblate 4.3.2\n" -#: apps/activity/apps.py:10 apps/activity/models.py:127 -#: apps/activity/models.py:167 +#: apps/activity/apps.py:10 apps/activity/models.py:129 +#: apps/activity/models.py:169 msgid "activity" msgstr "Veranstaltung" @@ -27,33 +27,33 @@ msgstr "Veranstaltung" msgid "The note of this club is inactive." msgstr "" -#: apps/activity/forms.py:41 apps/activity/models.py:140 +#: apps/activity/forms.py:41 apps/activity/models.py:142 msgid "The end date must be after the start date." msgstr "Das Abschlussdatum muss nach das Anfangsdatum sein." -#: apps/activity/forms.py:82 apps/activity/models.py:269 +#: apps/activity/forms.py:82 apps/activity/models.py:271 msgid "You can't invite someone once the activity is started." msgstr "" "Sie dürfen nicht jemandem einladen wenn die Veranstaltung angefangen hat." -#: apps/activity/forms.py:85 apps/activity/models.py:272 +#: apps/activity/forms.py:85 apps/activity/models.py:274 msgid "This activity is not validated yet." msgstr "Diese Veranstaltung ist noch nicht bestätigt." -#: apps/activity/forms.py:95 apps/activity/models.py:280 +#: apps/activity/forms.py:95 apps/activity/models.py:282 msgid "This person has been already invited 5 times this year." msgstr "Diese Person wurde schon 5 mal dieses Jahr eingeladen." -#: apps/activity/forms.py:99 apps/activity/models.py:284 +#: apps/activity/forms.py:99 apps/activity/models.py:286 msgid "This person is already invited." msgstr "Diese Person wurde schon eingeladen." -#: apps/activity/forms.py:103 apps/activity/models.py:288 +#: apps/activity/forms.py:103 apps/activity/models.py:290 msgid "You can't invite more than 3 people to this activity." msgstr "Sie dürfen höchstens 3 Leute zu dieser Veranstaltung einladen." -#: apps/activity/models.py:28 apps/activity/models.py:63 -#: apps/member/models.py:203 +#: apps/activity/models.py:28 apps/activity/models.py:63 apps/food/models.py:42 +#: apps/food/models.py:56 apps/member/models.py:203 #: apps/member/templates/member/includes/club_info.html:4 #: apps/member/templates/member/includes/profile_info.html:4 #: apps/note/models/notes.py:263 apps/note/models/transactions.py:26 @@ -99,121 +99,121 @@ msgstr "Vearnstaltungarte" msgid "description" msgstr "Beschreibung" -#: apps/activity/models.py:72 +#: apps/activity/models.py:74 msgid "location" msgstr "Ort" -#: apps/activity/models.py:76 +#: apps/activity/models.py:78 msgid "Place where the activity is organized, eg. Kfet." msgstr "Wo findet die Veranstaltung statt ? (z.B Kfet)." -#: apps/activity/models.py:83 +#: apps/activity/models.py:85 #: apps/activity/templates/activity/includes/activity_info.html:22 #: apps/note/models/notes.py:207 apps/note/models/transactions.py:67 #: apps/permission/models.py:163 msgid "type" msgstr "Type" -#: apps/activity/models.py:89 apps/logs/models.py:22 apps/member/models.py:313 +#: apps/activity/models.py:91 apps/logs/models.py:22 apps/member/models.py:318 #: apps/note/models/notes.py:148 apps/treasury/models.py:293 #: apps/wei/models.py:171 apps/wei/templates/wei/attribute_bus_1A.html:13 #: apps/wei/templates/wei/survey.html:15 msgid "user" msgstr "User" -#: apps/activity/models.py:96 +#: apps/activity/models.py:98 #: apps/activity/templates/activity/includes/activity_info.html:36 msgid "organizer" msgstr "Veranstalter" -#: apps/activity/models.py:97 +#: apps/activity/models.py:99 msgid "Club that organizes the activity. The entry fees will go to this club." msgstr "" "Die Veranstaltung wurde von einem Club organisert. Die Eintrittsbeiträge " "gehen an diesen Club." -#: apps/activity/models.py:104 +#: apps/activity/models.py:106 #: apps/activity/templates/activity/includes/activity_info.html:39 msgid "attendees club" msgstr "Teilnehmer" -#: apps/activity/models.py:105 +#: apps/activity/models.py:107 msgid "Club that is authorized to join the activity. Mostly the Kfet club." msgstr "Club die an die Veranstaltung teilnehmen können." -#: apps/activity/models.py:109 +#: apps/activity/models.py:111 #: apps/activity/templates/activity/includes/activity_info.html:25 msgid "start date" msgstr "Anfangsdatum" -#: apps/activity/models.py:113 +#: apps/activity/models.py:115 #: apps/activity/templates/activity/includes/activity_info.html:28 msgid "end date" msgstr "Abschlussdatum" -#: apps/activity/models.py:118 +#: apps/activity/models.py:120 #: apps/activity/templates/activity/includes/activity_info.html:50 #: apps/note/models/transactions.py:149 msgid "valid" msgstr "gültig" -#: apps/activity/models.py:123 +#: apps/activity/models.py:125 #: apps/activity/templates/activity/includes/activity_info.html:65 msgid "open" msgstr "geöffnet" -#: apps/activity/models.py:128 +#: apps/activity/models.py:130 msgid "activities" msgstr "Veranstaltungen" -#: apps/activity/models.py:172 +#: apps/activity/models.py:174 msgid "entry time" msgstr "Eintrittzeit" -#: apps/activity/models.py:178 apps/note/apps.py:14 +#: apps/activity/models.py:180 apps/note/apps.py:14 #: apps/note/models/notes.py:77 msgid "note" msgstr "Note" -#: apps/activity/models.py:189 +#: apps/activity/models.py:191 #: apps/activity/templates/activity/activity_entry.html:46 msgid "entry" msgstr "Eintritt" -#: apps/activity/models.py:190 +#: apps/activity/models.py:192 #: apps/activity/templates/activity/activity_entry.html:46 msgid "entries" msgstr "Eintritte" -#: apps/activity/models.py:193 +#: apps/activity/models.py:195 #, python-brace-format msgid "Entry for {guest}, invited by {note} to the activity {activity}" msgstr "Eintritt für {guest}, von {note} zur Vanstaltung {activity} eingeladen" -#: apps/activity/models.py:195 +#: apps/activity/models.py:197 #, python-brace-format msgid "Entry for {note} to the activity {activity}" msgstr "Eintritt von {note} zur Veranstaltung {activity}" -#: apps/activity/models.py:202 +#: apps/activity/models.py:204 msgid "Already entered on " msgstr "Schon eingetretten " -#: apps/activity/models.py:202 apps/activity/tables.py:56 +#: apps/activity/models.py:204 apps/activity/tables.py:56 msgid "{:%Y-%m-%d %H:%M:%S}" msgstr "{:%Y-%m-%d %H:%M:%S}" -#: apps/activity/models.py:210 +#: apps/activity/models.py:212 msgid "The balance is negative." msgstr "Kontostand ist im Rot." -#: apps/activity/models.py:240 +#: apps/activity/models.py:242 #: apps/treasury/templates/treasury/sogecredit_detail.html:14 #: apps/wei/templates/wei/attribute_bus_1A.html:16 msgid "last name" msgstr "Nachname" -#: apps/activity/models.py:245 +#: apps/activity/models.py:247 #: apps/member/templates/member/includes/profile_info.html:4 #: apps/registration/templates/registration/future_profile_detail.html:16 #: apps/treasury/templates/treasury/sogecredit_detail.html:17 @@ -222,19 +222,19 @@ msgstr "Nachname" msgid "first name" msgstr "Vorname" -#: apps/activity/models.py:252 +#: apps/activity/models.py:254 msgid "inviter" msgstr "Einlader" -#: apps/activity/models.py:256 +#: apps/activity/models.py:258 msgid "guest" msgstr "Gast" -#: apps/activity/models.py:257 +#: apps/activity/models.py:259 msgid "guests" msgstr "Gäste" -#: apps/activity/models.py:310 +#: apps/activity/models.py:312 msgid "Invitation" msgstr "Einladung" @@ -246,7 +246,9 @@ msgstr "Die Veranstaltung ist geöffnet." msgid "The validation of the activity is pending." msgstr "Diese Veranstaltung ist noch nicht bestätigt." -#: apps/activity/tables.py:43 apps/treasury/tables.py:107 +#: apps/activity/tables.py:43 +#: apps/member/templates/member/picture_update.html:18 +#: apps/treasury/tables.py:107 msgid "Remove" msgstr "Entfernen" @@ -262,15 +264,15 @@ msgstr "entfernen" msgid "Type" msgstr "Type" -#: apps/activity/tables.py:84 apps/member/forms.py:193 -#: apps/registration/forms.py:92 apps/treasury/forms.py:131 +#: apps/activity/tables.py:84 apps/member/forms.py:196 +#: apps/registration/forms.py:91 apps/treasury/forms.py:131 #: apps/wei/forms/registration.py:104 msgid "Last name" msgstr "Nachname" -#: apps/activity/tables.py:86 apps/member/forms.py:198 +#: apps/activity/tables.py:86 apps/member/forms.py:201 #: apps/note/templates/note/transaction_form.html:138 -#: apps/registration/forms.py:97 apps/treasury/forms.py:133 +#: apps/registration/forms.py:96 apps/treasury/forms.py:133 #: apps/wei/forms/registration.py:109 msgid "First name" msgstr "Vorname" @@ -297,7 +299,7 @@ msgstr "Gastliste" #: apps/note/models/transactions.py:261 #: apps/note/templates/note/transaction_form.html:17 #: apps/note/templates/note/transaction_form.html:152 -#: note_kfet/templates/base.html:72 +#: note_kfet/templates/base.html:78 msgid "Transfer" msgstr "Überweisen" @@ -334,6 +336,10 @@ msgid "Entry done!" msgstr "Eintrittseite" #: apps/activity/templates/activity/activity_form.html:16 +#: apps/food/templates/food/add_ingredient_form.html:17 +#: apps/food/templates/food/basic_food_form.html:17 +#: apps/food/templates/food/create_qrcode_form.html:20 +#: apps/food/templates/food/transformed_food_form.html:17 #: apps/member/templates/member/add_members.html:46 #: apps/member/templates/member/club_form.html:16 #: apps/note/templates/note/transactiontemplate_form.html:18 @@ -399,39 +405,39 @@ msgstr "bearbeiten" msgid "Invite" msgstr "Einladen" -#: apps/activity/views.py:36 +#: apps/activity/views.py:37 msgid "Create new activity" msgstr "Neue Veranstaltung schaffen" -#: apps/activity/views.py:67 note_kfet/templates/base.html:90 +#: apps/activity/views.py:67 note_kfet/templates/base.html:96 msgid "Activities" msgstr "Veranstaltungen" -#: apps/activity/views.py:93 +#: apps/activity/views.py:108 msgid "Activity detail" msgstr "Veranstaltunginfo" -#: apps/activity/views.py:113 +#: apps/activity/views.py:128 msgid "Update activity" msgstr "Veranstaltung bearbeiten" -#: apps/activity/views.py:140 +#: apps/activity/views.py:155 msgid "Invite guest to the activity \"{}\"" msgstr "Gast zur Veranstaltung \"{}\" einladen" -#: apps/activity/views.py:178 +#: apps/activity/views.py:193 msgid "You are not allowed to display the entry interface for this activity." msgstr "Sie haben nicht das Recht diese Seite zu benuzten." -#: apps/activity/views.py:181 +#: apps/activity/views.py:196 msgid "This activity does not support activity entries." msgstr "Diese Veranstaltung braucht nicht Eintritt." -#: apps/activity/views.py:184 +#: apps/activity/views.py:199 msgid "This activity is closed." msgstr "Diese Veranstaltung ist geschlossen." -#: apps/activity/views.py:280 +#: apps/activity/views.py:295 msgid "Entry for activity \"{}\"" msgstr "Eintritt zur Veranstaltung \"{}\"" @@ -439,6 +445,153 @@ msgstr "Eintritt zur Veranstaltung \"{}\"" msgid "API" msgstr "API" +#: apps/food/apps.py:11 apps/food/models.py:96 +msgid "food" +msgstr "" + +#: apps/food/forms.py:41 +msgid "pasta" +msgstr "" + +#: apps/food/forms.py:85 +msgid "lasagna" +msgstr "" + +#: apps/food/models.py:18 +#, fuzzy +#| msgid "phone number" +msgid "QR-code number" +msgstr "Telefonnummer" + +#: apps/food/models.py:26 +msgid "food container" +msgstr "" + +#: apps/food/models.py:30 +msgid "QR-code" +msgstr "" + +#: apps/food/models.py:31 +msgid "QR-codes" +msgstr "" + +#: apps/food/models.py:34 +#, python-brace-format +msgid "QR-code number {qr_code_number}" +msgstr "" + +#: apps/food/models.py:47 +msgid "Allergen" +msgstr "" + +#: apps/food/models.py:48 +msgid "Allergens" +msgstr "" + +#: apps/food/models.py:64 +msgid "owner" +msgstr "" + +#: apps/food/models.py:70 +msgid "allergen" +msgstr "" + +#: apps/food/models.py:74 +#, fuzzy +#| msgid "birth date" +msgid "expiry date" +msgstr "Geburtsdatum" + +#: apps/food/models.py:80 +msgid "was eaten" +msgstr "" + +#: apps/food/models.py:85 +msgid "is ready" +msgstr "" + +#: apps/food/models.py:97 +msgid "foods" +msgstr "" + +#: apps/food/models.py:113 +#, fuzzy +#| msgid "start date" +msgid "arrival date" +msgstr "Anfangsdatum" + +#: apps/food/models.py:143 +msgid "Basic food" +msgstr "" + +#: apps/food/models.py:144 +msgid "Basic foods" +msgstr "" + +#: apps/food/models.py:152 +#, fuzzy +#| msgid "created at" +msgid "creation date" +msgstr "erschafft am" + +#: apps/food/models.py:160 +msgid "transformed ingredient" +msgstr "" + +#: apps/food/models.py:165 +#, fuzzy +#| msgid "active" +msgid "is active" +msgstr "Aktiv" + +#: apps/food/models.py:170 +msgid "shelf life" +msgstr "" + +#: apps/food/models.py:216 apps/food/views.py:263 +#, fuzzy +#| msgid "Transfer money" +msgid "Transformed food" +msgstr "Geld überweisen" + +#: apps/food/models.py:217 +msgid "Transformed foods" +msgstr "" + +#: apps/food/views.py:25 +msgid "Add the ingredient" +msgstr "" + +#: apps/food/views.py:39 +msgid "The product isn't ready" +msgstr "" + +#: apps/food/views.py:63 apps/food/views.py:86 +msgid "Add a new aliment" +msgstr "" + +#: apps/food/views.py:94 +#, fuzzy +#| msgid "WEI Detail" +msgid "Details" +msgstr "WEI Infos" + +#: apps/food/views.py:110 +msgid "Add a new basic food with QRCode" +msgstr "" + +#: apps/food/views.py:153 +msgid "Add a new QRCode" +msgstr "" + +#: apps/food/views.py:201 +msgid "QRCode" +msgstr "" + +#: apps/food/views.py:220 +msgid "Add a new meal" +msgstr "" + #: apps/logs/apps.py:11 msgid "Logs" msgstr "Logs" @@ -508,11 +661,11 @@ msgstr "Mitgliedschaftpreis (bezahlte Studenten)" msgid "membership fee (unpaid students)" msgstr "Mitgliedschaftpreis (unbezahlte Studenten)" -#: apps/member/admin.py:65 apps/member/models.py:325 +#: apps/member/admin.py:65 apps/member/models.py:330 msgid "roles" msgstr "Rollen" -#: apps/member/admin.py:66 apps/member/models.py:339 +#: apps/member/admin.py:66 apps/member/models.py:344 msgid "fee" msgstr "Preis" @@ -565,49 +718,49 @@ msgstr "Maximal Größe: 2MB" msgid "This image cannot be loaded." msgstr "Dieses Bild kann nicht geladen werden." -#: apps/member/forms.py:148 apps/member/views.py:102 -#: apps/registration/forms.py:34 apps/registration/views.py:266 +#: apps/member/forms.py:151 apps/member/views.py:102 +#: apps/registration/forms.py:33 apps/registration/views.py:276 msgid "An alias with a similar name already exists." msgstr "Ein ähnliches Alias ist schon benutzt." -#: apps/member/forms.py:172 +#: apps/member/forms.py:175 msgid "Inscription paid by Société Générale" msgstr "Mitgliedschaft von der Société Générale bezahlt" -#: apps/member/forms.py:174 +#: apps/member/forms.py:177 msgid "Check this case if the Société Générale paid the inscription." msgstr "Die Société Générale die Mitgliedschaft bezahlt." -#: apps/member/forms.py:179 apps/registration/forms.py:79 +#: apps/member/forms.py:182 apps/registration/forms.py:78 #: apps/wei/forms/registration.py:91 msgid "Credit type" msgstr "Kredittype" -#: apps/member/forms.py:180 apps/registration/forms.py:80 +#: apps/member/forms.py:183 apps/registration/forms.py:79 #: apps/wei/forms/registration.py:92 msgid "No credit" msgstr "Kein Kredit" -#: apps/member/forms.py:182 +#: apps/member/forms.py:185 msgid "You can credit the note of the user." msgstr "Sie dûrfen diese Note kreditieren." -#: apps/member/forms.py:186 apps/registration/forms.py:85 +#: apps/member/forms.py:189 apps/registration/forms.py:84 #: apps/wei/forms/registration.py:97 msgid "Credit amount" msgstr "Kreditanzahl" -#: apps/member/forms.py:203 apps/note/templates/note/transaction_form.html:144 -#: apps/registration/forms.py:102 apps/treasury/forms.py:135 +#: apps/member/forms.py:206 apps/note/templates/note/transaction_form.html:144 +#: apps/registration/forms.py:101 apps/treasury/forms.py:135 #: apps/wei/forms/registration.py:114 msgid "Bank" msgstr "Bank" -#: apps/member/forms.py:230 +#: apps/member/forms.py:233 msgid "User" msgstr "User" -#: apps/member/forms.py:244 +#: apps/member/forms.py:247 msgid "Roles" msgstr "Rollen" @@ -857,46 +1010,52 @@ msgid "Maximal date of a membership, after which members must renew it." msgstr "" "Maximales Datum einer Mitgliedschaft, nach dem Mitglieder es erneuern müssen." -#: apps/member/models.py:263 apps/member/models.py:319 +#: apps/member/models.py:263 +#, fuzzy +#| msgid "Validate registration" +msgid "add to registration form" +msgstr "Registrierung validieren" + +#: apps/member/models.py:268 apps/member/models.py:324 #: apps/note/models/notes.py:176 msgid "club" msgstr "Club" -#: apps/member/models.py:264 +#: apps/member/models.py:269 msgid "clubs" msgstr "Clubs" -#: apps/member/models.py:330 +#: apps/member/models.py:335 msgid "membership starts on" msgstr "Mitgliedschaft fängt an" -#: apps/member/models.py:334 +#: apps/member/models.py:339 msgid "membership ends on" msgstr "Mitgliedschaft endet am" -#: apps/member/models.py:343 apps/note/models/transactions.py:385 +#: apps/member/models.py:348 apps/note/models/transactions.py:385 msgid "membership" msgstr "Mitgliedschaft" -#: apps/member/models.py:344 +#: apps/member/models.py:349 msgid "memberships" msgstr "Mitgliedschaften" -#: apps/member/models.py:348 +#: apps/member/models.py:353 #, python-brace-format msgid "Membership of {user} for the club {club}" msgstr "Mitgliedschaft von {user} für das Club {club}" -#: apps/member/models.py:367 +#: apps/member/models.py:372 #, python-brace-format msgid "The role {role} does not apply to the club {club}." msgstr "Die Rolle {role} ist nicht erlaubt für das Club {club}." -#: apps/member/models.py:376 apps/member/views.py:712 +#: apps/member/models.py:381 apps/member/views.py:715 msgid "User is already a member of the club" msgstr "User ist schon ein Mitglied dieser club" -#: apps/member/models.py:388 apps/member/views.py:721 +#: apps/member/models.py:393 apps/member/views.py:724 msgid "User is not a member of the parent club" msgstr "User ist noch nicht Mitglied des Urclubs" @@ -1013,7 +1172,7 @@ msgstr "" #: apps/member/templates/member/club_alias.html:10 #: apps/member/templates/member/profile_alias.html:10 apps/member/views.py:287 -#: apps/member/views.py:517 +#: apps/member/views.py:520 msgid "Note aliases" msgstr "Note Aliases" @@ -1169,11 +1328,11 @@ msgstr "" msgid "Show my applications" msgstr "" -#: apps/member/templates/member/picture_update.html:35 +#: apps/member/templates/member/picture_update.html:38 msgid "Nevermind" msgstr "Vergessen" -#: apps/member/templates/member/picture_update.html:36 +#: apps/member/templates/member/picture_update.html:39 msgid "Crop and upload" msgstr "Beschneiden und hochladen" @@ -1218,7 +1377,7 @@ msgstr "Speichern" msgid "Registrations" msgstr "Anmeldung" -#: apps/member/views.py:72 apps/registration/forms.py:24 +#: apps/member/views.py:72 apps/registration/forms.py:23 msgid "This address must be valid." msgstr "Diese Adresse muss gültig sein." @@ -1238,31 +1397,31 @@ msgstr "" msgid "Update note picture" msgstr "Notebild ändern" -#: apps/member/views.py:354 +#: apps/member/views.py:357 msgid "Manage auth token" msgstr "Auth token bearbeiten" -#: apps/member/views.py:381 +#: apps/member/views.py:384 msgid "Create new club" msgstr "Neue Club" -#: apps/member/views.py:400 +#: apps/member/views.py:403 msgid "Search club" msgstr "Club finden" -#: apps/member/views.py:433 +#: apps/member/views.py:436 msgid "Club detail" msgstr "Club Details" -#: apps/member/views.py:540 +#: apps/member/views.py:543 msgid "Update club" msgstr "Club bearbeiten" -#: apps/member/views.py:574 +#: apps/member/views.py:577 msgid "Add new member to the club" msgstr "Neue Mitglieder" -#: apps/member/views.py:703 apps/wei/views.py:973 +#: apps/member/views.py:706 apps/wei/views.py:973 msgid "" "This user don't have enough money to join this club, and can't have a " "negative balance." @@ -1270,19 +1429,19 @@ msgstr "" "Diese User hat nicht genug Geld um Mitglied zu werden, und darf nich im Rot " "sein." -#: apps/member/views.py:725 +#: apps/member/views.py:728 msgid "The membership must start after {:%m-%d-%Y}." msgstr "Die Mitgliedschaft muss nach {:%m-%d-Y} anfängen." -#: apps/member/views.py:730 +#: apps/member/views.py:733 msgid "The membership must begin before {:%m-%d-%Y}." msgstr "Die Mitgliedschaft muss vor {:%m-%d-Y} anfängen." -#: apps/member/views.py:880 +#: apps/member/views.py:883 msgid "Manage roles of an user in the club" msgstr "Rollen in diesen Club bearbeiten" -#: apps/member/views.py:905 +#: apps/member/views.py:908 msgid "Members of the club" msgstr "Mitlglieder dieses Club" @@ -2027,7 +2186,7 @@ msgstr "" "diesen Parametern zu erstellen. Bitte korrigieren Sie Ihre Daten und " "versuchen Sie es erneut." -#: apps/permission/views.py:112 note_kfet/templates/base.html:108 +#: apps/permission/views.py:112 note_kfet/templates/base.html:114 msgid "Rights" msgstr "Rechten" @@ -2039,15 +2198,15 @@ msgstr "Alle Rechten" msgid "registration" msgstr "Anmeldung" -#: apps/registration/forms.py:40 +#: apps/registration/forms.py:39 msgid "This email address is already used." msgstr "Diese email adresse ist schon benutzt." -#: apps/registration/forms.py:60 +#: apps/registration/forms.py:59 msgid "Register to the WEI" msgstr "Zu WEI anmelden" -#: apps/registration/forms.py:62 +#: apps/registration/forms.py:61 msgid "" "Check this case if you want to register to the WEI. If you hesitate, you " "will be able to register later, after validating your account in the Kfet." @@ -2056,11 +2215,11 @@ msgstr "" "falls Zweifel, können Sie sich später nach Bestätigung Ihres Kontos im Kfet " "registrieren." -#: apps/registration/forms.py:107 +#: apps/registration/forms.py:106 msgid "Join BDE Club" msgstr "BDE Mitglieder werden" -#: apps/registration/forms.py:114 +#: apps/registration/forms.py:113 msgid "Join Kfet Club" msgstr "Kfet Mitglieder werden" @@ -2171,58 +2330,64 @@ msgstr "Danke" msgid "The Note Kfet team." msgstr "Die NoteKfet Team." -#: apps/registration/views.py:41 +#: apps/registration/views.py:42 msgid "Register new user" msgstr "Neuen User registrieren" -#: apps/registration/views.py:99 +#: apps/registration/views.py:100 msgid "Email validation" msgstr "Email validierung" -#: apps/registration/views.py:101 +#: apps/registration/views.py:102 msgid "Validate email" msgstr "Email validieren" -#: apps/registration/views.py:145 +#: apps/registration/views.py:146 msgid "Email validation unsuccessful" msgstr "Email validierung unerfolgreich" -#: apps/registration/views.py:156 +#: apps/registration/views.py:157 msgid "Email validation email sent" msgstr "Validierungsemail wurde gesendet" -#: apps/registration/views.py:164 +#: apps/registration/views.py:165 msgid "Resend email validation link" msgstr "E-Mail-Validierungslink erneut senden" -#: apps/registration/views.py:182 +#: apps/registration/views.py:183 msgid "Pre-registered users list" msgstr "Vorregistrierte Userliste" -#: apps/registration/views.py:206 +#: apps/registration/views.py:207 msgid "Unregistered users" msgstr "Unregistrierte Users" -#: apps/registration/views.py:219 +#: apps/registration/views.py:220 msgid "Registration detail" msgstr "Registrierung Detailen" -#: apps/registration/views.py:293 +#: apps/registration/views.py:256 +#, fuzzy, python-format +#| msgid "Note of %(club)s club" +msgid "Join %(club)s Club" +msgstr "%(club)s Note" + +#: apps/registration/views.py:299 msgid "You must join the BDE." msgstr "Sie müssen die BDE beitreten." -#: apps/registration/views.py:323 +#: apps/registration/views.py:330 msgid "" "The entered amount is not enough for the memberships, should be at least {}" msgstr "" "Der eingegebene Betrag reicht für die Mitgliedschaft nicht aus, sollte " "mindestens {} betragen" -#: apps/registration/views.py:417 +#: apps/registration/views.py:425 msgid "Invalidate pre-registration" msgstr "Ungültige Vorregistrierung" -#: apps/treasury/apps.py:12 note_kfet/templates/base.html:96 +#: apps/treasury/apps.py:12 note_kfet/templates/base.html:102 msgid "Treasury" msgstr "Quaestor" @@ -2648,7 +2813,7 @@ msgstr "Krediten von der Société générale handeln" #: apps/wei/apps.py:10 apps/wei/models.py:37 apps/wei/models.py:38 #: apps/wei/models.py:62 apps/wei/models.py:178 -#: note_kfet/templates/base.html:102 +#: note_kfet/templates/base.html:108 msgid "WEI" msgstr "WEI" @@ -3282,19 +3447,19 @@ msgstr "" msgid "Attribute bus" msgstr "" -#: note_kfet/settings/base.py:172 +#: note_kfet/settings/base.py:173 msgid "German" msgstr "Deutsch" -#: note_kfet/settings/base.py:173 +#: note_kfet/settings/base.py:174 msgid "English" msgstr "English" -#: note_kfet/settings/base.py:174 +#: note_kfet/settings/base.py:175 msgid "Spanish" msgstr "Spanisch" -#: note_kfet/settings/base.py:175 +#: note_kfet/settings/base.py:176 msgid "French" msgstr "Französich" @@ -3358,34 +3523,38 @@ msgstr "Reset" msgid "The ENS Paris-Saclay BDE note." msgstr "Die BDE ENS-Paris-Saclay Note." -#: note_kfet/templates/base.html:78 +#: note_kfet/templates/base.html:72 +msgid "Food" +msgstr "" + +#: note_kfet/templates/base.html:84 msgid "Users" msgstr "Users" -#: note_kfet/templates/base.html:84 +#: note_kfet/templates/base.html:90 msgid "Clubs" msgstr "Clubs" -#: note_kfet/templates/base.html:113 +#: note_kfet/templates/base.html:119 msgid "Admin" msgstr "Admin" -#: note_kfet/templates/base.html:127 +#: note_kfet/templates/base.html:133 msgid "My account" msgstr "Mein Konto" -#: note_kfet/templates/base.html:130 +#: note_kfet/templates/base.html:136 msgid "Log out" msgstr "Abmelden" -#: note_kfet/templates/base.html:138 +#: note_kfet/templates/base.html:144 #: note_kfet/templates/registration/signup.html:6 #: note_kfet/templates/registration/signup.html:11 #: note_kfet/templates/registration/signup.html:28 msgid "Sign up" msgstr "Registrieren" -#: note_kfet/templates/base.html:145 +#: note_kfet/templates/base.html:151 #: note_kfet/templates/registration/login.html:6 #: note_kfet/templates/registration/login.html:15 #: note_kfet/templates/registration/login.html:38 @@ -3393,13 +3562,13 @@ msgstr "Registrieren" msgid "Log in" msgstr "Anmelden" -#: note_kfet/templates/base.html:159 +#: note_kfet/templates/base.html:165 msgid "" "You are not a BDE member anymore. Please renew your membership if you want " "to use the note." msgstr "" -#: note_kfet/templates/base.html:165 +#: note_kfet/templates/base.html:171 msgid "" "Your e-mail address is not validated. Please check your mail inbox and click " "on the validation link." @@ -3407,7 +3576,7 @@ msgstr "" "Ihre E-Mail-Adresse ist nicht validiert. Bitte überprüfen Sie Ihren " "Posteingang und klicken Sie auf den Validierungslink." -#: note_kfet/templates/base.html:171 +#: note_kfet/templates/base.html:177 msgid "" "You declared that you opened a bank account in the Société générale. The " "bank did not validate the creation of the account to the BDE, so the " @@ -3416,15 +3585,19 @@ msgid "" "creation." msgstr "" -#: note_kfet/templates/base.html:194 +#: note_kfet/templates/base.html:200 msgid "Contact us" msgstr "Kontakt" -#: note_kfet/templates/base.html:196 +#: note_kfet/templates/base.html:202 msgid "Technical Support" msgstr "" -#: note_kfet/templates/base.html:198 +#: note_kfet/templates/base.html:204 +msgid "Charte Info (FR)" +msgstr "" + +#: note_kfet/templates/base.html:206 msgid "FAQ (FR)" msgstr "FAQ (FR)" diff --git a/locale/es/LC_MESSAGES/django.po b/locale/es/LC_MESSAGES/django.po index a5482763..ec3baf3b 100644 --- a/locale/es/LC_MESSAGES/django.po +++ b/locale/es/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-03-22 00:33+0100\n" +"POT-Creation-Date: 2024-08-06 13:39+0200\n" "PO-Revision-Date: 2022-04-11 23:12+0200\n" "Last-Translator: bleizi \n" "Language-Team: \n" @@ -18,8 +18,8 @@ msgstr "" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Generator: Poedit 3.0.1\n" -#: apps/activity/apps.py:10 apps/activity/models.py:127 -#: apps/activity/models.py:167 +#: apps/activity/apps.py:10 apps/activity/models.py:129 +#: apps/activity/models.py:169 msgid "activity" msgstr "actividad" @@ -27,32 +27,32 @@ msgstr "actividad" msgid "The note of this club is inactive." msgstr "La note del club está inactiva." -#: apps/activity/forms.py:41 apps/activity/models.py:140 +#: apps/activity/forms.py:41 apps/activity/models.py:142 msgid "The end date must be after the start date." msgstr "La fecha final tiene que ser después de la fecha de inicio." -#: apps/activity/forms.py:82 apps/activity/models.py:269 +#: apps/activity/forms.py:82 apps/activity/models.py:271 msgid "You can't invite someone once the activity is started." msgstr "No se puede invitar a alguien una vez que arrancó la actividad." -#: apps/activity/forms.py:85 apps/activity/models.py:272 +#: apps/activity/forms.py:85 apps/activity/models.py:274 msgid "This activity is not validated yet." msgstr "Esta actividad no fue validada por ahora." -#: apps/activity/forms.py:95 apps/activity/models.py:280 +#: apps/activity/forms.py:95 apps/activity/models.py:282 msgid "This person has been already invited 5 times this year." msgstr "Esta persona ya fue invitada 5 veces este año." -#: apps/activity/forms.py:99 apps/activity/models.py:284 +#: apps/activity/forms.py:99 apps/activity/models.py:286 msgid "This person is already invited." msgstr "Esta persona ya está invitada." -#: apps/activity/forms.py:103 apps/activity/models.py:288 +#: apps/activity/forms.py:103 apps/activity/models.py:290 msgid "You can't invite more than 3 people to this activity." msgstr "Usted no puede invitar más de 3 persona a esta actividad." -#: apps/activity/models.py:28 apps/activity/models.py:63 -#: apps/member/models.py:203 +#: apps/activity/models.py:28 apps/activity/models.py:63 apps/food/models.py:42 +#: apps/food/models.py:56 apps/member/models.py:203 #: apps/member/templates/member/includes/club_info.html:4 #: apps/member/templates/member/includes/profile_info.html:4 #: apps/note/models/notes.py:263 apps/note/models/transactions.py:26 @@ -98,121 +98,121 @@ msgstr "tipos de actividad" msgid "description" msgstr "descripción" -#: apps/activity/models.py:72 +#: apps/activity/models.py:74 msgid "location" msgstr "ubicación" -#: apps/activity/models.py:76 +#: apps/activity/models.py:78 msgid "Place where the activity is organized, eg. Kfet." msgstr "Lugar donde se organiza la actividad, por ejemplo la Kfet." -#: apps/activity/models.py:83 +#: apps/activity/models.py:85 #: apps/activity/templates/activity/includes/activity_info.html:22 #: apps/note/models/notes.py:207 apps/note/models/transactions.py:67 #: apps/permission/models.py:163 msgid "type" msgstr "tipo" -#: apps/activity/models.py:89 apps/logs/models.py:22 apps/member/models.py:313 +#: apps/activity/models.py:91 apps/logs/models.py:22 apps/member/models.py:318 #: apps/note/models/notes.py:148 apps/treasury/models.py:293 #: apps/wei/models.py:171 apps/wei/templates/wei/attribute_bus_1A.html:13 #: apps/wei/templates/wei/survey.html:15 msgid "user" msgstr "usuario" -#: apps/activity/models.py:96 +#: apps/activity/models.py:98 #: apps/activity/templates/activity/includes/activity_info.html:36 msgid "organizer" msgstr "organizador" -#: apps/activity/models.py:97 +#: apps/activity/models.py:99 msgid "Club that organizes the activity. The entry fees will go to this club." msgstr "" "El club que organiza la actividad. Los pagos de entrada serán dados a este " "club." -#: apps/activity/models.py:104 +#: apps/activity/models.py:106 #: apps/activity/templates/activity/includes/activity_info.html:39 msgid "attendees club" msgstr "club esperado" -#: apps/activity/models.py:105 +#: apps/activity/models.py:107 msgid "Club that is authorized to join the activity. Mostly the Kfet club." msgstr "Club permitido unirse a la actividad. Generalmente el club Kfet." -#: apps/activity/models.py:109 +#: apps/activity/models.py:111 #: apps/activity/templates/activity/includes/activity_info.html:25 msgid "start date" msgstr "fecha de inicio" -#: apps/activity/models.py:113 +#: apps/activity/models.py:115 #: apps/activity/templates/activity/includes/activity_info.html:28 msgid "end date" msgstr "fecha de fin" -#: apps/activity/models.py:118 +#: apps/activity/models.py:120 #: apps/activity/templates/activity/includes/activity_info.html:50 #: apps/note/models/transactions.py:149 msgid "valid" msgstr "válido" -#: apps/activity/models.py:123 +#: apps/activity/models.py:125 #: apps/activity/templates/activity/includes/activity_info.html:65 msgid "open" msgstr "abierto" -#: apps/activity/models.py:128 +#: apps/activity/models.py:130 msgid "activities" msgstr "actividades" -#: apps/activity/models.py:172 +#: apps/activity/models.py:174 msgid "entry time" msgstr "hora de entrada" -#: apps/activity/models.py:178 apps/note/apps.py:14 +#: apps/activity/models.py:180 apps/note/apps.py:14 #: apps/note/models/notes.py:77 msgid "note" msgstr "note" -#: apps/activity/models.py:189 +#: apps/activity/models.py:191 #: apps/activity/templates/activity/activity_entry.html:46 msgid "entry" msgstr "entrada" -#: apps/activity/models.py:190 +#: apps/activity/models.py:192 #: apps/activity/templates/activity/activity_entry.html:46 msgid "entries" msgstr "entradas" -#: apps/activity/models.py:193 +#: apps/activity/models.py:195 #, python-brace-format msgid "Entry for {guest}, invited by {note} to the activity {activity}" msgstr "Entrada para {guest}, invitado por {note} en la actividad {activity}" -#: apps/activity/models.py:195 +#: apps/activity/models.py:197 #, python-brace-format msgid "Entry for {note} to the activity {activity}" msgstr "Entrada para {note} en la actividad {activity}" -#: apps/activity/models.py:202 +#: apps/activity/models.py:204 msgid "Already entered on " msgstr "Entrado ya el " -#: apps/activity/models.py:202 apps/activity/tables.py:56 +#: apps/activity/models.py:204 apps/activity/tables.py:56 msgid "{:%Y-%m-%d %H:%M:%S}" msgstr "{:%d/%m/%Y %H:%M:%S}" -#: apps/activity/models.py:210 +#: apps/activity/models.py:212 msgid "The balance is negative." msgstr "El saldo es negativo." -#: apps/activity/models.py:240 +#: apps/activity/models.py:242 #: apps/treasury/templates/treasury/sogecredit_detail.html:14 #: apps/wei/templates/wei/attribute_bus_1A.html:16 msgid "last name" msgstr "apellido" -#: apps/activity/models.py:245 +#: apps/activity/models.py:247 #: apps/member/templates/member/includes/profile_info.html:4 #: apps/registration/templates/registration/future_profile_detail.html:16 #: apps/treasury/templates/treasury/sogecredit_detail.html:17 @@ -221,19 +221,19 @@ msgstr "apellido" msgid "first name" msgstr "nombre" -#: apps/activity/models.py:252 +#: apps/activity/models.py:254 msgid "inviter" msgstr "huésped" -#: apps/activity/models.py:256 +#: apps/activity/models.py:258 msgid "guest" msgstr "invitado" -#: apps/activity/models.py:257 +#: apps/activity/models.py:259 msgid "guests" msgstr "invitados" -#: apps/activity/models.py:310 +#: apps/activity/models.py:312 msgid "Invitation" msgstr "Invitación" @@ -245,7 +245,9 @@ msgstr "La actividad está actualmente abierta." msgid "The validation of the activity is pending." msgstr "La validación de esta actividad es pendiente." -#: apps/activity/tables.py:43 apps/treasury/tables.py:107 +#: apps/activity/tables.py:43 +#: apps/member/templates/member/picture_update.html:18 +#: apps/treasury/tables.py:107 msgid "Remove" msgstr "Quitar" @@ -261,15 +263,15 @@ msgstr "quitar" msgid "Type" msgstr "Tipo" -#: apps/activity/tables.py:84 apps/member/forms.py:193 -#: apps/registration/forms.py:92 apps/treasury/forms.py:131 +#: apps/activity/tables.py:84 apps/member/forms.py:196 +#: apps/registration/forms.py:91 apps/treasury/forms.py:131 #: apps/wei/forms/registration.py:104 msgid "Last name" msgstr "Apellido" -#: apps/activity/tables.py:86 apps/member/forms.py:198 +#: apps/activity/tables.py:86 apps/member/forms.py:201 #: apps/note/templates/note/transaction_form.html:138 -#: apps/registration/forms.py:97 apps/treasury/forms.py:133 +#: apps/registration/forms.py:96 apps/treasury/forms.py:133 #: apps/wei/forms/registration.py:109 msgid "First name" msgstr "Nombre" @@ -294,7 +296,7 @@ msgstr "Invitados suprimidos" #: apps/note/models/transactions.py:261 #: apps/note/templates/note/transaction_form.html:17 #: apps/note/templates/note/transaction_form.html:152 -#: note_kfet/templates/base.html:72 +#: note_kfet/templates/base.html:78 msgid "Transfer" msgstr "Transferencia" @@ -329,6 +331,10 @@ msgid "Entry done!" msgstr "Entrada echa !" #: apps/activity/templates/activity/activity_form.html:16 +#: apps/food/templates/food/add_ingredient_form.html:17 +#: apps/food/templates/food/basic_food_form.html:17 +#: apps/food/templates/food/create_qrcode_form.html:20 +#: apps/food/templates/food/transformed_food_form.html:17 #: apps/member/templates/member/add_members.html:46 #: apps/member/templates/member/club_form.html:16 #: apps/note/templates/note/transactiontemplate_form.html:18 @@ -394,41 +400,41 @@ msgstr "modificar" msgid "Invite" msgstr "Invitar" -#: apps/activity/views.py:36 +#: apps/activity/views.py:37 msgid "Create new activity" msgstr "Crear una nueva actividad" -#: apps/activity/views.py:67 note_kfet/templates/base.html:90 +#: apps/activity/views.py:67 note_kfet/templates/base.html:96 msgid "Activities" msgstr "Actividades" -#: apps/activity/views.py:93 +#: apps/activity/views.py:108 msgid "Activity detail" msgstr "Detalles de la actividad" -#: apps/activity/views.py:113 +#: apps/activity/views.py:128 msgid "Update activity" msgstr "Modificar la actividad" -#: apps/activity/views.py:140 +#: apps/activity/views.py:155 msgid "Invite guest to the activity \"{}\"" msgstr "Invitar alguien para la actividad \"{}\"" -#: apps/activity/views.py:178 +#: apps/activity/views.py:193 msgid "You are not allowed to display the entry interface for this activity." msgstr "" "Usted no tiene derecho a mostrar la interfaz de las entradas para esta " "actividad." -#: apps/activity/views.py:181 +#: apps/activity/views.py:196 msgid "This activity does not support activity entries." msgstr "Esta actividad no necesita entradas." -#: apps/activity/views.py:184 +#: apps/activity/views.py:199 msgid "This activity is closed." msgstr "Esta actividad esta cerrada." -#: apps/activity/views.py:280 +#: apps/activity/views.py:295 msgid "Entry for activity \"{}\"" msgstr "Entradas para la actividad \"{}\"" @@ -436,6 +442,153 @@ msgstr "Entradas para la actividad \"{}\"" msgid "API" msgstr "API" +#: apps/food/apps.py:11 apps/food/models.py:96 +msgid "food" +msgstr "" + +#: apps/food/forms.py:41 +msgid "pasta" +msgstr "" + +#: apps/food/forms.py:85 +msgid "lasagna" +msgstr "" + +#: apps/food/models.py:18 +#, fuzzy +#| msgid "phone number" +msgid "QR-code number" +msgstr "número de teléfono" + +#: apps/food/models.py:26 +msgid "food container" +msgstr "" + +#: apps/food/models.py:30 +msgid "QR-code" +msgstr "" + +#: apps/food/models.py:31 +msgid "QR-codes" +msgstr "" + +#: apps/food/models.py:34 +#, python-brace-format +msgid "QR-code number {qr_code_number}" +msgstr "" + +#: apps/food/models.py:47 +msgid "Allergen" +msgstr "" + +#: apps/food/models.py:48 +msgid "Allergens" +msgstr "" + +#: apps/food/models.py:64 +msgid "owner" +msgstr "" + +#: apps/food/models.py:70 +msgid "allergen" +msgstr "" + +#: apps/food/models.py:74 +#, fuzzy +#| msgid "birth date" +msgid "expiry date" +msgstr "fecha de nacimiento" + +#: apps/food/models.py:80 +msgid "was eaten" +msgstr "" + +#: apps/food/models.py:85 +msgid "is ready" +msgstr "" + +#: apps/food/models.py:97 +msgid "foods" +msgstr "" + +#: apps/food/models.py:113 +#, fuzzy +#| msgid "invalidate" +msgid "arrival date" +msgstr "invalidar" + +#: apps/food/models.py:143 +msgid "Basic food" +msgstr "" + +#: apps/food/models.py:144 +msgid "Basic foods" +msgstr "" + +#: apps/food/models.py:152 +#, fuzzy +#| msgid "created at" +msgid "creation date" +msgstr "creada el" + +#: apps/food/models.py:160 +msgid "transformed ingredient" +msgstr "" + +#: apps/food/models.py:165 +#, fuzzy +#| msgid "active" +msgid "is active" +msgstr "activo" + +#: apps/food/models.py:170 +msgid "shelf life" +msgstr "" + +#: apps/food/models.py:216 apps/food/views.py:263 +#, fuzzy +#| msgid "Transfer money" +msgid "Transformed food" +msgstr "Transferir dinero" + +#: apps/food/models.py:217 +msgid "Transformed foods" +msgstr "" + +#: apps/food/views.py:25 +msgid "Add the ingredient" +msgstr "" + +#: apps/food/views.py:39 +msgid "The product isn't ready" +msgstr "" + +#: apps/food/views.py:63 apps/food/views.py:86 +msgid "Add a new aliment" +msgstr "" + +#: apps/food/views.py:94 +#, fuzzy +#| msgid "WEI Detail" +msgid "Details" +msgstr "Detalles del WEI" + +#: apps/food/views.py:110 +msgid "Add a new basic food with QRCode" +msgstr "" + +#: apps/food/views.py:153 +msgid "Add a new QRCode" +msgstr "" + +#: apps/food/views.py:201 +msgid "QRCode" +msgstr "" + +#: apps/food/views.py:220 +msgid "Add a new meal" +msgstr "" + #: apps/logs/apps.py:11 msgid "Logs" msgstr "Logs" @@ -505,11 +658,11 @@ msgstr "pago de afiliación (estudiantes pagados)" msgid "membership fee (unpaid students)" msgstr "pago de afiliación (estudiantes no pagados)" -#: apps/member/admin.py:65 apps/member/models.py:325 +#: apps/member/admin.py:65 apps/member/models.py:330 msgid "roles" msgstr "papel" -#: apps/member/admin.py:66 apps/member/models.py:339 +#: apps/member/admin.py:66 apps/member/models.py:344 msgid "fee" msgstr "pago" @@ -561,49 +714,49 @@ msgstr "Tamaño máximo : 2Mo" msgid "This image cannot be loaded." msgstr "Esta imagen no puede ser cargada." -#: apps/member/forms.py:148 apps/member/views.py:102 -#: apps/registration/forms.py:34 apps/registration/views.py:266 +#: apps/member/forms.py:151 apps/member/views.py:102 +#: apps/registration/forms.py:33 apps/registration/views.py:276 msgid "An alias with a similar name already exists." msgstr "Un alias similar ya existe." -#: apps/member/forms.py:172 +#: apps/member/forms.py:175 msgid "Inscription paid by Société Générale" msgstr "Registración pagadas por Société Générale" -#: apps/member/forms.py:174 +#: apps/member/forms.py:177 msgid "Check this case if the Société Générale paid the inscription." msgstr "Marcar esta casilla si Société Générale pagó la registración." -#: apps/member/forms.py:179 apps/registration/forms.py:79 +#: apps/member/forms.py:182 apps/registration/forms.py:78 #: apps/wei/forms/registration.py:91 msgid "Credit type" msgstr "Tipo de crédito" -#: apps/member/forms.py:180 apps/registration/forms.py:80 +#: apps/member/forms.py:183 apps/registration/forms.py:79 #: apps/wei/forms/registration.py:92 msgid "No credit" msgstr "No crédito" -#: apps/member/forms.py:182 +#: apps/member/forms.py:185 msgid "You can credit the note of the user." msgstr "Usted puede acreditar la note del usuario." -#: apps/member/forms.py:186 apps/registration/forms.py:85 +#: apps/member/forms.py:189 apps/registration/forms.py:84 #: apps/wei/forms/registration.py:97 msgid "Credit amount" msgstr "Valor del crédito" -#: apps/member/forms.py:203 apps/note/templates/note/transaction_form.html:144 -#: apps/registration/forms.py:102 apps/treasury/forms.py:135 +#: apps/member/forms.py:206 apps/note/templates/note/transaction_form.html:144 +#: apps/registration/forms.py:101 apps/treasury/forms.py:135 #: apps/wei/forms/registration.py:114 msgid "Bank" msgstr "Banco" -#: apps/member/forms.py:230 +#: apps/member/forms.py:233 msgid "User" msgstr "Usuario" -#: apps/member/forms.py:244 +#: apps/member/forms.py:247 msgid "Roles" msgstr "Papeles" @@ -850,46 +1003,52 @@ msgstr "" "Ultima fecha de una afiliación, después de la cual los miembros tienen que " "prorrogarla." -#: apps/member/models.py:263 apps/member/models.py:319 +#: apps/member/models.py:263 +#, fuzzy +#| msgid "Validate registration" +msgid "add to registration form" +msgstr "Validar la afiliación" + +#: apps/member/models.py:268 apps/member/models.py:324 #: apps/note/models/notes.py:176 msgid "club" msgstr "club" -#: apps/member/models.py:264 +#: apps/member/models.py:269 msgid "clubs" msgstr "clubs" -#: apps/member/models.py:330 +#: apps/member/models.py:335 msgid "membership starts on" msgstr "afiliación empezá el" -#: apps/member/models.py:334 +#: apps/member/models.py:339 msgid "membership ends on" msgstr "afiliación termina el" -#: apps/member/models.py:343 apps/note/models/transactions.py:385 +#: apps/member/models.py:348 apps/note/models/transactions.py:385 msgid "membership" msgstr "afiliación" -#: apps/member/models.py:344 +#: apps/member/models.py:349 msgid "memberships" msgstr "afiliaciones" -#: apps/member/models.py:348 +#: apps/member/models.py:353 #, python-brace-format msgid "Membership of {user} for the club {club}" msgstr "Afiliación of {user} for the club {club}" -#: apps/member/models.py:367 +#: apps/member/models.py:372 #, python-brace-format msgid "The role {role} does not apply to the club {club}." msgstr "El papel {role} no se encuentra en el club {club}." -#: apps/member/models.py:376 apps/member/views.py:712 +#: apps/member/models.py:381 apps/member/views.py:715 msgid "User is already a member of the club" msgstr "Usuario ya esta un miembro del club" -#: apps/member/models.py:388 apps/member/views.py:721 +#: apps/member/models.py:393 apps/member/views.py:724 msgid "User is not a member of the parent club" msgstr "Usuario no es un miembro del club pariente" @@ -1003,7 +1162,7 @@ msgstr "" #: apps/member/templates/member/club_alias.html:10 #: apps/member/templates/member/profile_alias.html:10 apps/member/views.py:287 -#: apps/member/views.py:517 +#: apps/member/views.py:520 msgid "Note aliases" msgstr "Alias de la note" @@ -1151,11 +1310,11 @@ msgstr "Introspección :" msgid "Show my applications" msgstr "Mostrar mis aplicaciones" -#: apps/member/templates/member/picture_update.html:35 +#: apps/member/templates/member/picture_update.html:38 msgid "Nevermind" msgstr "No importa" -#: apps/member/templates/member/picture_update.html:36 +#: apps/member/templates/member/picture_update.html:39 msgid "Crop and upload" msgstr "Podar y subir" @@ -1204,7 +1363,7 @@ msgstr "Guardar cambios" msgid "Registrations" msgstr "Registraciones" -#: apps/member/views.py:72 apps/registration/forms.py:24 +#: apps/member/views.py:72 apps/registration/forms.py:23 msgid "This address must be valid." msgstr "Este correo tiene que ser valido." @@ -1224,31 +1383,31 @@ msgstr "Amistades de note" msgid "Update note picture" msgstr "Modificar la imagen de la note" -#: apps/member/views.py:354 +#: apps/member/views.py:357 msgid "Manage auth token" msgstr "Gestionar los token de autentificación" -#: apps/member/views.py:381 +#: apps/member/views.py:384 msgid "Create new club" msgstr "Crear un nuevo club" -#: apps/member/views.py:400 +#: apps/member/views.py:403 msgid "Search club" msgstr "Buscar un club" -#: apps/member/views.py:433 +#: apps/member/views.py:436 msgid "Club detail" msgstr "Detalles del club" -#: apps/member/views.py:540 +#: apps/member/views.py:543 msgid "Update club" msgstr "Modificar el club" -#: apps/member/views.py:574 +#: apps/member/views.py:577 msgid "Add new member to the club" msgstr "Añadir un nuevo miembro al club" -#: apps/member/views.py:703 apps/wei/views.py:973 +#: apps/member/views.py:706 apps/wei/views.py:973 msgid "" "This user don't have enough money to join this club, and can't have a " "negative balance." @@ -1256,19 +1415,19 @@ msgstr "" "Este usuario no tiene suficiente dinero para unirse a este club, y no puede " "tener un saldo negativo." -#: apps/member/views.py:725 +#: apps/member/views.py:728 msgid "The membership must start after {:%m-%d-%Y}." msgstr "La afiliación tiene que empezar después del {:%d-%m-%Y}." -#: apps/member/views.py:730 +#: apps/member/views.py:733 msgid "The membership must begin before {:%m-%d-%Y}." msgstr "La afiliación tiene que empezar antes del {:%d-%m-%Y}." -#: apps/member/views.py:880 +#: apps/member/views.py:883 msgid "Manage roles of an user in the club" msgstr "Gestionar los papeles de un usuario en el club" -#: apps/member/views.py:905 +#: apps/member/views.py:908 msgid "Members of the club" msgstr "Miembros del club" @@ -2005,7 +2164,7 @@ msgid "" "with these parameters. Please correct your data and retry." msgstr "" -#: apps/permission/views.py:112 note_kfet/templates/base.html:108 +#: apps/permission/views.py:112 note_kfet/templates/base.html:114 msgid "Rights" msgstr "Permisos" @@ -2017,15 +2176,15 @@ msgstr "Todos los permisos" msgid "registration" msgstr "afiliación" -#: apps/registration/forms.py:40 +#: apps/registration/forms.py:39 msgid "This email address is already used." msgstr "Este correo electrónico ya esta utilizado." -#: apps/registration/forms.py:60 +#: apps/registration/forms.py:59 msgid "Register to the WEI" msgstr "Registrarse en el WEI" -#: apps/registration/forms.py:62 +#: apps/registration/forms.py:61 msgid "" "Check this case if you want to register to the WEI. If you hesitate, you " "will be able to register later, after validating your account in the Kfet." @@ -2033,11 +2192,11 @@ msgstr "" "Marcar esta casilla si usted quiere registrarse en el WEI. Si duda, podrá " "registrarse más tarde, después de validar su cuenta Note Kfet." -#: apps/registration/forms.py:107 +#: apps/registration/forms.py:106 msgid "Join BDE Club" msgstr "Afiliarse al club BDE" -#: apps/registration/forms.py:114 +#: apps/registration/forms.py:113 msgid "Join Kfet Club" msgstr "Afiliarse al club Kfet" @@ -2146,58 +2305,64 @@ msgstr "Gracias" msgid "The Note Kfet team." msgstr "El equipo Note Kfet." -#: apps/registration/views.py:41 +#: apps/registration/views.py:42 msgid "Register new user" msgstr "Registrar un nuevo usuario" -#: apps/registration/views.py:99 +#: apps/registration/views.py:100 msgid "Email validation" msgstr "Validación del correo electrónico" -#: apps/registration/views.py:101 +#: apps/registration/views.py:102 msgid "Validate email" msgstr "Validar el correo electrónico" -#: apps/registration/views.py:145 +#: apps/registration/views.py:146 msgid "Email validation unsuccessful" msgstr "La validación del correo electrónico fracasó" -#: apps/registration/views.py:156 +#: apps/registration/views.py:157 msgid "Email validation email sent" msgstr "Correo de validación enviado" -#: apps/registration/views.py:164 +#: apps/registration/views.py:165 msgid "Resend email validation link" msgstr "Reenviar el enlace de validación" -#: apps/registration/views.py:182 +#: apps/registration/views.py:183 msgid "Pre-registered users list" msgstr "Lista de los usuarios con afiliación pendiente" -#: apps/registration/views.py:206 +#: apps/registration/views.py:207 msgid "Unregistered users" msgstr "Usuarios con afiliación pendiente" -#: apps/registration/views.py:219 +#: apps/registration/views.py:220 msgid "Registration detail" msgstr "Detalles de la afiliación" -#: apps/registration/views.py:293 +#: apps/registration/views.py:256 +#, fuzzy, python-format +#| msgid "Note of %(club)s club" +msgid "Join %(club)s Club" +msgstr "Note del club %(club)s" + +#: apps/registration/views.py:299 msgid "You must join the BDE." msgstr "Usted tiene que afiliarse al BDE." -#: apps/registration/views.py:323 +#: apps/registration/views.py:330 msgid "" "The entered amount is not enough for the memberships, should be at least {}" msgstr "" "El monto dado no es suficiente para las afiliaciones, tiene que ser al menos " "{}" -#: apps/registration/views.py:417 +#: apps/registration/views.py:425 msgid "Invalidate pre-registration" msgstr "Invalidar la afiliación" -#: apps/treasury/apps.py:12 note_kfet/templates/base.html:96 +#: apps/treasury/apps.py:12 note_kfet/templates/base.html:102 msgid "Treasury" msgstr "Tesorería" @@ -2614,7 +2779,7 @@ msgstr "Gestionar los créditos de la Société Générale" #: apps/wei/apps.py:10 apps/wei/models.py:37 apps/wei/models.py:38 #: apps/wei/models.py:62 apps/wei/models.py:178 -#: note_kfet/templates/base.html:102 +#: note_kfet/templates/base.html:108 msgid "WEI" msgstr "WEI" @@ -3228,19 +3393,19 @@ msgstr "Repartir los primer años en los buses" msgid "Attribute bus" msgstr "Repartir en un bus" -#: note_kfet/settings/base.py:172 +#: note_kfet/settings/base.py:173 msgid "German" msgstr "Alemán" -#: note_kfet/settings/base.py:173 +#: note_kfet/settings/base.py:174 msgid "English" msgstr "Ingles" -#: note_kfet/settings/base.py:174 +#: note_kfet/settings/base.py:175 msgid "Spanish" msgstr "Español" -#: note_kfet/settings/base.py:175 +#: note_kfet/settings/base.py:176 msgid "French" msgstr "Francés" @@ -3302,34 +3467,38 @@ msgstr "Reiniciar" msgid "The ENS Paris-Saclay BDE note." msgstr "La note del BDE de la ENS Paris-Saclay." -#: note_kfet/templates/base.html:78 +#: note_kfet/templates/base.html:72 +msgid "Food" +msgstr "" + +#: note_kfet/templates/base.html:84 msgid "Users" msgstr "Usuarios" -#: note_kfet/templates/base.html:84 +#: note_kfet/templates/base.html:90 msgid "Clubs" msgstr "Clubs" -#: note_kfet/templates/base.html:113 +#: note_kfet/templates/base.html:119 msgid "Admin" msgstr "" -#: note_kfet/templates/base.html:127 +#: note_kfet/templates/base.html:133 msgid "My account" msgstr "Mi cuenta" -#: note_kfet/templates/base.html:130 +#: note_kfet/templates/base.html:136 msgid "Log out" msgstr "Desconectarse" -#: note_kfet/templates/base.html:138 +#: note_kfet/templates/base.html:144 #: note_kfet/templates/registration/signup.html:6 #: note_kfet/templates/registration/signup.html:11 #: note_kfet/templates/registration/signup.html:28 msgid "Sign up" msgstr "Registrar" -#: note_kfet/templates/base.html:145 +#: note_kfet/templates/base.html:151 #: note_kfet/templates/registration/login.html:6 #: note_kfet/templates/registration/login.html:15 #: note_kfet/templates/registration/login.html:38 @@ -3337,7 +3506,7 @@ msgstr "Registrar" msgid "Log in" msgstr "Conectarse" -#: note_kfet/templates/base.html:159 +#: note_kfet/templates/base.html:165 msgid "" "You are not a BDE member anymore. Please renew your membership if you want " "to use the note." @@ -3345,7 +3514,7 @@ msgstr "" "Usted ya no está miembro del BDE. Por favor renueva su afiliación si quiere " "usar la note." -#: note_kfet/templates/base.html:165 +#: note_kfet/templates/base.html:171 msgid "" "Your e-mail address is not validated. Please check your mail inbox and click " "on the validation link." @@ -3353,7 +3522,7 @@ msgstr "" "Su correo electrónico no fue validado. Por favor mire en sus correos y haga " "clic en el enlace de validación." -#: note_kfet/templates/base.html:171 +#: note_kfet/templates/base.html:177 msgid "" "You declared that you opened a bank account in the Société générale. The " "bank did not validate the creation of the account to the BDE, so the " @@ -3366,15 +3535,19 @@ msgstr "" "pagados. El proceso de convalidación puede durar unos días. Por favor " "comprueba que fue hasta el final de la creación de la cuenta." -#: note_kfet/templates/base.html:194 +#: note_kfet/templates/base.html:200 msgid "Contact us" msgstr "Contactarnos" -#: note_kfet/templates/base.html:196 +#: note_kfet/templates/base.html:202 msgid "Technical Support" msgstr "Soporte técnico" -#: note_kfet/templates/base.html:198 +#: note_kfet/templates/base.html:204 +msgid "Charte Info (FR)" +msgstr "" + +#: note_kfet/templates/base.html:206 msgid "FAQ (FR)" msgstr "FAQ (FR)" diff --git a/locale/fr/LC_MESSAGES/django.po b/locale/fr/LC_MESSAGES/django.po index 0caa0790..f67001ce 100644 --- a/locale/fr/LC_MESSAGES/django.po +++ b/locale/fr/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-03-22 00:33+0100\n" +"POT-Creation-Date: 2024-08-06 13:39+0200\n" "PO-Revision-Date: 2022-04-11 22:05+0200\n" "Last-Translator: bleizi \n" "Language-Team: French \n" @@ -18,8 +18,8 @@ msgstr "" "Plural-Forms: nplurals=2; plural=n > 1;\n" "X-Generator: Poedit 3.0\n" -#: apps/activity/apps.py:10 apps/activity/models.py:127 -#: apps/activity/models.py:167 +#: apps/activity/apps.py:10 apps/activity/models.py:129 +#: apps/activity/models.py:169 msgid "activity" msgstr "activité" @@ -27,33 +27,33 @@ msgstr "activité" msgid "The note of this club is inactive." msgstr "La note du club est inactive." -#: apps/activity/forms.py:41 apps/activity/models.py:140 +#: apps/activity/forms.py:41 apps/activity/models.py:142 msgid "The end date must be after the start date." msgstr "La date de fin doit être après celle de début." -#: apps/activity/forms.py:82 apps/activity/models.py:269 +#: apps/activity/forms.py:82 apps/activity/models.py:271 msgid "You can't invite someone once the activity is started." msgstr "" "Vous ne pouvez pas inviter quelqu'un une fois que l'activité a démarré." -#: apps/activity/forms.py:85 apps/activity/models.py:272 +#: apps/activity/forms.py:85 apps/activity/models.py:274 msgid "This activity is not validated yet." msgstr "Cette activité n'est pas encore validée." -#: apps/activity/forms.py:95 apps/activity/models.py:280 +#: apps/activity/forms.py:95 apps/activity/models.py:282 msgid "This person has been already invited 5 times this year." msgstr "Cette personne a déjà été invitée 5 fois cette année." -#: apps/activity/forms.py:99 apps/activity/models.py:284 +#: apps/activity/forms.py:99 apps/activity/models.py:286 msgid "This person is already invited." msgstr "Cette personne est déjà invitée." -#: apps/activity/forms.py:103 apps/activity/models.py:288 +#: apps/activity/forms.py:103 apps/activity/models.py:290 msgid "You can't invite more than 3 people to this activity." msgstr "Vous ne pouvez pas inviter plus de 3 personnes à cette activité." -#: apps/activity/models.py:28 apps/activity/models.py:63 -#: apps/member/models.py:203 +#: apps/activity/models.py:28 apps/activity/models.py:63 apps/food/models.py:42 +#: apps/food/models.py:56 apps/member/models.py:203 #: apps/member/templates/member/includes/club_info.html:4 #: apps/member/templates/member/includes/profile_info.html:4 #: apps/note/models/notes.py:263 apps/note/models/transactions.py:26 @@ -99,121 +99,121 @@ msgstr "types d'activité" msgid "description" msgstr "description" -#: apps/activity/models.py:72 +#: apps/activity/models.py:74 msgid "location" msgstr "lieu" -#: apps/activity/models.py:76 +#: apps/activity/models.py:78 msgid "Place where the activity is organized, eg. Kfet." msgstr "Lieu où l'activité est organisée, par exemple la Kfet." -#: apps/activity/models.py:83 +#: apps/activity/models.py:85 #: apps/activity/templates/activity/includes/activity_info.html:22 #: apps/note/models/notes.py:207 apps/note/models/transactions.py:67 #: apps/permission/models.py:163 msgid "type" msgstr "type" -#: apps/activity/models.py:89 apps/logs/models.py:22 apps/member/models.py:318 +#: apps/activity/models.py:91 apps/logs/models.py:22 apps/member/models.py:318 #: apps/note/models/notes.py:148 apps/treasury/models.py:293 #: apps/wei/models.py:171 apps/wei/templates/wei/attribute_bus_1A.html:13 #: apps/wei/templates/wei/survey.html:15 msgid "user" msgstr "utilisateur·ice" -#: apps/activity/models.py:96 +#: apps/activity/models.py:98 #: apps/activity/templates/activity/includes/activity_info.html:36 msgid "organizer" msgstr "organisateur·ice" -#: apps/activity/models.py:97 +#: apps/activity/models.py:99 msgid "Club that organizes the activity. The entry fees will go to this club." msgstr "" "Le club qui organise l'activité. Les coûts d'invitation iront pour ce club." -#: apps/activity/models.py:104 +#: apps/activity/models.py:106 #: apps/activity/templates/activity/includes/activity_info.html:39 msgid "attendees club" msgstr "club attendu" -#: apps/activity/models.py:105 +#: apps/activity/models.py:107 msgid "Club that is authorized to join the activity. Mostly the Kfet club." msgstr "" "Club qui est autorisé à rejoindre l'activité. Très souvent le club Kfet." -#: apps/activity/models.py:109 +#: apps/activity/models.py:111 #: apps/activity/templates/activity/includes/activity_info.html:25 msgid "start date" msgstr "date de début" -#: apps/activity/models.py:113 +#: apps/activity/models.py:115 #: apps/activity/templates/activity/includes/activity_info.html:28 msgid "end date" msgstr "date de fin" -#: apps/activity/models.py:118 +#: apps/activity/models.py:120 #: apps/activity/templates/activity/includes/activity_info.html:50 #: apps/note/models/transactions.py:149 msgid "valid" msgstr "valide" -#: apps/activity/models.py:123 +#: apps/activity/models.py:125 #: apps/activity/templates/activity/includes/activity_info.html:65 msgid "open" msgstr "ouvrir" -#: apps/activity/models.py:128 +#: apps/activity/models.py:130 msgid "activities" msgstr "activités" -#: apps/activity/models.py:172 +#: apps/activity/models.py:174 msgid "entry time" msgstr "heure d'entrée" -#: apps/activity/models.py:178 apps/note/apps.py:14 +#: apps/activity/models.py:180 apps/note/apps.py:14 #: apps/note/models/notes.py:77 msgid "note" msgstr "note" -#: apps/activity/models.py:189 +#: apps/activity/models.py:191 #: apps/activity/templates/activity/activity_entry.html:46 msgid "entry" msgstr "entrée" -#: apps/activity/models.py:190 +#: apps/activity/models.py:192 #: apps/activity/templates/activity/activity_entry.html:46 msgid "entries" msgstr "entrées" -#: apps/activity/models.py:193 +#: apps/activity/models.py:195 #, python-brace-format msgid "Entry for {guest}, invited by {note} to the activity {activity}" msgstr "Entrée pour {guest}, invité·e par {note} à l'activité {activity}" -#: apps/activity/models.py:195 +#: apps/activity/models.py:197 #, python-brace-format msgid "Entry for {note} to the activity {activity}" msgstr "Entrée de la note {note} pour l'activité « {activity} »" -#: apps/activity/models.py:202 +#: apps/activity/models.py:204 msgid "Already entered on " msgstr "Déjà rentré·e le " -#: apps/activity/models.py:202 apps/activity/tables.py:56 +#: apps/activity/models.py:204 apps/activity/tables.py:56 msgid "{:%Y-%m-%d %H:%M:%S}" msgstr "{:%d/%m/%Y %H:%M:%S}" -#: apps/activity/models.py:210 +#: apps/activity/models.py:212 msgid "The balance is negative." msgstr "La note est en négatif." -#: apps/activity/models.py:240 +#: apps/activity/models.py:242 #: apps/treasury/templates/treasury/sogecredit_detail.html:14 #: apps/wei/templates/wei/attribute_bus_1A.html:16 msgid "last name" msgstr "nom de famille" -#: apps/activity/models.py:245 +#: apps/activity/models.py:247 #: apps/member/templates/member/includes/profile_info.html:4 #: apps/registration/templates/registration/future_profile_detail.html:16 #: apps/treasury/templates/treasury/sogecredit_detail.html:17 @@ -222,19 +222,19 @@ msgstr "nom de famille" msgid "first name" msgstr "prénom" -#: apps/activity/models.py:252 +#: apps/activity/models.py:254 msgid "inviter" msgstr "hôte" -#: apps/activity/models.py:256 +#: apps/activity/models.py:258 msgid "guest" msgstr "invité·e" -#: apps/activity/models.py:257 +#: apps/activity/models.py:259 msgid "guests" msgstr "invité·e·s" -#: apps/activity/models.py:310 +#: apps/activity/models.py:312 msgid "Invitation" msgstr "Invitation" @@ -246,8 +246,9 @@ msgstr "Cette activité est actuellement ouverte." msgid "The validation of the activity is pending." msgstr "La validation de cette activité est en attente." -#: apps/activity/tables.py:43 apps/treasury/tables.py:107 +#: apps/activity/tables.py:43 #: apps/member/templates/member/picture_update.html:18 +#: apps/treasury/tables.py:107 msgid "Remove" msgstr "Supprimer" @@ -264,14 +265,14 @@ msgid "Type" msgstr "Type" #: apps/activity/tables.py:84 apps/member/forms.py:196 -#: apps/registration/forms.py:92 apps/treasury/forms.py:131 +#: apps/registration/forms.py:91 apps/treasury/forms.py:131 #: apps/wei/forms/registration.py:104 msgid "Last name" msgstr "Nom de famille" #: apps/activity/tables.py:86 apps/member/forms.py:201 #: apps/note/templates/note/transaction_form.html:138 -#: apps/registration/forms.py:97 apps/treasury/forms.py:133 +#: apps/registration/forms.py:96 apps/treasury/forms.py:133 #: apps/wei/forms/registration.py:109 msgid "First name" msgstr "Prénom" @@ -296,7 +297,7 @@ msgstr "Invité·e supprimé·e" #: apps/note/models/transactions.py:261 #: apps/note/templates/note/transaction_form.html:17 #: apps/note/templates/note/transaction_form.html:152 -#: note_kfet/templates/base.html:72 +#: note_kfet/templates/base.html:78 msgid "Transfer" msgstr "Virement" @@ -325,13 +326,18 @@ msgstr "Retour à la page de l'activité" #: apps/activity/templates/activity/activity_entry.html:129 msgid "Entry done, but caution: the user is not a Kfet member." msgstr "" -"Entrée effectuée, mais attention : la personne n'est pas un·e adhérent·e Kfet." +"Entrée effectuée, mais attention : la personne n'est pas un·e adhérent·e " +"Kfet." #: apps/activity/templates/activity/activity_entry.html:132 msgid "Entry done!" msgstr "Entrée effectuée !" #: apps/activity/templates/activity/activity_form.html:16 +#: apps/food/templates/food/add_ingredient_form.html:17 +#: apps/food/templates/food/basic_food_form.html:17 +#: apps/food/templates/food/create_qrcode_form.html:20 +#: apps/food/templates/food/transformed_food_form.html:17 #: apps/member/templates/member/add_members.html:46 #: apps/member/templates/member/club_form.html:16 #: apps/note/templates/note/transactiontemplate_form.html:18 @@ -397,11 +403,11 @@ msgstr "modifier" msgid "Invite" msgstr "Inviter" -#: apps/activity/views.py:36 +#: apps/activity/views.py:37 msgid "Create new activity" msgstr "Créer une nouvelle activité" -#: apps/activity/views.py:68 note_kfet/templates/base.html:90 +#: apps/activity/views.py:67 note_kfet/templates/base.html:96 msgid "Activities" msgstr "Activités" @@ -439,6 +445,139 @@ msgstr "Entrées pour l'activité « {} »" msgid "API" msgstr "API" +#: apps/food/apps.py:11 apps/food/models.py:96 +msgid "food" +msgstr "bouffe" + +#: apps/food/forms.py:41 +msgid "pasta" +msgstr "pâtes" + +#: apps/food/forms.py:85 +msgid "lasagna" +msgstr "lasagnes" + +#: apps/food/models.py:18 +msgid "QR-code number" +msgstr "numéro de QR-code" + +#: apps/food/models.py:26 +msgid "food container" +msgstr "récipient" + +#: apps/food/models.py:30 +msgid "QR-code" +msgstr "QR-code" + +#: apps/food/models.py:31 +msgid "QR-codes" +msgstr "QR-codes" + +#: apps/food/models.py:34 +#, python-brace-format +msgid "QR-code number {qr_code_number}" +msgstr "numéro du QR-code {qr_code_number}" + +#: apps/food/models.py:47 +msgid "Allergen" +msgstr "Allergène" + +#: apps/food/models.py:48 +msgid "Allergens" +msgstr "Allergènes" + +#: apps/food/models.py:64 +msgid "owner" +msgstr "propriétaire" + +#: apps/food/models.py:70 +msgid "allergen" +msgstr "allergène" + +#: apps/food/models.py:74 +msgid "expiry date" +msgstr "date de péremption" + +#: apps/food/models.py:80 +msgid "was eaten" +msgstr "a été mangé" + +#: apps/food/models.py:85 +msgid "is ready" +msgstr "est prêt" + +#: apps/food/models.py:97 +msgid "foods" +msgstr "bouffes" + +#: apps/food/models.py:113 +msgid "arrival date" +msgstr "date d'arrivée" + +#: apps/food/models.py:143 +msgid "Basic food" +msgstr "Bouffe basique" + +#: apps/food/models.py:144 +msgid "Basic foods" +msgstr "Bouffes basiques" + +#: apps/food/models.py:152 +msgid "creation date" +msgstr "date de création" + +#: apps/food/models.py:160 +msgid "transformed ingredient" +msgstr "ingrédients tranformées" + +#: apps/food/models.py:165 +msgid "is active" +msgstr "est en cours" + +#: apps/food/models.py:170 +msgid "shelf life" +msgstr "durée de vie" + +#: apps/food/models.py:216 apps/food/views.py:263 +msgid "Transformed food" +msgstr "Bouffe transformée" + +#: apps/food/models.py:217 +msgid "Transformed foods" +msgstr "Bouffes transformées" + +#: apps/food/views.py:25 +msgid "Add the ingredient" +msgstr "Ajouter un ingrédient" + +#: apps/food/views.py:39 +msgid "The product isn't ready" +msgstr "Le produit n'est pas prêt" + +#: apps/food/views.py:63 apps/food/views.py:86 +msgid "Add a new aliment" +msgstr "Ajouter un nouvel aliment" + +#: apps/food/views.py:94 +msgid "Details" +msgstr "Détails" + +#: apps/food/views.py:110 +msgid "Add a new basic food with QRCode" +msgstr "Ajouter un nouvel ingrédient avec un QR-code" + +#: apps/food/views.py:153 +msgid "Add a new QRCode" +msgstr "Ajouter un nouveau QR-code" + +#: apps/food/views.py:201 +msgid "QRCode" +msgstr "QR-code" + +#: apps/food/views.py:220 +msgid "Add a new meal" +msgstr "Ajouter un nouveau plat" + #: apps/logs/apps.py:11 msgid "Logs" msgstr "Logs" @@ -563,8 +702,8 @@ msgstr "Taille maximale : 2 Mo" msgid "This image cannot be loaded." msgstr "Cette image ne peut pas être chargée." -#: apps/member/forms.py:148 apps/member/views.py:102 -#: apps/registration/forms.py:34 apps/registration/views.py:276 +#: apps/member/forms.py:151 apps/member/views.py:102 +#: apps/registration/forms.py:33 apps/registration/views.py:276 msgid "An alias with a similar name already exists." msgstr "Un alias avec un nom similaire existe déjà." @@ -576,12 +715,12 @@ msgstr "Inscription payée par la Société générale" msgid "Check this case if the Société Générale paid the inscription." msgstr "Cochez cette case si la Société Générale a payé l'inscription." -#: apps/member/forms.py:182 apps/registration/forms.py:79 +#: apps/member/forms.py:182 apps/registration/forms.py:78 #: apps/wei/forms/registration.py:91 msgid "Credit type" msgstr "Type de rechargement" -#: apps/member/forms.py:183 apps/registration/forms.py:80 +#: apps/member/forms.py:183 apps/registration/forms.py:79 #: apps/wei/forms/registration.py:92 msgid "No credit" msgstr "Pas de rechargement" @@ -590,13 +729,13 @@ msgstr "Pas de rechargement" msgid "You can credit the note of the user." msgstr "Vous pouvez créditer la note de l'utilisateur·ice avant l'adhésion." -#: apps/member/forms.py:186 apps/registration/forms.py:85 +#: apps/member/forms.py:189 apps/registration/forms.py:84 #: apps/wei/forms/registration.py:97 msgid "Credit amount" msgstr "Montant à créditer" #: apps/member/forms.py:206 apps/note/templates/note/transaction_form.html:144 -#: apps/registration/forms.py:102 apps/treasury/forms.py:135 +#: apps/registration/forms.py:101 apps/treasury/forms.py:135 #: apps/wei/forms/registration.py:114 msgid "Bank" msgstr "Banque" @@ -755,8 +894,8 @@ msgid "" "Register on the mailing list to stay informed of the events of the campus (1 " "mail/week)" msgstr "" -"S'inscrire sur la liste de diffusion pour rester informé·e des événements sur " -"le campus (1 mail par semaine)" +"S'inscrire sur la liste de diffusion pour rester informé·e des événements " +"sur le campus (1 mail par semaine)" #: apps/member/models.py:108 msgid "" @@ -850,8 +989,8 @@ msgstr "fin de l'adhésion" #: apps/member/models.py:259 msgid "Maximal date of a membership, after which members must renew it." msgstr "" -"Date maximale d'une fin d'adhésion, après laquelle les adhérent·e·s doivent la " -"renouveler." +"Date maximale d'une fin d'adhésion, après laquelle les adhérent·e·s doivent " +"la renouveler." #: apps/member/models.py:263 msgid "add to registration form" @@ -892,11 +1031,11 @@ msgstr "Adhésion de {user} pour le club {club}" msgid "The role {role} does not apply to the club {club}." msgstr "Le rôle {role} ne s'applique pas au club {club}." -#: apps/member/models.py:381 apps/member/views.py:712 +#: apps/member/models.py:381 apps/member/views.py:715 msgid "User is already a member of the club" msgstr "L'utilisateur·ice est déjà membre du club" -#: apps/member/models.py:393 apps/member/views.py:721 +#: apps/member/models.py:393 apps/member/views.py:724 msgid "User is not a member of the parent club" msgstr "L'utilisateur·ice n'est pas membre du club parent" @@ -921,8 +1060,8 @@ msgid "" "The user is not a member of the club·s %(clubs)s. Please create the required " "memberships, otherwise it will fail." msgstr "" -"Cet·te utilisateur·ice n'est pas membre du/des club·s parent·s %(clubs)s. Merci de " -"d'abord créer l'adhésion requise, sinon cette adhésion va échouer." +"Cet·te utilisateur·ice n'est pas membre du/des club·s parent·s %(clubs)s. " +"Merci de d'abord créer l'adhésion requise, sinon cette adhésion va échouer." #: apps/member/templates/member/add_members.html:29 #, python-format @@ -939,8 +1078,9 @@ msgid "" "This club has parents %(clubs)s. Please make sure that the user is a member " "of this or these club·s, otherwise the creation of this membership will fail." msgstr "" -"Ce club a pour parents %(clubs)s. Merci de vous assurer que l'utilisateur·ice " -"est membre de ce·s club·s, sinon la création de cette adhésion va échouer." +"Ce club a pour parents %(clubs)s. Merci de vous assurer que " +"l'utilisateur·ice est membre de ce·s club·s, sinon la création de cette " +"adhésion va échouer." #: apps/member/templates/member/base.html:17 #: apps/registration/templates/registration/future_profile_detail.html:12 @@ -1010,7 +1150,7 @@ msgstr "" #: apps/member/templates/member/club_alias.html:10 #: apps/member/templates/member/profile_alias.html:10 apps/member/views.py:287 -#: apps/member/views.py:517 +#: apps/member/views.py:520 msgid "Note aliases" msgstr "Alias de la note" @@ -1211,7 +1351,7 @@ msgstr "Sauvegarder les changements" msgid "Registrations" msgstr "Inscriptions" -#: apps/member/views.py:72 apps/registration/forms.py:24 +#: apps/member/views.py:72 apps/registration/forms.py:23 msgid "This address must be valid." msgstr "Cette adresse doit être valide." @@ -1231,51 +1371,51 @@ msgstr "Amitiés note" msgid "Update note picture" msgstr "Modifier la photo de la note" -#: apps/member/views.py:354 +#: apps/member/views.py:357 msgid "Manage auth token" msgstr "Gérer les jetons d'authentification" -#: apps/member/views.py:381 +#: apps/member/views.py:384 msgid "Create new club" msgstr "Créer un nouveau club" -#: apps/member/views.py:400 +#: apps/member/views.py:403 msgid "Search club" msgstr "Chercher un club" -#: apps/member/views.py:433 +#: apps/member/views.py:436 msgid "Club detail" msgstr "Détails du club" -#: apps/member/views.py:540 +#: apps/member/views.py:543 msgid "Update club" msgstr "Modifier le club" -#: apps/member/views.py:574 +#: apps/member/views.py:577 msgid "Add new member to the club" msgstr "Ajouter un·e nouvelle·au membre au club" -#: apps/member/views.py:703 apps/wei/views.py:973 +#: apps/member/views.py:706 apps/wei/views.py:973 msgid "" "This user don't have enough money to join this club, and can't have a " "negative balance." msgstr "" -"Cet·te utilisateur·ice n'a pas assez d'argent pour rejoindre ce club et ne peut pas " -"avoir un solde négatif." +"Cet·te utilisateur·ice n'a pas assez d'argent pour rejoindre ce club et ne " +"peut pas avoir un solde négatif." -#: apps/member/views.py:725 +#: apps/member/views.py:728 msgid "The membership must start after {:%m-%d-%Y}." msgstr "L'adhésion doit commencer après le {:%d/%m/%Y}." -#: apps/member/views.py:730 +#: apps/member/views.py:733 msgid "The membership must begin before {:%m-%d-%Y}." msgstr "L'adhésion doit commencer avant le {:%d/%m/%Y}." -#: apps/member/views.py:880 +#: apps/member/views.py:883 msgid "Manage roles of an user in the club" msgstr "Gérer les rôles d'un·e utilisateur·ice dans le club" -#: apps/member/views.py:905 +#: apps/member/views.py:908 msgid "Members of the club" msgstr "Membres du club" @@ -1384,8 +1524,8 @@ msgstr "" #: apps/note/models/notes.py:70 msgid "The note is blocked by the the BDE and can't be manually reactivated." msgstr "" -"La note est bloquée de force par le BDE et ne peut pas être débloquée par le·a " -"possesseur·ice de la note." +"La note est bloquée de force par le BDE et ne peut pas être débloquée par " +"le·a possesseur·ice de la note." #: apps/note/models/notes.py:78 msgid "notes" @@ -1941,7 +2081,8 @@ msgstr "Liste des utilisateur·ice·s ayant des droits surnormaux" #: apps/permission/templates/permission/all_rights.html:16 msgid "Superusers have all rights on everything, to manage the website." msgstr "" -"Les super-utilisateur·ice·s ont tous les droits sur tout, afin de gérer le site." +"Les super-utilisateur·ice·s ont tous les droits sur tout, afin de gérer le " +"site." #: apps/permission/templates/permission/all_rights.html:21 msgid "Superusers" @@ -2012,7 +2153,7 @@ msgstr "" "Vous n'avez pas la permission d'ajouter une instance du modèle « {model} » " "avec ces paramètres. Merci de les corriger et de réessayer." -#: apps/permission/views.py:112 note_kfet/templates/base.html:108 +#: apps/permission/views.py:112 note_kfet/templates/base.html:114 msgid "Rights" msgstr "Droits" @@ -2024,15 +2165,15 @@ msgstr "Tous les droits" msgid "registration" msgstr "inscription" -#: apps/registration/forms.py:40 +#: apps/registration/forms.py:39 msgid "This email address is already used." msgstr "Cet email est déjà pris." -#: apps/registration/forms.py:60 +#: apps/registration/forms.py:59 msgid "Register to the WEI" msgstr "S'inscrire au WEI" -#: apps/registration/forms.py:62 +#: apps/registration/forms.py:61 msgid "" "Check this case if you want to register to the WEI. If you hesitate, you " "will be able to register later, after validating your account in the Kfet." @@ -2041,11 +2182,11 @@ msgstr "" "pourrez toujours vous inscrire plus tard, après avoir validé votre compte à " "la Kfet." -#: apps/registration/forms.py:107 +#: apps/registration/forms.py:106 msgid "Join BDE Club" msgstr "Adhérer au club BDE" -#: apps/registration/forms.py:114 +#: apps/registration/forms.py:113 msgid "Join Kfet Club" msgstr "Adhérer au club Kfet" @@ -2100,7 +2241,8 @@ msgstr "Valider le compte" #: apps/registration/templates/registration/future_profile_detail.html:63 msgid "" "The user declared that he/she opened a bank account in the Société générale." -msgstr "L'utilisateur·ice a déclaré avoir ouvert un compte à la société générale." +msgstr "" +"L'utilisateur·ice a déclaré avoir ouvert un compte à la société générale." #: apps/registration/templates/registration/future_profile_detail.html:73 #: apps/wei/templates/wei/weimembership_form.html:127 @@ -2152,39 +2294,39 @@ msgstr "Merci" msgid "The Note Kfet team." msgstr "L'équipe de la Note Kfet." -#: apps/registration/views.py:41 +#: apps/registration/views.py:42 msgid "Register new user" msgstr "Enregistrer un·e nouvel·le utilisateur·ice" -#: apps/registration/views.py:99 +#: apps/registration/views.py:100 msgid "Email validation" msgstr "Validation de l'adresse mail" -#: apps/registration/views.py:101 +#: apps/registration/views.py:102 msgid "Validate email" msgstr "Valider l'adresse e-mail" -#: apps/registration/views.py:145 +#: apps/registration/views.py:146 msgid "Email validation unsuccessful" msgstr "La validation de l'adresse mail a échoué" -#: apps/registration/views.py:156 +#: apps/registration/views.py:157 msgid "Email validation email sent" msgstr "L'email de vérification de l'adresse email a bien été envoyé" -#: apps/registration/views.py:164 +#: apps/registration/views.py:165 msgid "Resend email validation link" msgstr "Renvoyer le lien de validation" -#: apps/registration/views.py:182 +#: apps/registration/views.py:183 msgid "Pre-registered users list" msgstr "Liste des utilisateur·ice·s en attente d'inscription" -#: apps/registration/views.py:206 +#: apps/registration/views.py:207 msgid "Unregistered users" msgstr "Utilisateur·ice·s en attente d'inscription" -#: apps/registration/views.py:219 +#: apps/registration/views.py:220 msgid "Registration detail" msgstr "Détails de l'inscription" @@ -2208,7 +2350,7 @@ msgstr "" msgid "Invalidate pre-registration" msgstr "Invalider l'inscription" -#: apps/treasury/apps.py:12 note_kfet/templates/base.html:96 +#: apps/treasury/apps.py:12 note_kfet/templates/base.html:102 msgid "Treasury" msgstr "Trésorerie" @@ -2377,8 +2519,9 @@ msgid "" "This user doesn't have enough money to pay the memberships with its note. " "Please ask her/him to credit the note before invalidating this credit." msgstr "" -"Cet·te utilisateur·ice n'a pas assez d'argent pour payer les adhésions avec sa " -"note. Merci de lui demander de recharger sa note avant d'invalider ce crédit." +"Cet·te utilisateur·ice n'a pas assez d'argent pour payer les adhésions avec " +"sa note. Merci de lui demander de recharger sa note avant d'invalider ce " +"crédit." #: apps/treasury/tables.py:20 msgid "Invoice #{:d}" @@ -2529,12 +2672,13 @@ msgid "" "If this credit is validated, then the user won't be able to ask for a credit " "from the Société générale." msgstr "" -"Si ce crédit est validé, alors l'utilisateur·ice ne pourra plus demander d'être " -"crédité·e par la Société générale à l'avenir." +"Si ce crédit est validé, alors l'utilisateur·ice ne pourra plus demander " +"d'être crédité·e par la Société générale à l'avenir." #: apps/treasury/templates/treasury/sogecredit_detail.html:44 msgid "If you think there is an error, please contact the \"respos info\"." -msgstr "Si vous pensez qu'il y a une erreur, merci de contacter un·e respo info." +msgstr "" +"Si vous pensez qu'il y a une erreur, merci de contacter un·e respo info." #: apps/treasury/templates/treasury/sogecredit_detail.html:50 msgid "This credit is already validated." @@ -2545,14 +2689,14 @@ msgid "" "Warning: if you don't validate this credit, the note of the user doesn't " "have enough money to pay its memberships." msgstr "" -"Attention : si vous ne validez pas ce crédit, la note de l'utilisateur·ice n'a " -"pas assez d'argent pour payer les adhésions." +"Attention : si vous ne validez pas ce crédit, la note de l'utilisateur·ice " +"n'a pas assez d'argent pour payer les adhésions." #: apps/treasury/templates/treasury/sogecredit_detail.html:56 msgid "Please ask the user to credit its note before deleting this credit." msgstr "" -"Merci de demander à l'utilisateur·ice de recharger sa note avant de supprimer la " -"demande de crédit." +"Merci de demander à l'utilisateur·ice de recharger sa note avant de " +"supprimer la demande de crédit." #: apps/treasury/templates/treasury/sogecredit_detail.html:63 #: apps/wei/tables.py:60 apps/wei/tables.py:102 @@ -2570,8 +2714,8 @@ msgstr "Filtrer avec uniquement les crédits non valides" #: apps/treasury/templates/treasury/sogecredit_list.html:50 msgid "There is no matched user that have asked for a Société générale credit." msgstr "" -"Il n'y a pas d'utilisateur·ice trouvé·e ayant demandé un crédit de la Société " -"générale." +"Il n'y a pas d'utilisateur·ice trouvé·e ayant demandé un crédit de la " +"Société générale." #: apps/treasury/templates/treasury/sogecredit_list.html:63 msgid "Add credit from the Société générale" @@ -2624,15 +2768,15 @@ msgstr "Gérer les crédits de la Société générale" #: apps/wei/apps.py:10 apps/wei/models.py:37 apps/wei/models.py:38 #: apps/wei/models.py:62 apps/wei/models.py:178 -#: note_kfet/templates/base.html:102 +#: note_kfet/templates/base.html:108 msgid "WEI" msgstr "WEI" #: apps/wei/forms/registration.py:35 msgid "The selected user is not validated. Please validate its account first" msgstr "" -"L'utilisateur·ice sélectionné·e n'est pas validé·e. Merci de d'abord valider son " -"compte" +"L'utilisateur·ice sélectionné·e n'est pas validé·e. Merci de d'abord valider " +"son compte" #: apps/wei/forms/registration.py:59 apps/wei/models.py:126 #: apps/wei/models.py:324 @@ -2644,9 +2788,9 @@ msgid "" "This choice is not definitive. The WEI organizers are free to attribute for " "you a bus and a team, in particular if you are a free eletron." msgstr "" -"Ce choix n'est pas définitif. Les organisateur·ice·s du WEI sont libres de vous " -"attribuer un bus et une équipe, en particulier si vous êtes un·e électron " -"libre." +"Ce choix n'est pas définitif. Les organisateur·ice·s du WEI sont libres de " +"vous attribuer un bus et une équipe, en particulier si vous êtes un·e " +"électron libre." #: apps/wei/forms/registration.py:67 msgid "Team" @@ -3244,19 +3388,19 @@ msgstr "Répartir les 1A dans les bus" msgid "Attribute bus" msgstr "Attribuer un bus" -#: note_kfet/settings/base.py:172 +#: note_kfet/settings/base.py:173 msgid "German" msgstr "Allemand" -#: note_kfet/settings/base.py:173 +#: note_kfet/settings/base.py:174 msgid "English" msgstr "Anglais" -#: note_kfet/settings/base.py:174 +#: note_kfet/settings/base.py:175 msgid "Spanish" msgstr "Espagnol" -#: note_kfet/settings/base.py:175 +#: note_kfet/settings/base.py:176 msgid "French" msgstr "Français" @@ -3321,34 +3465,38 @@ msgstr "Réinitialiser" msgid "The ENS Paris-Saclay BDE note." msgstr "La note du BDE de l'ENS Paris-Saclay." -#: note_kfet/templates/base.html:78 +#: note_kfet/templates/base.html:72 +msgid "Food" +msgstr "Bouffe" + +#: note_kfet/templates/base.html:84 msgid "Users" msgstr "Utilisateur·ice·s" -#: note_kfet/templates/base.html:84 +#: note_kfet/templates/base.html:90 msgid "Clubs" msgstr "Clubs" -#: note_kfet/templates/base.html:113 +#: note_kfet/templates/base.html:119 msgid "Admin" msgstr "Admin" -#: note_kfet/templates/base.html:127 +#: note_kfet/templates/base.html:133 msgid "My account" msgstr "Mon compte" -#: note_kfet/templates/base.html:130 +#: note_kfet/templates/base.html:136 msgid "Log out" msgstr "Se déconnecter" -#: note_kfet/templates/base.html:138 +#: note_kfet/templates/base.html:144 #: note_kfet/templates/registration/signup.html:6 #: note_kfet/templates/registration/signup.html:11 #: note_kfet/templates/registration/signup.html:28 msgid "Sign up" msgstr "Inscription" -#: note_kfet/templates/base.html:145 +#: note_kfet/templates/base.html:151 #: note_kfet/templates/registration/login.html:6 #: note_kfet/templates/registration/login.html:15 #: note_kfet/templates/registration/login.html:38 @@ -3356,15 +3504,15 @@ msgstr "Inscription" msgid "Log in" msgstr "Se connecter" -#: note_kfet/templates/base.html:159 +#: note_kfet/templates/base.html:165 msgid "" "You are not a BDE member anymore. Please renew your membership if you want " "to use the note." msgstr "" -"Vous n'êtes plus adhérent·e BDE. Merci de réadhérer si vous voulez profiter de " -"la note." +"Vous n'êtes plus adhérent·e BDE. Merci de réadhérer si vous voulez profiter " +"de la note." -#: note_kfet/templates/base.html:165 +#: note_kfet/templates/base.html:171 msgid "" "Your e-mail address is not validated. Please check your mail inbox and click " "on the validation link." @@ -3372,7 +3520,7 @@ msgstr "" "Votre adresse e-mail n'est pas validée. Merci de vérifier votre boîte mail " "et de cliquer sur le lien de validation." -#: note_kfet/templates/base.html:171 +#: note_kfet/templates/base.html:177 msgid "" "You declared that you opened a bank account in the Société générale. The " "bank did not validate the creation of the account to the BDE, so the " @@ -3386,22 +3534,22 @@ msgstr "" "vérification peut durer quelques jours. Merci de vous assurer de bien aller " "au bout de vos démarches." -#: note_kfet/templates/base.html:194 +#: note_kfet/templates/base.html:200 msgid "Contact us" msgstr "Nous contacter" -#: note_kfet/templates/base.html:196 +#: note_kfet/templates/base.html:202 msgid "Technical Support" msgstr "Support technique" -#: note_kfet/templates/base.html:198 -msgid "FAQ (FR)" -msgstr "FAQ (FR)" - -#: note_kfet/templates/base.html:200 +#: note_kfet/templates/base.html:204 msgid "Charte Info (FR)" msgstr "Charte Info (FR)" +#: note_kfet/templates/base.html:206 +msgid "FAQ (FR)" +msgstr "FAQ (FR)" + #: note_kfet/templates/base_search.html:15 msgid "Search by attribute such as name…" msgstr "Chercher par un attribut tel que le nom …" diff --git a/note_kfet/templates/base.html b/note_kfet/templates/base.html index 6a7721b2..15743018 100644 --- a/note_kfet/templates/base.html +++ b/note_kfet/templates/base.html @@ -66,14 +66,16 @@ SPDX-License-Identifier: GPL-3.0-or-later {% trans 'Consumptions' %} {% endif %} - + {% if request.user.is_authenticated %} + + {% endif %} {% if user.is_authenticated and user|is_member:"Kfet" %} {% endif %} {% if "auth.user"|model_list_length >= 2 %} From b2b1f03b46335cde4e2773c285d466c4b9395a80 Mon Sep 17 00:00:00 2001 From: quark Date: Tue, 6 Aug 2024 15:20:22 +0200 Subject: [PATCH 22/29] Edit food HTML template for translation, translations. Now the mandatory allergens are automatically created --- .../0003_create_14_allergens_mandatory.py | 62 ++++ .../food/templates/food/basicfood_detail.html | 12 +- apps/food/templates/food/qrcode_detail.html | 28 +- .../food/transformedfood_detail.html | 46 +-- .../templates/food/transformedfood_list.html | 26 +- locale/de/LC_MESSAGES/django.po | 279 +++++++++++++++- locale/es/LC_MESSAGES/django.po | 304 +++++++++++++++++- locale/fr/LC_MESSAGES/django.po | 290 ++++++++++++++++- 8 files changed, 964 insertions(+), 83 deletions(-) create mode 100644 apps/food/migrations/0003_create_14_allergens_mandatory.py diff --git a/apps/food/migrations/0003_create_14_allergens_mandatory.py b/apps/food/migrations/0003_create_14_allergens_mandatory.py new file mode 100644 index 00000000..236eaea4 --- /dev/null +++ b/apps/food/migrations/0003_create_14_allergens_mandatory.py @@ -0,0 +1,62 @@ +from django.db import migrations + +def create_14_mandatory_allergens(apps, schema_editor): + """ + There are 14 mandatory allergens, they are pre-injected + """ + + Allergen = apps.get_model("food", "allergen") + + Allergen.objects.get_or_create( + name="Gluten", + ) + Allergen.objects.get_or_create( + name="Fruits à coques", + ) + Allergen.objects.get_or_create( + name="Crustacés", + ) + Allergen.objects.get_or_create( + name="Céléri", + ) + Allergen.objects.get_or_create( + name="Oeufs", + ) + Allergen.objects.get_or_create( + name="Moutarde", + ) + Allergen.objects.get_or_create( + name="Poissons", + ) + Allergen.objects.get_or_create( + name="Soja", + ) + Allergen.objects.get_or_create( + name="Lait", + ) + Allergen.objects.get_or_create( + name="Sulfites", + ) + Allergen.objects.get_or_create( + name="Sésame", + ) + Allergen.objects.get_or_create( + name="Lupin", + ) + Allergen.objects.get_or_create( + name="Arachides", + ) + Allergen.objects.get_or_create( + name="Mollusques", + ) + +class Migration(migrations.Migration): + dependencies = [ + ('food', '0002_transformedfood_shelf_life'), + ] + + operations = [ + migrations.RunPython(create_14_mandatory_allergens), + ] + + diff --git a/apps/food/templates/food/basicfood_detail.html b/apps/food/templates/food/basicfood_detail.html index 8daefaf8..b890a93d 100644 --- a/apps/food/templates/food/basicfood_detail.html +++ b/apps/food/templates/food/basicfood_detail.html @@ -11,17 +11,17 @@ SPDX-License-Identifier: GPL-3.0-or-later {{ title }}
    -

    name : {{ food.name }}

    -

    owner : {{ food.owner }}

    -

    arrival_date : {{ food.arrival_date }}

    -

    expiry_date : {{ food.expiry_date }}

    -

    allergens :

    +

    {% trans 'Name' %} : {{ food.name }}

    +

    {% trans 'Owner' %} : {{ food.owner }}

    +

    {% trans 'Arrival date' %} : {{ food.arrival_date }}

    +

    {% trans 'Expiry date' %} : {{ food.expiry_date }}

    +

    {% trans 'Allergens' %} :

      {% for allergen in food.allergens.iterator %}
    • {{ allergen.name }}
    • {% endfor %}
    - Update + {% trans 'Update' %}
{% endblock %} diff --git a/apps/food/templates/food/qrcode_detail.html b/apps/food/templates/food/qrcode_detail.html index c74dd0e3..def3a028 100644 --- a/apps/food/templates/food/qrcode_detail.html +++ b/apps/food/templates/food/qrcode_detail.html @@ -6,19 +6,19 @@ SPDX-License-Identifier: GPL-3.0-or-later {% block content %}
-

- HTML not finished
- {{ title }} -

-
-

qrcode : {{ qrcode.qr_code_number }}

-

name : {{ qrcode.food_container.name }}

- {% if qrcode.food_container.polymorphic_ctype.name == 'Basic food' %} - Update - {% else %} - Update - {% endif %} - Add the ingrdient -
+

+ HTML not finished
+ {{ title }} +

+
+

{% trans 'QR-code' %} : {{ qrcode.qr_code_number }}

+

{% trans 'Name' %} : {{ qrcode.food_container.name }}

+ {% if qrcode.food_container.polymorphic_ctype.name == 'Basic food' %} + {% trans 'Update' %} + {% else %} + {% trans 'Update' %} + {% endif %} + {% trans 'Add the ingredient' %} +
{% endblock %} diff --git a/apps/food/templates/food/transformedfood_detail.html b/apps/food/templates/food/transformedfood_detail.html index 2ac87989..5ffb2cc0 100644 --- a/apps/food/templates/food/transformedfood_detail.html +++ b/apps/food/templates/food/transformedfood_detail.html @@ -6,28 +6,28 @@ SPDX-License-Identifier: GPL-3.0-or-later {% block content %}
-

- HTML not finished
- {{ title }} -

-
-

name : {{ food.name }}

-

owner : {{ food.owner }}

-

creation_date : {{ food.creation_date }}

-

expiry_date : {{ food.expiry_date }}

-

allergens :

-
    - {% for allergen in food.allergens.iterator %} -
  • {{ allergen.name }}
  • - {% endfor %} -
-

ingredients :

- - Update -
+

+ HTML not finished
+ {{ title }} +

+
+

{% trans 'Name' %} : {{ food.name }}

+

{% trans 'Owner' %} : {{ food.owner }}

+

{% trans 'Creation date' %} : {{ food.creation_date }}

+

{% trans 'Expiry date' %} : {{ food.expiry_date }}

+

{% trans 'Allergens' %} :

+
    + {% for allergen in food.allergens.iterator %} +
  • {{ allergen.name }}
  • + {% endfor %} +
+

{% trans 'Ingredients' %} :

+ + {% trans 'Update' %} +
{% endblock %} diff --git a/apps/food/templates/food/transformedfood_list.html b/apps/food/templates/food/transformedfood_list.html index 98d89aa4..2c4cb93d 100644 --- a/apps/food/templates/food/transformedfood_list.html +++ b/apps/food/templates/food/transformedfood_list.html @@ -7,18 +7,18 @@ SPDX-License-Identifier: GPL-3.0-or-later {% block content %}
- -

- In preparation -

- {% render_table table %} -

- Open -

- {% render_table open_table %} + +

+ {% trans 'In preparation' %} +

+ {% render_table table %} +

+ {% trans 'Free' %} +

+ {% render_table open_table %}
{% endblock %} diff --git a/locale/de/LC_MESSAGES/django.po b/locale/de/LC_MESSAGES/django.po index 538aff43..91be9609 100644 --- a/locale/de/LC_MESSAGES/django.po +++ b/locale/de/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-08-06 13:39+0200\n" +"POT-Creation-Date: 2024-08-06 14:46+0200\n" "PO-Revision-Date: 2020-11-16 20:02+0000\n" "Last-Translator: bleizi \n" "Language-Team: German \n" @@ -467,7 +467,7 @@ msgstr "Telefonnummer" msgid "food container" msgstr "" -#: apps/food/models.py:30 +#: apps/food/models.py:30 apps/food/templates/food/qrcode_detail.html:14 msgid "QR-code" msgstr "" @@ -484,7 +484,8 @@ msgstr "" msgid "Allergen" msgstr "" -#: apps/food/models.py:48 +#: apps/food/models.py:48 apps/food/templates/food/basicfood_detail.html:18 +#: apps/food/templates/food/transformedfood_detail.html:18 msgid "Allergens" msgstr "" @@ -558,10 +559,73 @@ msgstr "Geld überweisen" msgid "Transformed foods" msgstr "" -#: apps/food/views.py:25 +#: apps/food/templates/food/basicfood_detail.html:14 +#: apps/food/templates/food/qrcode_detail.html:15 +#: apps/food/templates/food/transformedfood_detail.html:14 +#: apps/note/templates/note/transaction_form.html:132 +#: apps/treasury/models.py:60 +msgid "Name" +msgstr "Name" + +#: apps/food/templates/food/basicfood_detail.html:15 +#: apps/food/templates/food/transformedfood_detail.html:15 +#, fuzzy +#| msgid "Owned" +msgid "Owner" +msgstr "Besetzt" + +#: apps/food/templates/food/basicfood_detail.html:16 +#, fuzzy +#| msgid "start date" +msgid "Arrival date" +msgstr "Anfangsdatum" + +#: apps/food/templates/food/basicfood_detail.html:17 +#: apps/food/templates/food/transformedfood_detail.html:17 +#, fuzzy +#| msgid "birth date" +msgid "Expiry date" +msgstr "Geburtsdatum" + +#: apps/food/templates/food/basicfood_detail.html:24 +#: apps/food/templates/food/qrcode_detail.html:17 +#: apps/food/templates/food/qrcode_detail.html:19 +#: apps/food/templates/food/transformedfood_detail.html:30 +#, fuzzy +#| msgid "Update bus" +msgid "Update" +msgstr "Bus bearbeiten" + +#: apps/food/templates/food/qrcode_detail.html:21 apps/food/views.py:25 msgid "Add the ingredient" msgstr "" +#: apps/food/templates/food/transformedfood_detail.html:16 +#, fuzzy +#| msgid "created at" +msgid "Creation date" +msgstr "erschafft am" + +#: apps/food/templates/food/transformedfood_detail.html:24 +msgid "Ingredients" +msgstr "" + +#: apps/food/templates/food/transformedfood_list.html:12 +#, fuzzy +#| msgid "New user" +msgid "New meal" +msgstr "Neue User" + +#: apps/food/templates/food/transformedfood_list.html:16 +#, fuzzy +#| msgid "WEI registration" +msgid "In preparation" +msgstr "WEI Registrierung" + +#: apps/food/templates/food/transformedfood_list.html:20 +msgid "Free" +msgstr "" + #: apps/food/views.py:39 msgid "The product isn't ready" msgstr "" @@ -1904,11 +1968,6 @@ msgstr "Aktion" msgid "Amount" msgstr "Anzahl" -#: apps/note/templates/note/transaction_form.html:132 -#: apps/treasury/models.py:60 -msgid "Name" -msgstr "Name" - #: apps/note/templates/note/transaction_form.html:177 msgid "Select emitter" msgstr "Sender auswählen" @@ -3848,6 +3907,208 @@ msgstr "" "müssen Ihre E-Mail-Adresse auch überprüfen, indem Sie dem Link folgen, den " "Sie erhalten haben." +#, fuzzy +#~| msgid "Transfer money" +#~ msgid "New transformed food" +#~ msgstr "Geld überweisen" + +#, fuzzy +#~| msgid "Invitation" +#~ msgid "Syndication" +#~ msgstr "Einladung" + +#, fuzzy +#~| msgid "There is no results." +#~ msgid "That page contains no results" +#~ msgstr "Es gibt keine Ergebnisse." + +#, fuzzy +#~| msgid "phone number" +#~ msgid "Enter a number." +#~ msgstr "Telefonnummer" + +#, fuzzy +#~| msgid "add" +#~ msgid "and" +#~ msgstr "hinzufügen" + +#, fuzzy, python-format +#~| msgid "A template with this name already exist" +#~ msgid "%(model_name)s with this %(field_labels)s already exists." +#~ msgstr "Eine Vorlage mit diesem Namen ist bereits vorhanden" + +#, fuzzy +#~| msgid "This image cannot be loaded." +#~ msgid "This field cannot be null." +#~ msgstr "Dieses Bild kann nicht geladen werden." + +#, fuzzy +#~| msgid "This image cannot be loaded." +#~ msgid "This field cannot be blank." +#~ msgstr "Dieses Bild kann nicht geladen werden." + +#, fuzzy, python-format +#~| msgid "A template with this name already exist" +#~ msgid "%(model_name)s with this %(field_label)s already exists." +#~ msgstr "Eine Vorlage mit diesem Namen ist bereits vorhanden" + +#, fuzzy +#~| msgid "phone number" +#~ msgid "Decimal number" +#~ msgstr "Telefonnummer" + +#, fuzzy +#~| msgid "action" +#~ msgid "Duration" +#~ msgstr "Aktion" + +#, fuzzy +#~| msgid "address" +#~ msgid "Email address" +#~ msgstr "Adresse" + +#, fuzzy +#~| msgid "phone number" +#~ msgid "Floating point number" +#~ msgstr "Telefonnummer" + +#, fuzzy +#~| msgid "IP Address" +#~ msgid "IPv4 address" +#~ msgstr "IP Adresse" + +#, fuzzy +#~| msgid "IP Address" +#~ msgid "IP address" +#~ msgstr "IP Adresse" + +#, fuzzy +#~| msgid "Invoice identifier" +#~ msgid "Universally unique identifier" +#~ msgstr "Rechnungskennung" + +#, fuzzy +#~| msgid "phone number" +#~ msgid "Enter a whole number." +#~ msgstr "Telefonnummer" + +#, fuzzy +#~| msgid "Email validation" +#~ msgid "Enter a valid duration." +#~ msgstr "Email validierung" + +#, fuzzy +#~| msgid "Current activity" +#~ msgid "Currently" +#~ msgstr "Aktuelle Veranstaltung" + +#, fuzzy +#~| msgid "change" +#~ msgid "Change" +#~ msgstr "bearbeiten" + +#, fuzzy +#~| msgid "Search WEI" +#~ msgid "March" +#~ msgstr "WEI finden" + +#, fuzzy +#~| msgid "member" +#~ msgid "September" +#~ msgstr "Mitglied" + +#, fuzzy +#~| msgid "member" +#~ msgid "November" +#~ msgstr "Mitglied" + +#, fuzzy +#~| msgid "member" +#~ msgid "December" +#~ msgstr "Mitglied" + +#, fuzzy +#~| msgid "fee" +#~ msgid "feb" +#~ msgstr "Preis" + +#, fuzzy +#~| msgid "product" +#~ msgid "oct" +#~ msgstr "Produkt" + +#, fuzzy +#~| msgid "Search WEI" +#~ msgctxt "abbrev. month" +#~ msgid "March" +#~ msgstr "WEI finden" + +#, fuzzy +#~| msgid "Search WEI" +#~ msgctxt "alt. month" +#~ msgid "March" +#~ msgstr "WEI finden" + +#, fuzzy +#~| msgid "member" +#~ msgctxt "alt. month" +#~ msgid "September" +#~ msgstr "Mitglied" + +#, fuzzy +#~| msgid "member" +#~ msgctxt "alt. month" +#~ msgid "November" +#~ msgstr "Mitglied" + +#, fuzzy +#~| msgid "member" +#~ msgctxt "alt. month" +#~ msgid "December" +#~ msgstr "Mitglied" + +#, fuzzy +#~| msgid "This activity is not validated yet." +#~ msgid "This is not a valid IPv6 address." +#~ msgstr "Diese Veranstaltung ist noch nicht bestätigt." + +#, fuzzy, python-format +#~| msgid "year" +#~ msgid "%d year" +#~ msgid_plural "%d years" +#~ msgstr[0] "Jahr" +#~ msgstr[1] "Jahr" + +#, fuzzy +#~| msgid "No reason specified" +#~ msgid "No year specified" +#~ msgstr "Kein Grund gegeben" + +#, fuzzy +#~| msgid "No reason specified" +#~ msgid "No month specified" +#~ msgstr "Kein Grund gegeben" + +#, fuzzy +#~| msgid "No reason specified" +#~ msgid "No day specified" +#~ msgstr "Kein Grund gegeben" + +#, fuzzy +#~| msgid "No reason specified" +#~ msgid "No week specified" +#~ msgstr "Kein Grund gegeben" + +#, fuzzy +#~| msgid "Reset my password" +#~ msgid "Resource owner password-based" +#~ msgstr "Mein Passwort zurücksetzen" + +#, fuzzy +#~| msgid "This address must be valid." +#~ msgid "The access token is invalid." +#~ msgstr "Diese Adresse muss gültig sein." + #~ msgid "Join BDA Club" #~ msgstr "BDA Mitglieder werden" diff --git a/locale/es/LC_MESSAGES/django.po b/locale/es/LC_MESSAGES/django.po index ec3baf3b..a6b18b6e 100644 --- a/locale/es/LC_MESSAGES/django.po +++ b/locale/es/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-08-06 13:39+0200\n" +"POT-Creation-Date: 2024-08-06 14:46+0200\n" "PO-Revision-Date: 2022-04-11 23:12+0200\n" "Last-Translator: bleizi \n" "Language-Team: \n" @@ -464,7 +464,7 @@ msgstr "número de teléfono" msgid "food container" msgstr "" -#: apps/food/models.py:30 +#: apps/food/models.py:30 apps/food/templates/food/qrcode_detail.html:14 msgid "QR-code" msgstr "" @@ -481,7 +481,8 @@ msgstr "" msgid "Allergen" msgstr "" -#: apps/food/models.py:48 +#: apps/food/models.py:48 apps/food/templates/food/basicfood_detail.html:18 +#: apps/food/templates/food/transformedfood_detail.html:18 msgid "Allergens" msgstr "" @@ -555,10 +556,73 @@ msgstr "Transferir dinero" msgid "Transformed foods" msgstr "" -#: apps/food/views.py:25 +#: apps/food/templates/food/basicfood_detail.html:14 +#: apps/food/templates/food/qrcode_detail.html:15 +#: apps/food/templates/food/transformedfood_detail.html:14 +#: apps/note/templates/note/transaction_form.html:132 +#: apps/treasury/models.py:60 +msgid "Name" +msgstr "Nombre" + +#: apps/food/templates/food/basicfood_detail.html:15 +#: apps/food/templates/food/transformedfood_detail.html:15 +#, fuzzy +#| msgid "Owned" +msgid "Owner" +msgstr "Tenido" + +#: apps/food/templates/food/basicfood_detail.html:16 +#, fuzzy +#| msgid "invalidate" +msgid "Arrival date" +msgstr "invalidar" + +#: apps/food/templates/food/basicfood_detail.html:17 +#: apps/food/templates/food/transformedfood_detail.html:17 +#, fuzzy +#| msgid "birth date" +msgid "Expiry date" +msgstr "fecha de nacimiento" + +#: apps/food/templates/food/basicfood_detail.html:24 +#: apps/food/templates/food/qrcode_detail.html:17 +#: apps/food/templates/food/qrcode_detail.html:19 +#: apps/food/templates/food/transformedfood_detail.html:30 +#, fuzzy +#| msgid "Update bus" +msgid "Update" +msgstr "Modificar el bus" + +#: apps/food/templates/food/qrcode_detail.html:21 apps/food/views.py:25 msgid "Add the ingredient" msgstr "" +#: apps/food/templates/food/transformedfood_detail.html:16 +#, fuzzy +#| msgid "created at" +msgid "Creation date" +msgstr "creada el" + +#: apps/food/templates/food/transformedfood_detail.html:24 +msgid "Ingredients" +msgstr "" + +#: apps/food/templates/food/transformedfood_list.html:12 +#, fuzzy +#| msgid "New user" +msgid "New meal" +msgstr "Nuevo usuario" + +#: apps/food/templates/food/transformedfood_list.html:16 +#, fuzzy +#| msgid "WEI registration" +msgid "In preparation" +msgstr "Apuntación al WEI" + +#: apps/food/templates/food/transformedfood_list.html:20 +msgid "Free" +msgstr "" + #: apps/food/views.py:39 msgid "The product isn't ready" msgstr "" @@ -1888,11 +1952,6 @@ msgstr "Acción" msgid "Amount" msgstr "Monto" -#: apps/note/templates/note/transaction_form.html:132 -#: apps/treasury/models.py:60 -msgid "Name" -msgstr "Nombre" - #: apps/note/templates/note/transaction_form.html:177 msgid "Select emitter" msgstr "Elegir el remitente" @@ -3772,6 +3831,233 @@ msgstr "" "pagar su afiliación. Tambien tiene que validar su correo electronico con el " "enlace que recibió." +#, fuzzy +#~| msgid "Transfer money" +#~ msgid "New transformed food" +#~ msgstr "Transferir dinero" + +#, fuzzy +#~| msgid "Invitation" +#~ msgid "Syndication" +#~ msgstr "Invitación" + +#, fuzzy +#~| msgid "There is no results." +#~ msgid "That page contains no results" +#~ msgstr "No hay resultado." + +#, fuzzy +#~| msgid "phone number" +#~ msgid "Enter a number." +#~ msgstr "número de teléfono" + +#, fuzzy +#~| msgid "add" +#~ msgid "and" +#~ msgstr "añadir" + +#, fuzzy, python-format +#~| msgid "A template with this name already exist" +#~ msgid "%(model_name)s with this %(field_labels)s already exists." +#~ msgstr "Un plantilla de transacción con un nombre similar ya existe" + +#, fuzzy +#~| msgid "This image cannot be loaded." +#~ msgid "This field cannot be null." +#~ msgstr "Esta imagen no puede ser cargada." + +#, fuzzy +#~| msgid "This image cannot be loaded." +#~ msgid "This field cannot be blank." +#~ msgstr "Esta imagen no puede ser cargada." + +#, fuzzy, python-format +#~| msgid "A template with this name already exist" +#~ msgid "%(model_name)s with this %(field_label)s already exists." +#~ msgstr "Un plantilla de transacción con un nombre similar ya existe" + +#, fuzzy +#~| msgid "phone number" +#~ msgid "Decimal number" +#~ msgstr "número de teléfono" + +#, fuzzy +#~| msgid "action" +#~ msgid "Duration" +#~ msgstr "acción" + +#, fuzzy +#~| msgid "address" +#~ msgid "Email address" +#~ msgstr "dirección" + +#, fuzzy +#~| msgid "phone number" +#~ msgid "Floating point number" +#~ msgstr "número de teléfono" + +#, fuzzy +#~| msgid "IP Address" +#~ msgid "IPv4 address" +#~ msgstr "Dirección IP" + +#, fuzzy +#~| msgid "IP Address" +#~ msgid "IP address" +#~ msgstr "Dirección IP" + +#, fuzzy +#~| msgid "Invoice identifier" +#~ msgid "Universally unique identifier" +#~ msgstr "Numero de factura" + +#, fuzzy +#~| msgid "phone number" +#~ msgid "Enter a whole number." +#~ msgstr "número de teléfono" + +#, fuzzy +#~| msgid "invalidate" +#~ msgid "Enter a valid date." +#~ msgstr "invalidar" + +#, fuzzy +#~| msgid "Email validation" +#~ msgid "Enter a valid duration." +#~ msgstr "Validación del correo electrónico" + +#, fuzzy +#~| msgid "Current activity" +#~ msgid "Currently" +#~ msgstr "Actividad actual" + +#, fuzzy +#~| msgid "change" +#~ msgid "Change" +#~ msgstr "cambiar" + +#, fuzzy +#~| msgid "Search WEI" +#~ msgid "March" +#~ msgstr "Buscar un WEI" + +#, fuzzy +#~| msgid "member" +#~ msgid "September" +#~ msgstr "miembro" + +#, fuzzy +#~| msgid "member" +#~ msgid "November" +#~ msgstr "miembro" + +#, fuzzy +#~| msgid "member" +#~ msgid "December" +#~ msgstr "miembro" + +#, fuzzy +#~| msgid "fee" +#~ msgid "feb" +#~ msgstr "pago" + +#, fuzzy +#~| msgid "product" +#~ msgid "oct" +#~ msgstr "producto" + +#, fuzzy +#~| msgid "Search WEI" +#~ msgctxt "abbrev. month" +#~ msgid "March" +#~ msgstr "Buscar un WEI" + +#, fuzzy +#~| msgid "Search WEI" +#~ msgctxt "alt. month" +#~ msgid "March" +#~ msgstr "Buscar un WEI" + +#, fuzzy +#~| msgid "member" +#~ msgctxt "alt. month" +#~ msgid "September" +#~ msgstr "miembro" + +#, fuzzy +#~| msgid "member" +#~ msgctxt "alt. month" +#~ msgid "November" +#~ msgstr "miembro" + +#, fuzzy +#~| msgid "member" +#~ msgctxt "alt. month" +#~ msgid "December" +#~ msgstr "miembro" + +#, fuzzy +#~| msgid "This activity is not validated yet." +#~ msgid "This is not a valid IPv6 address." +#~ msgstr "Esta actividad no fue validada por ahora." + +#, fuzzy, python-format +#~| msgid "year" +#~ msgid "%d year" +#~ msgid_plural "%d years" +#~ msgstr[0] "año" +#~ msgstr[1] "año" + +#, fuzzy +#~| msgid "No reason specified" +#~ msgid "No year specified" +#~ msgstr "Ningún motivo dado" + +#, fuzzy +#~| msgid "No reason specified" +#~ msgid "No month specified" +#~ msgstr "Ningún motivo dado" + +#, fuzzy +#~| msgid "No reason specified" +#~ msgid "No day specified" +#~ msgstr "Ningún motivo dado" + +#, fuzzy +#~| msgid "No reason specified" +#~ msgid "No week specified" +#~ msgstr "Ningún motivo dado" + +#, fuzzy +#~| msgid "Authorization:" +#~ msgid "Authorization code" +#~ msgstr "Autorizaciones :" + +#, fuzzy +#~| msgid "Reset my password" +#~ msgid "Resource owner password-based" +#~ msgstr "Reiniciar mi contraseña" + +#, fuzzy +#~| msgid "Client secret" +#~ msgid "Client credentials" +#~ msgstr "Secreto cliente" + +#, fuzzy +#~| msgid "This address must be valid." +#~ msgid "The access token is invalid." +#~ msgstr "Este correo tiene que ser valido." + +#, fuzzy +#~| msgid "The user does not have enough money." +#~ msgid "The access token is valid but does not have enough scope." +#~ msgstr "El usuario no tiene suficientemente dinero." + +#, fuzzy +#~| msgid "Application requires following permissions:" +#~ msgid "Application requires following permissions" +#~ msgstr "La aplicación necesita los derechos siguientes :" + #~ msgid "Join BDA Club" #~ msgstr "Afiliarse al club BDA" diff --git a/locale/fr/LC_MESSAGES/django.po b/locale/fr/LC_MESSAGES/django.po index f67001ce..9793d6d9 100644 --- a/locale/fr/LC_MESSAGES/django.po +++ b/locale/fr/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-08-06 13:39+0200\n" +"POT-Creation-Date: 2024-08-06 14:46+0200\n" "PO-Revision-Date: 2022-04-11 22:05+0200\n" "Last-Translator: bleizi \n" "Language-Team: French \n" @@ -465,7 +465,7 @@ msgstr "numéro de QR-code" msgid "food container" msgstr "récipient" -#: apps/food/models.py:30 +#: apps/food/models.py:30 apps/food/templates/food/qrcode_detail.html:14 msgid "QR-code" msgstr "QR-code" @@ -482,7 +482,8 @@ msgstr "numéro du QR-code {qr_code_number}" msgid "Allergen" msgstr "Allergène" -#: apps/food/models.py:48 +#: apps/food/models.py:48 apps/food/templates/food/basicfood_detail.html:18 +#: apps/food/templates/food/transformedfood_detail.html:18 msgid "Allergens" msgstr "Allergènes" @@ -546,10 +547,59 @@ msgstr "Bouffe transformée" msgid "Transformed foods" msgstr "Bouffes transformées" -#: apps/food/views.py:25 +#: apps/food/templates/food/basicfood_detail.html:14 +#: apps/food/templates/food/qrcode_detail.html:15 +#: apps/food/templates/food/transformedfood_detail.html:14 +#: apps/note/templates/note/transaction_form.html:132 +#: apps/treasury/models.py:60 +msgid "Name" +msgstr "Nom" + +#: apps/food/templates/food/basicfood_detail.html:15 +#: apps/food/templates/food/transformedfood_detail.html:15 +msgid "Owner" +msgstr "Propriétaire" + +#: apps/food/templates/food/basicfood_detail.html:16 +msgid "Arrival date" +msgstr "Date d'arrivée" + +#: apps/food/templates/food/basicfood_detail.html:17 +#: apps/food/templates/food/transformedfood_detail.html:17 +msgid "Expiry date" +msgstr "Date de péremption" + +#: apps/food/templates/food/basicfood_detail.html:24 +#: apps/food/templates/food/qrcode_detail.html:17 +#: apps/food/templates/food/qrcode_detail.html:19 +#: apps/food/templates/food/transformedfood_detail.html:30 +msgid "Update" +msgstr "Modifier" + +#: apps/food/templates/food/qrcode_detail.html:21 apps/food/views.py:25 msgid "Add the ingredient" msgstr "Ajouter un ingrédient" +#: apps/food/templates/food/transformedfood_detail.html:16 +msgid "Creation date" +msgstr "Date de création" + +#: apps/food/templates/food/transformedfood_detail.html:24 +msgid "Ingredients" +msgstr "Ingrédients" + +#: apps/food/templates/food/transformedfood_list.html:12 +msgid "New meal" +msgstr "Nouveau plat" + +#: apps/food/templates/food/transformedfood_list.html:16 +msgid "In preparation" +msgstr "En cours de préparation" + +#: apps/food/templates/food/transformedfood_list.html:20 +msgid "Free" +msgstr "Open" + #: apps/food/views.py:39 msgid "The product isn't ready" msgstr "Le produit n'est pas prêt" @@ -1873,11 +1923,6 @@ msgstr "Action" msgid "Amount" msgstr "Montant" -#: apps/note/templates/note/transaction_form.html:132 -#: apps/treasury/models.py:60 -msgid "Name" -msgstr "Nom" - #: apps/note/templates/note/transaction_form.html:177 msgid "Select emitter" msgstr "Sélection de l'émetteur·ice" @@ -3778,6 +3823,233 @@ msgstr "" "d'adhésion. Vous devez également valider votre adresse email en suivant le " "lien que vous avez reçu." +#, fuzzy +#~| msgid "Transformed food" +#~ msgid "New transformed food" +#~ msgstr "Bouffe transformée" + +#, fuzzy +#~| msgid "Invitation" +#~ msgid "Syndication" +#~ msgstr "Invitation" + +#, fuzzy +#~| msgid "There is no results." +#~ msgid "That page contains no results" +#~ msgstr "Il n'y a pas de résultat." + +#, fuzzy +#~| msgid "phone number" +#~ msgid "Enter a number." +#~ msgstr "numéro de téléphone" + +#, fuzzy +#~| msgid "add" +#~ msgid "and" +#~ msgstr "ajouter" + +#, fuzzy, python-format +#~| msgid "A template with this name already exist" +#~ msgid "%(model_name)s with this %(field_labels)s already exists." +#~ msgstr "Un modèle de transaction avec un nom similaire existe déjà" + +#, fuzzy +#~| msgid "This image cannot be loaded." +#~ msgid "This field cannot be null." +#~ msgstr "Cette image ne peut pas être chargée." + +#, fuzzy +#~| msgid "This image cannot be loaded." +#~ msgid "This field cannot be blank." +#~ msgstr "Cette image ne peut pas être chargée." + +#, fuzzy, python-format +#~| msgid "A template with this name already exist" +#~ msgid "%(model_name)s with this %(field_label)s already exists." +#~ msgstr "Un modèle de transaction avec un nom similaire existe déjà" + +#, fuzzy +#~| msgid "phone number" +#~ msgid "Decimal number" +#~ msgstr "numéro de téléphone" + +#, fuzzy +#~| msgid "action" +#~ msgid "Duration" +#~ msgstr "action" + +#, fuzzy +#~| msgid "address" +#~ msgid "Email address" +#~ msgstr "adresse" + +#, fuzzy +#~| msgid "phone number" +#~ msgid "Floating point number" +#~ msgstr "numéro de téléphone" + +#, fuzzy +#~| msgid "IP Address" +#~ msgid "IPv4 address" +#~ msgstr "Adresse IP" + +#, fuzzy +#~| msgid "IP Address" +#~ msgid "IP address" +#~ msgstr "Adresse IP" + +#, fuzzy +#~| msgid "Invoice identifier" +#~ msgid "Universally unique identifier" +#~ msgstr "Numéro de facture" + +#, fuzzy +#~| msgid "phone number" +#~ msgid "Enter a whole number." +#~ msgstr "numéro de téléphone" + +#, fuzzy +#~| msgid "invalidate" +#~ msgid "Enter a valid date." +#~ msgstr "dévalider" + +#, fuzzy +#~| msgid "Email validation" +#~ msgid "Enter a valid duration." +#~ msgstr "Validation de l'adresse mail" + +#, fuzzy +#~| msgid "Current activity" +#~ msgid "Currently" +#~ msgstr "Activité en cours" + +#, fuzzy +#~| msgid "change" +#~ msgid "Change" +#~ msgstr "modifier" + +#, fuzzy +#~| msgid "Search" +#~ msgid "March" +#~ msgstr "Recherche" + +#, fuzzy +#~| msgid "member" +#~ msgid "September" +#~ msgstr "adhérent·e" + +#, fuzzy +#~| msgid "member" +#~ msgid "November" +#~ msgstr "adhérent·e" + +#, fuzzy +#~| msgid "member" +#~ msgid "December" +#~ msgstr "adhérent·e" + +#, fuzzy +#~| msgid "fee" +#~ msgid "feb" +#~ msgstr "cotisation" + +#, fuzzy +#~| msgid "product" +#~ msgid "oct" +#~ msgstr "produit" + +#, fuzzy +#~| msgid "Search" +#~ msgctxt "abbrev. month" +#~ msgid "March" +#~ msgstr "Recherche" + +#, fuzzy +#~| msgid "Search" +#~ msgctxt "alt. month" +#~ msgid "March" +#~ msgstr "Recherche" + +#, fuzzy +#~| msgid "member" +#~ msgctxt "alt. month" +#~ msgid "September" +#~ msgstr "adhérent·e" + +#, fuzzy +#~| msgid "member" +#~ msgctxt "alt. month" +#~ msgid "November" +#~ msgstr "adhérent·e" + +#, fuzzy +#~| msgid "member" +#~ msgctxt "alt. month" +#~ msgid "December" +#~ msgstr "adhérent·e" + +#, fuzzy +#~| msgid "This activity is not validated yet." +#~ msgid "This is not a valid IPv6 address." +#~ msgstr "Cette activité n'est pas encore validée." + +#, fuzzy, python-format +#~| msgid "year" +#~ msgid "%d year" +#~ msgid_plural "%d years" +#~ msgstr[0] "année" +#~ msgstr[1] "année" + +#, fuzzy +#~| msgid "No reason specified" +#~ msgid "No year specified" +#~ msgstr "Pas de motif spécifié" + +#, fuzzy +#~| msgid "No reason specified" +#~ msgid "No month specified" +#~ msgstr "Pas de motif spécifié" + +#, fuzzy +#~| msgid "No reason specified" +#~ msgid "No day specified" +#~ msgstr "Pas de motif spécifié" + +#, fuzzy +#~| msgid "No reason specified" +#~ msgid "No week specified" +#~ msgstr "Pas de motif spécifié" + +#, fuzzy +#~| msgid "Authorization:" +#~ msgid "Authorization code" +#~ msgstr "Autorisation :" + +#, fuzzy +#~| msgid "Reset my password" +#~ msgid "Resource owner password-based" +#~ msgstr "Réinitialiser mon mot de passe" + +#, fuzzy +#~| msgid "Client secret" +#~ msgid "Client credentials" +#~ msgstr "Secret client" + +#, fuzzy +#~| msgid "This address must be valid." +#~ msgid "The access token is invalid." +#~ msgstr "Cette adresse doit être valide." + +#, fuzzy +#~| msgid "The user does not have enough money." +#~ msgid "The access token is valid but does not have enough scope." +#~ msgstr "L'utilisateur·ice n'a pas assez d'argent." + +#, fuzzy +#~| msgid "Application requires following permissions:" +#~ msgid "Application requires following permissions" +#~ msgstr "L'application requiert les permissions suivantes :" + #, fuzzy #~| msgid "People having you as a friend" #~ msgid "You already have that person as a friend" From 196df1e77565b07a1f7480e01b8b0dc43923cd0a Mon Sep 17 00:00:00 2001 From: quark Date: Tue, 13 Aug 2024 02:07:32 +0200 Subject: [PATCH 23/29] Remove initial.json (food) mandatory allergen are directly created in migration. Edit tables.py and views.py transformedfoodlist.html to improve/change the view. Edit base.html, urls.py to correct little mistakes. Edit initial.json (permission) to begin permission for food apps and create a new role (Respo Bouffe). --- apps/food/fixtures/initial.json | 107 ------------------ apps/food/tables.py | 2 +- .../templates/food/transformedfood_list.html | 46 +++++++- apps/food/urls.py | 2 +- apps/food/views.py | 52 ++++++--- apps/permission/fixtures/initial.json | 81 +++++++++++-- note_kfet/templates/base.html | 2 +- 7 files changed, 154 insertions(+), 138 deletions(-) delete mode 100644 apps/food/fixtures/initial.json diff --git a/apps/food/fixtures/initial.json b/apps/food/fixtures/initial.json deleted file mode 100644 index c91a2dec..00000000 --- a/apps/food/fixtures/initial.json +++ /dev/null @@ -1,107 +0,0 @@ -[ - { - "model": "food.allergen", - "pk": 1, - "fields": { - "name": "alcohol" - } - }, - { - "model": "food.allergen", - "pk": 2, - "fields": { - "name": "celery" - } - }, - { - "model": "food.allergen", - "pk": 3, - "fields": { - "name": "crustecean" - } - }, - { - "model": "food.allergen", - "pk": 4, - "fields": { - "name": "egg" - } - }, - { - "model": "food.allergen", - "pk": 5, - "fields": { - "name": "fish" - } - }, - { - "model": "food.allergen", - "pk": 6, - "fields": { - "name": "gluten" - } - }, - { - "model": "food.allergen", - "pk": 7, - "fields": { - "name": "groundnut" - } - }, - { - "model": "food.allergen", - "pk": 8, - "fields": { - "name": "lupine" - } - }, - { - "model": "food.allergen", - "pk": 9, - "fields": { - "name": "milk" - } - }, - { - "model": "food.allergen", - "pk": 10, - "fields": { - "name": "mollusc" - } - }, - { - "model": "food.allergen", - "pk": 11, - "fields": { - "name": "mustard" - } - }, - { - "model": "food.allergen", - "pk": 12, - "fields": { - "name": "nut" - } - }, - { - "model": "food.allergen", - "pk": 13, - "fields": { - "name": "sesame" - } - }, - { - "model": "food.allergen", - "pk": 14, - "fields": { - "name": "soy" - } - }, - { - "model": "food.allergen", - "pk": 15, - "fields": { - "name": "sulphite" - } - } -] diff --git a/apps/food/tables.py b/apps/food/tables.py index c824e42e..4a180c76 100644 --- a/apps/food/tables.py +++ b/apps/food/tables.py @@ -16,4 +16,4 @@ class TransformedFoodTable(tables.Table): class Meta: model = TransformedFood template_name = 'django_tables2/bootstrap4.html' - fields = ('name', ) + fields = ('name', "owner", "allergens", "expiry_date") diff --git a/apps/food/templates/food/transformedfood_list.html b/apps/food/templates/food/transformedfood_list.html index 2c4cb93d..fea2dd30 100644 --- a/apps/food/templates/food/transformedfood_list.html +++ b/apps/food/templates/food/transformedfood_list.html @@ -7,18 +7,54 @@ SPDX-License-Identifier: GPL-3.0-or-later {% block content %}
+

+ {% trans "Meal served" %} +

+ {% if can_create_meal %} + {% endif %} + {% if served.data %} + {% render_table served %} + {% else %} +
+
+ {% trans "There is no meal served." %} +
+
+ {% endif %} +
+ +

- {% trans 'In preparation' %} + {% trans "Open" %}

+ {% if open.data %} + {% render_table open %} + {% else %} +
class="card-body"> +
+ {% trans "There is no free meal." %} +
+
+ {% endif %} +
+ +
+

+ {% trans "All meals" %} +

+ {% if table.data %} {% render_table table %} -

- {% trans 'Free' %} -

- {% render_table open_table %} + {% else %} +
class="card-body"> +
+ {% trans "There is no meal." %} +
+
+ {% endif %}
{% endblock %} diff --git a/apps/food/urls.py b/apps/food/urls.py index 76527033..59640bae 100644 --- a/apps/food/urls.py +++ b/apps/food/urls.py @@ -8,7 +8,7 @@ from . import views app_name = 'food' urlpatterns = [ - path('', views.TransfomedListView.as_view(), name='food_list'), + path('', views.TransformedListView.as_view(), name='food_list'), path('', views.QRCodeView.as_view(), name='qrcode_view'), path('detail/', views.FoodView.as_view(), name='food_view'), diff --git a/apps/food/views.py b/apps/food/views.py index 15050d9e..10f296f7 100644 --- a/apps/food/views.py +++ b/apps/food/views.py @@ -4,12 +4,16 @@ from django.db import transaction from django.contrib.auth.mixins import LoginRequiredMixin from django.http import HttpResponseRedirect -from django_tables2.views import SingleTableView +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, TemplateView +from django.views.generic.list import ListView +from permission.backends import PermissionBackend from permission.views import ProtectQuerysetMixin, ProtectedCreateView +from member.models import Club +from note_kfet.middlewares import get_current_request from .forms import AddIngredientForms, BasicFoodForms, QRCodeForms, TransformedFoodForms from .models import BasicFood, Food, QRCode, TransformedFood @@ -253,26 +257,46 @@ class TransformedFoodCreateView(TransformedFoodFormView, ProtectedCreateView): ) -class TransfomedListView(ProtectQuerysetMixin, LoginRequiredMixin, SingleTableView): +class TransformedListView(ProtectQuerysetMixin, LoginRequiredMixin, MultiTableMixin, ListView): """ - Displays not ready TransformedFood + Displays ready TransformedFood """ model = TransformedFood - table_class = TransformedFoodTable - ordering = ('name',) + tables = [TransformedFoodTable, TransformedFoodTable, TransformedFoodTable] extra_context = {"title": _("Transformed food")} def get_queryset(self, **kwargs): - return super().get_queryset(**kwargs)\ - .filter(is_ready=False)\ - .distinct() + 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) - context['open_table'] = TransformedFoodTable( - TransformedFood.objects.filter( - was_eaten=False, - expiry_date__lt=timezone.now() - ), - prefix="open-") + + # context["can_create_meal"] = PermissionBackend.check_perm(self.request, "food.add_transformedfood", TransformedFood(creation_date = timezone.now(), name = "", expiry_date = timezone.now(), owner = )) <- défi prendre un club qui fonctionne (s'il existe) pour l'utilisateur + context["can_create_meal"] = True + + tables = context["tables"] + for name, table in zip(["table", "open", "served"], tables): + context[name] = table return context diff --git a/apps/permission/fixtures/initial.json b/apps/permission/fixtures/initial.json index b59fdb61..92ab6340 100644 --- a/apps/permission/fixtures/initial.json +++ b/apps/permission/fixtures/initial.json @@ -3111,6 +3111,54 @@ "description": "Voir ceux nous ayant pour ami, pour toujours" } }, + { + "model": "permission.permission", + "pk": 199, + "fields": { + "model": [ + "food", + "transformedfood" + ], + "query": "", + "type": "view", + "mask": 3, + "field": "", + "permanent": false, + "description": "Voir tout les plats" + } + }, + { + "model": "permission.permission", + "pk": 200, + "fields": { + "model": [ + "food", + "transformedfood" + ], + "query": "{\"owner\": \"club\"}", + "type": "view", + "mask": 3, + "field": "", + "permanent": false, + "description": "Voir tout les plats de son club" + } + }, + { + "model": "permission.permission", + "pk": 200, + "fields": { + "model": [ + "food", + "transformedfood" + ], + "query": "{\"is_ready\": true, \"is_active\": true, \"was_eaten\": false}", + "type": "view", + "mask": 1, + "field": "", + "permanent": false, + "description": "Voir les plats préparés actifs servis" + } + }, { "model": "permission.role", "pk": 1, @@ -3148,11 +3196,11 @@ 187, 188, 189, - 190, - 191, - 195, - 196, - 198 + 190, + 191, + 195, + 196, + 198 ] } }, @@ -3190,7 +3238,8 @@ 157, 158, 159, - 160 + 160, + 200 ] } }, @@ -3216,7 +3265,8 @@ 49, 50, 141, - 169 + 169, + 199 ] } }, @@ -3390,7 +3440,8 @@ 166, 167, 168, - 182 + 182, + 199 ] } }, @@ -3594,7 +3645,8 @@ 168, 176, 177, - 197 + 197, + 199 ] } }, @@ -3612,6 +3664,17 @@ ] } }, + { + "model": "permission.role", + "pk": 22, + "fields": { + "for_club": 2, + "name": "Respo Bouffe", + "permissions": [ + 199 + ] + } + }, { "model": "wei.weirole", "pk": 12, diff --git a/note_kfet/templates/base.html b/note_kfet/templates/base.html index 15743018..9f5ae867 100644 --- a/note_kfet/templates/base.html +++ b/note_kfet/templates/base.html @@ -69,7 +69,7 @@ SPDX-License-Identifier: GPL-3.0-or-later {% if request.user.is_authenticated %} {% endif %} {% if user.is_authenticated and user|is_member:"Kfet" %} From 6d7076b03e96c2fc709b947a682118eec205eb00 Mon Sep 17 00:00:00 2001 From: quark Date: Wed, 14 Aug 2024 01:32:55 +0200 Subject: [PATCH 24/29] Edit forms, views, template to improve/modify view. Edit urls to remove some path. Few changes in models. --- apps/food/forms.py | 20 ++- .../migrations/0004_auto_20240813_2358.py | 28 +++++ apps/food/models.py | 19 +-- .../templates/food/add_ingredient_form.html | 1 - ...sic_food_form.html => basicfood_form.html} | 3 +- apps/food/templates/food/qrcode_detail.html | 26 ++-- apps/food/urls.py | 3 - apps/food/views.py | 115 +++++++++++++++--- apps/permission/fixtures/initial.json | 12 +- 9 files changed, 179 insertions(+), 48 deletions(-) create mode 100644 apps/food/migrations/0004_auto_20240813_2358.py rename apps/food/templates/food/{basic_food_form.html => basicfood_form.html} (89%) diff --git a/apps/food/forms.py b/apps/food/forms.py index 59226a52..e8601306 100644 --- a/apps/food/forms.py +++ b/apps/food/forms.py @@ -11,7 +11,7 @@ from note_kfet.inputs import Autocomplete, DateTimePickerInput from note_kfet.middlewares import get_current_request from permission.backends import PermissionBackend -from .models import BasicFood, QRCode, TransformedFood +from .models import BasicFood, QRCode, TransformedFood, Food class AddIngredientForms(forms.ModelForm): @@ -20,7 +20,7 @@ class AddIngredientForms(forms.ModelForm): """ def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) - self.fields['ingredient'].queryset = self.fields['ingredient'].queryset.filter(is_ready=False) + self.fields['ingredient'].queryset = self.fields['ingredient'].queryset.filter(is_ready=False, is_active=True, was_eaten=False) class Meta: model = TransformedFood @@ -45,7 +45,7 @@ class BasicFoodForms(forms.ModelForm): class Meta: model = BasicFood - fields = ('name', 'owner', 'date_type', 'expiry_date', 'allergens') + fields = ('name', 'owner', 'date_type', 'expiry_date', 'is_active', 'was_eaten', 'allergens',) widgets = { "owner": Autocomplete( model=Club, @@ -80,6 +80,8 @@ class TransformedFoodForms(forms.ModelForm): self.fields['creation_date'].required = True self.fields['creation_date'].initial = timezone.now self.fields['is_active'].initial = True + self.fields['is_ready'].initial = False + self.fields['was_eaten'].initial = False # Some example self.fields['name'].widget.attrs.update({"placeholder": _("lasagna")}) @@ -89,7 +91,7 @@ class TransformedFoodForms(forms.ModelForm): class Meta: model = TransformedFood - fields = ('name', 'creation_date', 'owner', 'is_active', 'shelf_life') + fields = ('name', 'creation_date', 'owner', 'is_active', 'is_ready', 'was_eaten', 'shelf_life') widgets = { "owner": Autocomplete( model=Club, @@ -97,3 +99,13 @@ class TransformedFoodForms(forms.ModelForm): ), 'creation_date': DateTimePickerInput(), } + +class FoodForms(forms.ModelForm): + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.fields['was_eaten'].initial = True + + class Meta: + model = Food + fields = ('was_eaten',) + diff --git a/apps/food/migrations/0004_auto_20240813_2358.py b/apps/food/migrations/0004_auto_20240813_2358.py new file mode 100644 index 00000000..d7fdf200 --- /dev/null +++ b/apps/food/migrations/0004_auto_20240813_2358.py @@ -0,0 +1,28 @@ +# Generated by Django 2.2.28 on 2024-08-13 21:58 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('food', '0003_create_14_allergens_mandatory'), + ] + + operations = [ + migrations.RemoveField( + model_name='transformedfood', + name='is_active', + ), + migrations.AddField( + model_name='food', + name='is_active', + field=models.BooleanField(default=True, verbose_name='is active'), + ), + migrations.AlterField( + model_name='qrcode', + name='food_container', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='QR_code', to='food.Food', verbose_name='food container'), + ), + ] diff --git a/apps/food/models.py b/apps/food/models.py index 50db24f3..48974e00 100644 --- a/apps/food/models.py +++ b/apps/food/models.py @@ -19,9 +19,9 @@ class QRCode(models.Model): unique=True, ) - food_container = models.OneToOneField( + food_container = models.ForeignKey( 'Food', - on_delete=models.PROTECT, + on_delete=models.CASCADE, related_name='QR_code', verbose_name=_('food container'), ) @@ -80,11 +80,21 @@ class Food(PolymorphicModel): 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 @@ -160,11 +170,6 @@ class TransformedFood(Food): verbose_name=_('transformed ingredient'), ) - is_active = models.BooleanField( - default=True, - verbose_name=_('is active'), - ) - # Without microbiological analyzes, the storage time is 3 days shelf_life = models.DurationField( verbose_name=_("shelf life"), diff --git a/apps/food/templates/food/add_ingredient_form.html b/apps/food/templates/food/add_ingredient_form.html index 86e3b03e..395928e4 100644 --- a/apps/food/templates/food/add_ingredient_form.html +++ b/apps/food/templates/food/add_ingredient_form.html @@ -7,7 +7,6 @@ SPDX-License-Identifier: GPL-3.0-or-later {% block content %}

- HTML not finished
{{ title }}

diff --git a/apps/food/templates/food/basic_food_form.html b/apps/food/templates/food/basicfood_form.html similarity index 89% rename from apps/food/templates/food/basic_food_form.html rename to apps/food/templates/food/basicfood_form.html index dbfb49e3..6fe6f06f 100644 --- a/apps/food/templates/food/basic_food_form.html +++ b/apps/food/templates/food/basicfood_form.html @@ -7,13 +7,12 @@ SPDX-License-Identifier: GPL-3.0-or-later {% block content %}

- HTML not finished
{{ title }}

{% csrf_token %} - {{ form|crispy }} + {{ form | crispy }}
diff --git a/apps/food/templates/food/qrcode_detail.html b/apps/food/templates/food/qrcode_detail.html index def3a028..4c6b1118 100644 --- a/apps/food/templates/food/qrcode_detail.html +++ b/apps/food/templates/food/qrcode_detail.html @@ -7,18 +7,28 @@ SPDX-License-Identifier: GPL-3.0-or-later {% block content %}

- HTML not finished
+ HTML finished
{{ title }}

-

{% trans 'QR-code' %} : {{ qrcode.qr_code_number }}

+

{% trans 'QR-code number' %} : {{ qrcode.qr_code_number }}

{% trans 'Name' %} : {{ qrcode.food_container.name }}

- {% if qrcode.food_container.polymorphic_ctype.name == 'Basic food' %} - {% trans 'Update' %} - {% else %} - {% trans 'Update' %} +

{% trans 'Owner' %} : {{ qrcode.food_container.owner }}

+

{% trans 'Expiry date' %} : {{ qrcode.food_container.expiry_date }}

+ {% if qrcode.food_container.polymorphic_ctype.model == 'basicfood' and can_update_basic %} + + {% trans 'Update' %} + + {% elif can_update_transformed %} + + {% trans 'Update' %} + {% endif %} - {% trans 'Add the ingredient' %} -
+ {% if can_add_ingredient %} + + {% trans 'Add the ingredient' %} + + {% endif %} +
{% endblock %} diff --git a/apps/food/urls.py b/apps/food/urls.py index 59640bae..09bb8ebe 100644 --- a/apps/food/urls.py +++ b/apps/food/urls.py @@ -13,12 +13,9 @@ urlpatterns = [ path('detail/', views.FoodView.as_view(), name='food_view'), path('/create_qrcode', views.QRCodeCreateView.as_view(), name='qrcode_create'), - path('create', views.FoodCreateView.as_view(), name='food_create'), path('/create_qrcode/basic', views.QRCodeBasicFoodCreateView.as_view(), name='qrcode_basic_create'), path('create/transformed', views.TransformedFoodCreateView.as_view(), name='transformed_create'), - path('update/basic/', views.BasicFoodUpdateView.as_view(), name='basic_update'), path('update/transformed/', views.TransformedFoodUpdateView.as_view(), name='transformed_update'), - path('add/', views.AddIngredientView.as_view(), name='add_ingredient'), ] diff --git a/apps/food/views.py b/apps/food/views.py index 10f296f7..cddb19f6 100644 --- a/apps/food/views.py +++ b/apps/food/views.py @@ -10,12 +10,13 @@ from django.utils.translation import gettext_lazy as _ from django.utils import timezone from django.views.generic import DetailView, UpdateView, TemplateView from django.views.generic.list import ListView +from django.forms import HiddenInput from permission.backends import PermissionBackend from permission.views import ProtectQuerysetMixin, ProtectedCreateView from member.models import Club from note_kfet.middlewares import get_current_request -from .forms import AddIngredientForms, BasicFoodForms, QRCodeForms, TransformedFoodForms +from .forms import AddIngredientForms, BasicFoodForms, QRCodeForms, TransformedFoodForms, FoodForms from .models import BasicFood, Food, QRCode, TransformedFood from .tables import TransformedFoodTable @@ -24,6 +25,7 @@ class AddIngredientView(ProtectQuerysetMixin, UpdateView): """ A view to add an ingredient """ + # TO DO : ajouter un champ fully_used dans le form et changer was_eaten en conséquence + mieux filtrer les plat dispo avec des perms model = Food template_name = 'food/add_ingredient_form.html' extra_context = {"title": _("Add the ingredient")} @@ -39,8 +41,9 @@ class AddIngredientView(ProtectQuerysetMixin, UpdateView): form.instance.creater = self.request.user food = Food.objects.get(pk=self.kwargs['pk']) add_ingredient_form = AddIngredientForms(data=self.request.POST) - if not food.is_ready: - form.add_error(None, _("The product isn't ready")) + food_form = FoodForms(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) @@ -59,11 +62,11 @@ class AddIngredientView(ProtectQuerysetMixin, UpdateView): class BasicFoodUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView): """ - A view to add a basic food + A view to update a basic food """ model = BasicFood form_class = BasicFoodForms - template_name = 'food/basic_food_form.html' + template_name = 'food/basicfood_form.html' extra_context = {"title": _("Add a new aliment")} @transaction.atomic @@ -81,6 +84,18 @@ class BasicFoodUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView): 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) + form = context['form'] + # TO DO : Add perms here + if 1==0: + form.fields['is_active'].widget = HiddenInput() + if 1==0: + form.fields['was_eaten'].widget = HiddenInput() + form.fields['is_active'].help_text = _("Uncheck if the food doesn't exist anymore") + form.fields['was_eaten'].help_text = _("Check if the food has been entirely eaten") + + return context class FoodCreateView(ProtectQuerysetMixin, LoginRequiredMixin, TemplateView): """ @@ -98,7 +113,6 @@ class FoodView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView): extra_context = {"title": _("Details")} context_object_name = "food" - class QRCodeBasicFoodCreateView(ProtectQuerysetMixin, ProtectedCreateView): ##################################################################### # TO DO @@ -110,7 +124,7 @@ class QRCodeBasicFoodCreateView(ProtectQuerysetMixin, ProtectedCreateView): """ model = BasicFood form_class = BasicFoodForms - template_name = 'food/basic_food_form.html' + template_name = 'food/basicfood_form.html' extra_context = {"title": _("Add a new basic food with QRCode")} @transaction.atomic @@ -124,7 +138,9 @@ class QRCodeBasicFoodCreateView(ProtectQuerysetMixin, ProtectedCreateView): 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 = True + 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() @@ -146,6 +162,16 @@ class QRCodeBasicFoodCreateView(ProtectQuerysetMixin, ProtectedCreateView): expiry_date=timezone.now(), ) + 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): """ @@ -160,6 +186,8 @@ class QRCodeCreateView(ProtectQuerysetMixin, ProtectedCreateView): 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) @@ -213,11 +241,21 @@ class QRCodeView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView): else: return HttpResponseRedirect(reverse("food:qrcode_create", kwargs=kwargs)) + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + # TO DO : Add perms here + context["can_update_basic"]=True + context["can_update_transformed"]=True + context["can_add_ingredient"] = True -class TransformedFoodFormView(ProtectQuerysetMixin): + return context + + +class TransformedFoodCreateView(ProtectQuerysetMixin, ProtectedCreateView): """ A view to add a tranformed food """ + # TO DO : fix the "NotImplementedError" (╯°□°)╯︵ ┻━┻ ... model = TransformedFood template_name = 'food/transformed_food_form.html' form_class = TransformedFoodForms @@ -233,6 +271,9 @@ class TransformedFoodFormView(ProtectQuerysetMixin): # 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() @@ -244,18 +285,58 @@ class TransformedFoodFormView(ProtectQuerysetMixin): 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) + + # 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() -class TransformedFoodUpdateView(TransformedFoodFormView, LoginRequiredMixin, UpdateView): - pass + # Field shelf life is only display for authorized user + # TO DO : Add permission here + if not True: + form.fields['shelf_life'].widget = HiddenInput() + + return context -class TransformedFoodCreateView(TransformedFoodFormView, ProtectedCreateView): - def get_sample_object(self): - return TransformedFood( - name="", - creation_date=timezone.now(), - ) +class TransformedFoodUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView): + """ + A view to update transformed product + """ + model = TransformedFood + template_name = 'food/transformed_food_form.html' + form_class = TransformedFoodForms + extra_context = {'title' : _('Update 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) + + form = context['form'] + + fields = ['is_active','is_ready','was_eaten','shelf_life'] + # TO DO : Add permissions here + permissions = [True]*len(fields) + for i in range(len(fields)): + if not permissions[i] : form[fields[i]].widget = HiddenInput() + return context class TransformedListView(ProtectQuerysetMixin, LoginRequiredMixin, MultiTableMixin, ListView): """ diff --git a/apps/permission/fixtures/initial.json b/apps/permission/fixtures/initial.json index 92ab6340..a4c5ecad 100644 --- a/apps/permission/fixtures/initial.json +++ b/apps/permission/fixtures/initial.json @@ -3119,7 +3119,7 @@ "food", "transformedfood" ], - "query": "", + "query": "[]", "type": "view", "mask": 3, "field": "", @@ -3135,7 +3135,7 @@ "food", "transformedfood" ], - "query": "{\"owner\": \"club\"}", + "query": "{\"owner\": [\"club\"]}", "type": "view", "mask": 3, "field": "", @@ -3145,7 +3145,7 @@ }, { "model": "permission.permission", - "pk": 200, + "pk": 201, "fields": { "model": [ "food", @@ -3239,7 +3239,7 @@ 158, 159, 160, - 200 + 201 ] } }, @@ -3266,7 +3266,7 @@ 50, 141, 169, - 199 + 200 ] } }, @@ -3441,7 +3441,7 @@ 167, 168, 182, - 199 + 200 ] } }, From debeb33d4637902ec3459776aceb13c938c2187d Mon Sep 17 00:00:00 2001 From: quark Date: Sat, 17 Aug 2024 02:28:27 +0200 Subject: [PATCH 25/29] Improve/modify form, view, template. Add permissions --- apps/food/forms.py | 29 +- apps/food/models.py | 6 +- .../food/templates/food/basicfood_detail.html | 26 +- .../food/templates/food/create_food_form.html | 21 - .../templates/food/create_qrcode_form.html | 7 +- apps/food/templates/food/qrcode_detail.html | 19 +- .../food/transformedfood_detail.html | 52 ++- ...od_form.html => transformedfood_form.html} | 1 - .../templates/food/transformedfood_list.html | 4 +- apps/food/views.py | 132 +++--- apps/permission/fixtures/initial.json | 440 +++++++++++++++++- 11 files changed, 605 insertions(+), 132 deletions(-) delete mode 100644 apps/food/templates/food/create_food_form.html rename apps/food/templates/food/{transformed_food_form.html => transformedfood_form.html} (94%) diff --git a/apps/food/forms.py b/apps/food/forms.py index e8601306..0b2532aa 100644 --- a/apps/food/forms.py +++ b/apps/food/forms.py @@ -11,7 +11,7 @@ from note_kfet.inputs import Autocomplete, DateTimePickerInput from note_kfet.middlewares import get_current_request from permission.backends import PermissionBackend -from .models import BasicFood, QRCode, TransformedFood, Food +from .models import BasicFood, QRCode, TransformedFood class AddIngredientForms(forms.ModelForm): @@ -20,11 +20,20 @@ class AddIngredientForms(forms.ModelForm): """ def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) - self.fields['ingredient'].queryset = self.fields['ingredient'].queryset.filter(is_ready=False, is_active=True, was_eaten=False) + self.fields['ingredient'].queryset = self.fields['ingredient'].queryset.filter( + polymorphic_ctype__model='transformedfood', + owner_id=self.instance.owner_id, + is_ready=False, + is_active=True, + was_eaten=False, + ) + # Caution, the logic is inverted here, we flip the logic on saving in AddIngredientView + self.fields['is_active'].initial = True + self.fields['is_active'].label = _("Fully used") class Meta: model = TransformedFood - fields = ('ingredient',) + fields = ('ingredient', 'is_active') class BasicFoodForms(forms.ModelForm): @@ -38,7 +47,7 @@ class BasicFoodForms(forms.ModelForm): self.fields['owner'].required = True # Some example - self.fields['name'].widget.attrs.update({"placeholder": _("pasta")}) + self.fields['name'].widget.attrs.update({"placeholder": _("Pasta METRO 5kg")}) clubs = list(Club.objects.filter(PermissionBackend.filter_queryset(get_current_request(), Club, "change")).all()) shuffle(clubs) self.fields['owner'].widget.attrs["placeholder"] = ", ".join(club.name for club in clubs[:4]) + ", ..." @@ -84,7 +93,7 @@ class TransformedFoodForms(forms.ModelForm): self.fields['was_eaten'].initial = False # Some example - self.fields['name'].widget.attrs.update({"placeholder": _("lasagna")}) + self.fields['name'].widget.attrs.update({"placeholder": _("Lasagna")}) clubs = list(Club.objects.filter(PermissionBackend.filter_queryset(get_current_request(), Club, "change")).all()) shuffle(clubs) self.fields['owner'].widget.attrs["placeholder"] = ", ".join(club.name for club in clubs[:4]) + ", ..." @@ -99,13 +108,3 @@ class TransformedFoodForms(forms.ModelForm): ), 'creation_date': DateTimePickerInput(), } - -class FoodForms(forms.ModelForm): - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - self.fields['was_eaten'].initial = True - - class Meta: - model = Food - fields = ('was_eaten',) - diff --git a/apps/food/models.py b/apps/food/models.py index 48974e00..97e00ff9 100644 --- a/apps/food/models.py +++ b/apps/food/models.py @@ -94,7 +94,6 @@ class Food(PolymorphicModel): verbose_name=_('is active'), ) - def __str__(self): return self.name @@ -176,6 +175,11 @@ class TransformedFood(Food): 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 diff --git a/apps/food/templates/food/basicfood_detail.html b/apps/food/templates/food/basicfood_detail.html index b890a93d..846fadba 100644 --- a/apps/food/templates/food/basicfood_detail.html +++ b/apps/food/templates/food/basicfood_detail.html @@ -7,21 +7,31 @@ SPDX-License-Identifier: GPL-3.0-or-later {% block content %}

- HTML not finished
- {{ title }} + {{ title }} {{ food.name }}

-

{% trans 'Name' %} : {{ food.name }}

-

{% trans 'Owner' %} : {{ food.owner }}

-

{% trans 'Arrival date' %} : {{ food.arrival_date }}

-

{% trans 'Expiry date' %} : {{ food.expiry_date }}

-

{% trans 'Allergens' %} :

    +
  • {% trans 'Owner' %} : {{ food.owner }}

  • +
  • {% trans 'Arrival date' %} : {{ food.arrival_date }}

  • +
  • {% trans 'Expiry date' %} : {{ food.expiry_date }} ({{ food.date_type }})

  • +
  • {% trans 'Allergens' %} :
  • +
      {% for allergen in food.allergens.iterator %}
    • {{ allergen.name }}
    • {% endfor %} +
    +

    +

  • {% trans 'Active' %} : {{ food.is_active }}

  • +
  • {% trans 'Eaten' %} : {{ food.was_eaten }}

- {% trans 'Update' %} + {% if can_update %} + {% trans 'Update' %} + {% endif %} + {% if can_add_ingredient %} + + {% trans 'Add to a meal' %} + + {% endif %}
{% endblock %} diff --git a/apps/food/templates/food/create_food_form.html b/apps/food/templates/food/create_food_form.html deleted file mode 100644 index 5728dd25..00000000 --- a/apps/food/templates/food/create_food_form.html +++ /dev/null @@ -1,21 +0,0 @@ -{% extends "base.html" %} -{% comment %} -SPDX-License-Identifier: GPL-3.0-or-later -{% endcomment %} - -{% block content %} -
-

- HTML not finished
- {{ title }} -

-
-
- -
-
-
-{% endblock %} diff --git a/apps/food/templates/food/create_qrcode_form.html b/apps/food/templates/food/create_qrcode_form.html index 69909b7f..17fff49f 100644 --- a/apps/food/templates/food/create_qrcode_form.html +++ b/apps/food/templates/food/create_qrcode_form.html @@ -7,17 +7,16 @@ SPDX-License-Identifier: GPL-3.0-or-later {% block content %}

- HTML not finished
{{ title }}

- - New basic food + + {% trans 'New basic food' %}
{% csrf_token %} {{ form|crispy }} - +
diff --git a/apps/food/templates/food/qrcode_detail.html b/apps/food/templates/food/qrcode_detail.html index 4c6b1118..6e3e8110 100644 --- a/apps/food/templates/food/qrcode_detail.html +++ b/apps/food/templates/food/qrcode_detail.html @@ -7,14 +7,14 @@ SPDX-License-Identifier: GPL-3.0-or-later {% block content %}

- HTML finished
- {{ title }} + {{ title }} {% trans 'number' %} {{ qrcode.qr_code_number }}

-

{% trans 'QR-code number' %} : {{ qrcode.qr_code_number }}

-

{% trans 'Name' %} : {{ qrcode.food_container.name }}

-

{% trans 'Owner' %} : {{ qrcode.food_container.owner }}

-

{% trans 'Expiry date' %} : {{ qrcode.food_container.expiry_date }}

+
    +
  • {% trans 'Name' %} : {{ qrcode.food_container.name }}

  • +
  • {% trans 'Owner' %} : {{ qrcode.food_container.owner }}

  • +
  • {% trans 'Expiry date' %} : {{ qrcode.food_container.expiry_date }}

  • +
{% if qrcode.food_container.polymorphic_ctype.model == 'basicfood' and can_update_basic %} {% trans 'Update' %} @@ -24,9 +24,14 @@ SPDX-License-Identifier: GPL-3.0-or-later {% trans 'Update' %} {% endif %} + {% if can_view_detail %} + + {% trans 'View details' %} + + {% endif %} {% if can_add_ingredient %} - {% trans 'Add the ingredient' %} + {% trans 'Add to a meal' %} {% endif %}
diff --git a/apps/food/templates/food/transformedfood_detail.html b/apps/food/templates/food/transformedfood_detail.html index 5ffb2cc0..ca32bc06 100644 --- a/apps/food/templates/food/transformedfood_detail.html +++ b/apps/food/templates/food/transformedfood_detail.html @@ -7,27 +7,45 @@ SPDX-License-Identifier: GPL-3.0-or-later {% block content %}

- HTML not finished
- {{ title }} + {{ title }} {{ food.name }}

-

{% trans 'Name' %} : {{ food.name }}

-

{% trans 'Owner' %} : {{ food.owner }}

-

{% trans 'Creation date' %} : {{ food.creation_date }}

-

{% trans 'Expiry date' %} : {{ food.expiry_date }}

-

{% trans 'Allergens' %} :

    - {% for allergen in food.allergens.iterator %} -
  • {{ allergen.name }}
  • - {% endfor %} +
  • {% trans 'Owner' %} : {{ food.owner }}

  • + {% if can_see_ready %} +
  • {% trans 'Ready' %} : {{ food.is_ready }}

  • + {% endif %} +
  • {% trans 'Creation date' %} : {{ food.creation_date }}

  • +
  • {% trans 'Expiry date' %} : {{ food.expiry_date }}

  • +
  • {% trans 'Allergens' %} :
  • +
      + {% for allergen in food.allergens.iterator %} +
    • {{ allergen.name }}
    • + {% endfor %} +
    +

    +

  • {% trans 'Ingredients' %} :
  • + +

    +

  • {% trans 'Shelf life' %} : {{ food.shelf_life }}

  • +
  • {% trans 'Ready' %} : {{ food.is_ready }}

  • +
  • {% trans 'Active' %} : {{ food.is_active }}

  • +
  • {% trans 'Eaten' %} : {{ food.was_eaten }}

-

{% trans 'Ingredients' %} :

- - {% trans 'Update' %} + {% if can_update %} + + {% trans 'Update' %} + + {% endif %} + {% if can_add_ingredient %} + + {% trans 'Add to a meal' %} + + {% endif %}
{% endblock %} diff --git a/apps/food/templates/food/transformed_food_form.html b/apps/food/templates/food/transformedfood_form.html similarity index 94% rename from apps/food/templates/food/transformed_food_form.html rename to apps/food/templates/food/transformedfood_form.html index 86e3b03e..395928e4 100644 --- a/apps/food/templates/food/transformed_food_form.html +++ b/apps/food/templates/food/transformedfood_form.html @@ -7,7 +7,6 @@ SPDX-License-Identifier: GPL-3.0-or-later {% block content %}

- HTML not finished
{{ title }}

diff --git a/apps/food/templates/food/transformedfood_list.html b/apps/food/templates/food/transformedfood_list.html index fea2dd30..4416cdb7 100644 --- a/apps/food/templates/food/transformedfood_list.html +++ b/apps/food/templates/food/transformedfood_list.html @@ -35,7 +35,7 @@ SPDX-License-Identifier: GPL-3.0-or-later {% if open.data %} {% render_table open %} {% else %} -
class="card-body"> +
{% trans "There is no free meal." %}
@@ -50,7 +50,7 @@ SPDX-License-Identifier: GPL-3.0-or-later {% if table.data %} {% render_table table %} {% else %} -
class="card-body"> +
{% trans "There is no meal." %}
diff --git a/apps/food/views.py b/apps/food/views.py index cddb19f6..9123c757 100644 --- a/apps/food/views.py +++ b/apps/food/views.py @@ -8,15 +8,13 @@ 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, TemplateView +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 member.models import Club -from note_kfet.middlewares import get_current_request -from .forms import AddIngredientForms, BasicFoodForms, QRCodeForms, TransformedFoodForms, FoodForms +from .forms import AddIngredientForms, BasicFoodForms, QRCodeForms, TransformedFoodForms from .models import BasicFood, Food, QRCode, TransformedFood from .tables import TransformedFoodTable @@ -25,7 +23,6 @@ class AddIngredientView(ProtectQuerysetMixin, UpdateView): """ A view to add an ingredient """ - # TO DO : ajouter un champ fully_used dans le form et changer was_eaten en conséquence + mieux filtrer les plat dispo avec des perms model = Food template_name = 'food/add_ingredient_form.html' extra_context = {"title": _("Add the ingredient")} @@ -41,19 +38,22 @@ class AddIngredientView(ProtectQuerysetMixin, UpdateView): form.instance.creater = self.request.user food = Food.objects.get(pk=self.kwargs['pk']) add_ingredient_form = AddIngredientForms(data=self.request.POST) - food_form = FoodForms(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): @@ -67,7 +67,7 @@ class BasicFoodUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView): model = BasicFood form_class = BasicFoodForms template_name = 'food/basicfood_form.html' - extra_context = {"title": _("Add a new aliment")} + extra_context = {"title": _("Update an aliment")} @transaction.atomic def form_valid(self, form): @@ -86,36 +86,29 @@ class BasicFoodUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView): def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) - form = context['form'] - # TO DO : Add perms here - if 1==0: - form.fields['is_active'].widget = HiddenInput() - if 1==0: - form.fields['was_eaten'].widget = HiddenInput() - form.fields['is_active'].help_text = _("Uncheck if the food doesn't exist anymore") - form.fields['was_eaten'].help_text = _("Check if the food has been entirely eaten") - return context -class FoodCreateView(ProtectQuerysetMixin, LoginRequiredMixin, TemplateView): - """ - A view to add a new aliment - """ - template_name = 'food/create_food_form.html' - extra_context = {"title": _("Add a new aliment")} - class FoodView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView): """ A view to see a food """ model = Food - extra_context = {"title": _("Details")} + 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) ##################################################################### @@ -157,9 +150,18 @@ class QRCodeBasicFoodCreateView(ProtectQuerysetMixin, ProtectedCreateView): 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): @@ -210,7 +212,6 @@ class QRCodeCreateView(ProtectQuerysetMixin, ProtectedCreateView): qrcode.save() qrcode.refresh_from_db() - qrcode.food_container.is_ready = True qrcode.food_container.save() return super().form_valid(form) @@ -222,6 +223,7 @@ class QRCodeCreateView(ProtectQuerysetMixin, ProtectedCreateView): def get_sample_object(self): return QRCode( qr_code_number=self.kwargs["slug"], + food_container_id=1 ) @@ -243,11 +245,19 @@ class QRCodeView(ProtectQuerysetMixin, LoginRequiredMixin, DetailView): def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) - # TO DO : Add perms here - context["can_update_basic"]=True - context["can_update_transformed"]=True - context["can_add_ingredient"] = True + 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 @@ -255,9 +265,8 @@ class TransformedFoodCreateView(ProtectQuerysetMixin, ProtectedCreateView): """ A view to add a tranformed food """ - # TO DO : fix the "NotImplementedError" (╯°□°)╯︵ ┻━┻ ... model = TransformedFood - template_name = 'food/transformed_food_form.html' + template_name = 'food/transformedfood_form.html' form_class = TransformedFoodForms extra_context = {"title": _("Add a new meal")} @@ -285,19 +294,35 @@ class TransformedFoodCreateView(ProtectQuerysetMixin, ProtectedCreateView): 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() - - # Field shelf life is only display for authorized user - # TO DO : Add permission here - if not True: - form.fields['shelf_life'].widget = HiddenInput() + form.fields['shelf_life'].widget = HiddenInput() return context @@ -307,9 +332,9 @@ class TransformedFoodUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, Update A view to update transformed product """ model = TransformedFood - template_name = 'food/transformed_food_form.html' - form_class = TransformedFoodForms - extra_context = {'title' : _('Update meal')} + template_name = 'food/transformedfood_form.html' + form_class = TransformedFoodForms + extra_context = {'title': _('Update a meal')} @transaction.atomic def form_valid(self, form): @@ -328,16 +353,9 @@ class TransformedFoodUpdateView(ProtectQuerysetMixin, LoginRequiredMixin, Update def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) - - form = context['form'] - - fields = ['is_active','is_ready','was_eaten','shelf_life'] - # TO DO : Add permissions here - permissions = [True]*len(fields) - for i in range(len(fields)): - if not permissions[i] : form[fields[i]].widget = HiddenInput() return context + class TransformedListView(ProtectQuerysetMixin, LoginRequiredMixin, MultiTableMixin, ListView): """ Displays ready TransformedFood @@ -361,11 +379,11 @@ class TransformedListView(ProtectQuerysetMixin, LoginRequiredMixin, MultiTableMi # 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()) + 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()) + 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") @@ -374,8 +392,18 @@ class TransformedListView(ProtectQuerysetMixin, LoginRequiredMixin, MultiTableMi def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) - # context["can_create_meal"] = PermissionBackend.check_perm(self.request, "food.add_transformedfood", TransformedFood(creation_date = timezone.now(), name = "", expiry_date = timezone.now(), owner = )) <- défi prendre un club qui fonctionne (s'il existe) pour l'utilisateur - context["can_create_meal"] = True + # 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): diff --git a/apps/permission/fixtures/initial.json b/apps/permission/fixtures/initial.json index a4c5ecad..8ea1a452 100644 --- a/apps/permission/fixtures/initial.json +++ b/apps/permission/fixtures/initial.json @@ -3119,7 +3119,7 @@ "food", "transformedfood" ], - "query": "[]", + "query": "{}", "type": "view", "mask": 3, "field": "", @@ -3159,6 +3159,406 @@ "description": "Voir les plats préparés actifs servis" } }, + { + "model": "permission.permission", + "pk": 202, + "fields": { + "model": [ + "food", + "qrcode" + ], + "query": "{}", + "type": "add", + "mask": 3, + "field": "", + "permanent": false, + "description": "Initialiser un QR code de traçabilité" + } + }, + { + "model": "permission.permission", + "pk": 203, + "fields": { + "model": [ + "food", + "basicfood" + ], + "query": "{\"owner\": [\"club\"]}", + "type": "add", + "mask": 3, + "field": "", + "permanent": false, + "description": "Créer un nouvel ingrédient pour son club" + } + }, + { + "model": "permission.permission", + "pk": 204, + "fields": { + "model": [ + "food", + "basicfood" + ], + "query": "{}", + "type": "add", + "mask": 3, + "field": "", + "permanent": false, + "description": "Créer un nouvel ingrédient" + } + }, + { + "model": "permission.permission", + "pk": 205, + "fields": { + "model": [ + "food", + "basicfood" + ], + "query": "{}", + "type": "view", + "mask": 3, + "field": "", + "permanent": false, + "description": "Voir toute la bouffe" + } + }, + { + "model": "permission.permission", + "pk": 206, + "fields": { + "model": [ + "food", + "basicfood" + ], + "query": "{\"is_active\": true}", + "type": "view", + "mask": 3, + "field": "", + "permanent": false, + "description": "Voir toute la bouffe active" + } + }, + { + "model": "permission.permission", + "pk": 207, + "fields": { + "model": [ + "food", + "basicfood" + ], + "query": "{\"is_active\": true, \"owner\": [\"club\"]}", + "type": "view", + "mask": 3, + "field": "", + "permanent": false, + "description": "Voir la bouffe active de son club" + } + }, + { + "model": "permission.permission", + "pk": 208, + "fields": { + "model": [ + "food", + "basicfood" + ], + "query": "{}", + "type": "change", + "mask": 3, + "field": "", + "permanent": false, + "description": "Modifier de la bouffe" + } + }, + { + "model": "permission.permission", + "pk": 209, + "fields": { + "model": [ + "food", + "basicfood" + ], + "query": "{\"is_active\": true, \"was_eaten\": false}", + "type": "change", + "mask": 3, + "field": "allergens", + "permanent": false, + "description": "Modifier les allergènes de la bouffe existante" + } + }, + { + "model": "permission.permission", + "pk": 210, + "fields": { + "model": [ + "food", + "basicfood" + ], + "query": "{\"is_active\": true, \"was_eaten\": false, \"owner\": [\"club\"]}", + "type": "change", + "mask": 3, + "field": "allergens", + "permanent": false, + "description": "Modifier les allergènes de la bouffe appartenant à son club" + } + }, + { + "model": "permission.permission", + "pk": 211, + "fields": { + "model": [ + "food", + "transformedfood" + ], + "query": "{}", + "type": "add", + "mask": 3, + "field": "", + "permanent": false, + "description": "Créer un plat" + } + }, + { + "model": "permission.permission", + "pk": 212, + "fields": { + "model": [ + "food", + "transformedfood" + ], + "query": "{\"owner\": [\"club\"]}", + "type": "add", + "mask": 3, + "field": "", + "permanent": false, + "description": "Créer un plat pour son club" + } + }, + { + "model": "permission.permission", + "pk": 213, + "fields": { + "model": [ + "food", + "transformedfood" + ], + "query": "{}", + "type": "change", + "mask": 3, + "field": "", + "permanent": false, + "description": "Modifier tout les plats" + } + }, + { + "model": "permission.permission", + "pk": 214, + "fields": { + "model": [ + "food", + "transformedfood" + ], + "query": "{\"is_active\": true}", + "type": "change", + "mask": 3, + "field": "was_eaten", + "permanent": false, + "description": "Indiquer si un plat a été mangé" + } + }, + { + "model": "permission.permission", + "pk": 215, + "fields": { + "model": [ + "food", + "transformedfood" + ], + "query": "{\"is_active\": true, \"owner\": [\"club\"]}", + "type": "change", + "mask": 3, + "field": "is_ready", + "permanent": false, + "description": "Indiquer si un plat de son club est prêt" + } + }, + { + "model": "permission.permission", + "pk": 216, + "fields": { + "model": [ + "food", + "transformedfood" + ], + "query": "{\"is_active\": true}", + "type": "change", + "mask": 3, + "field": "is_active", + "permanent": false, + "description": "Archiver un plat" + } + }, + { + "model": "permission.permission", + "pk": 217, + "fields": { + "model": [ + "food", + "basicfood" + ], + "query": "{\"is_active\": true}", + "type": "change", + "mask": 3, + "field": "is_active", + "permanent": false, + "description": "Archiver de la bouffe" + } + }, + { + "model": "permission.permission", + "pk": 218, + "fields": { + "model": [ + "food", + "transformedfood" + ], + "query": "{\"is_active\": true}", + "type": "view", + "mask": 3, + "field": "", + "permanent": false, + "description": "Voir tout les plats actifs" + } + }, + { + "model": "permission.permission", + "pk": 219, + "fields": { + "model": [ + "food", + "qrcode" + ], + "query": "{}", + "type": "view", + "mask": 3, + "field": "", + "permanent": false, + "description": "Voir tous les QR codes" + } + }, + { + "model": "permission.permission", + "pk": 220, + "fields": { + "model": [ + "food", + "qrcode" + ], + "query": "{\"food_container__is_active\": true}", + "type": "view", + "mask": 3, + "field": "", + "permanent": false, + "description": "Voir tous les QR codes actifs" + } + }, + { + "model": "permission.permission", + "pk": 221, + "fields": { + "model": [ + "food", + "qrcode" + ], + "query": "{\"food_container__owner\": [\"club\"], \"food_container__is_active\": true}", + "type": "view", + "mask": 3, + "field": "", + "permanent": false, + "description": "Voir tous les QR codes actifs de son club" + } + }, + { + "model": "permission.permission", + "pk" : 222, + "fields": { + "model": [ + "food", + "transformedfood" + ], + "query": "{\"owner\": [\"club\"], \"is_active\": true}", + "type": "change", + "mask": 3, + "field": "ingredients", + "permanent": false, + "description": "Changer les ingrédients d'un plat actif de son club" + } + }, + { + "model": "permission.permission", + "pk": 223, + "fields": { + "model": [ + "food", + "food" + ], + "query": "{}", + "type": "view", + "mask": 3, + "field": "", + "permanent": false, + "description": "Voir bouffe" + } + }, + { + "model": "permission.permission", + "pk": 224, + "fields": { + "model": [ + "food", + "food" + ], + "query": "{\"is_active\": true}", + "type": "view", + "mask": 3, + "field": "", + "permanent": false, + "description": "Voir bouffe active" + } + }, + { + "model": "permission.permission", + "pk": 225, + "fields": { + "model": [ + "food", + "food" + ], + "query": "{\"is_active\": true, \"owner\": [\"club\"]}", + "type": "view", + "mask": 3, + "field": "", + "permanent": false, + "description": "Voir bouffe active de son club" + } + }, + { + "model": "permission.permission", + "pk": 226, + "fields": { + "model": [ + "food", + "food" + ], + "query": "{}", + "type": "change", + "mask": 3, + "field": "", + "permanent": false, + "description": "Modifier bouffe" + } + }, { "model": "permission.role", "pk": 1, @@ -3266,7 +3666,16 @@ 50, 141, 169, - 200 + 200, + 202, + 203, + 207, + 210, + 212, + 215, + 221, + 222, + 225 ] } }, @@ -3441,7 +3850,20 @@ 167, 168, 182, - 200 + 200, + 202, + 203, + 206, + 209, + 212, + 214, + 215, + 216, + 217, + 218, + 220, + 222, + 224 ] } }, @@ -3671,7 +4093,17 @@ "for_club": 2, "name": "Respo Bouffe", "permissions": [ - 199 + 137, + 199, + 202, + 204, + 205, + 208, + 211, + 213, + 219, + 223, + 226 ] } }, From 549f56dc0b79b1e18fce972198b11306ee1959c3 Mon Sep 17 00:00:00 2001 From: quark Date: Sat, 17 Aug 2024 02:43:56 +0200 Subject: [PATCH 26/29] Translation --- locale/de/LC_MESSAGES/django.po | 337 ++++++++++++++++++++++-------- locale/es/LC_MESSAGES/django.po | 337 ++++++++++++++++++++++-------- locale/fr/LC_MESSAGES/django.po | 349 +++++++++++++++++++++++--------- 3 files changed, 764 insertions(+), 259 deletions(-) diff --git a/locale/de/LC_MESSAGES/django.po b/locale/de/LC_MESSAGES/django.po index 91be9609..821b0ad0 100644 --- a/locale/de/LC_MESSAGES/django.po +++ b/locale/de/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-08-06 14:46+0200\n" +"POT-Creation-Date: 2024-08-17 11:57+0200\n" "PO-Revision-Date: 2020-11-16 20:02+0000\n" "Last-Translator: bleizi \n" "Language-Team: German \n" @@ -336,10 +336,10 @@ msgid "Entry done!" msgstr "Eintrittseite" #: apps/activity/templates/activity/activity_form.html:16 -#: apps/food/templates/food/add_ingredient_form.html:17 -#: apps/food/templates/food/basic_food_form.html:17 -#: apps/food/templates/food/create_qrcode_form.html:20 -#: apps/food/templates/food/transformed_food_form.html:17 +#: apps/food/templates/food/add_ingredient_form.html:16 +#: apps/food/templates/food/basicfood_form.html:16 +#: apps/food/templates/food/create_qrcode_form.html:19 +#: apps/food/templates/food/transformedfood_form.html:16 #: apps/member/templates/member/add_members.html:46 #: apps/member/templates/member/club_form.html:16 #: apps/note/templates/note/transactiontemplate_form.html:18 @@ -445,16 +445,20 @@ msgstr "Eintritt zur Veranstaltung \"{}\"" msgid "API" msgstr "API" -#: apps/food/apps.py:11 apps/food/models.py:96 +#: apps/food/apps.py:11 apps/food/models.py:105 msgid "food" msgstr "" -#: apps/food/forms.py:41 -msgid "pasta" +#: apps/food/forms.py:32 +msgid "Fully used" msgstr "" -#: apps/food/forms.py:85 -msgid "lasagna" +#: apps/food/forms.py:50 +msgid "Pasta METRO 5kg" +msgstr "" + +#: apps/food/forms.py:96 +msgid "Lasagna" msgstr "" #: apps/food/models.py:18 @@ -467,7 +471,7 @@ msgstr "Telefonnummer" msgid "food container" msgstr "" -#: apps/food/models.py:30 apps/food/templates/food/qrcode_detail.html:14 +#: apps/food/models.py:30 msgid "QR-code" msgstr "" @@ -484,8 +488,8 @@ msgstr "" msgid "Allergen" msgstr "" -#: apps/food/models.py:48 apps/food/templates/food/basicfood_detail.html:18 -#: apps/food/templates/food/transformedfood_detail.html:18 +#: apps/food/models.py:48 apps/food/templates/food/basicfood_detail.html:17 +#: apps/food/templates/food/transformedfood_detail.html:20 msgid "Allergens" msgstr "" @@ -507,155 +511,232 @@ msgstr "Geburtsdatum" msgid "was eaten" msgstr "" -#: apps/food/models.py:85 +#: apps/food/models.py:89 msgid "is ready" msgstr "" -#: apps/food/models.py:97 -msgid "foods" -msgstr "" - -#: apps/food/models.py:113 -#, fuzzy -#| msgid "start date" -msgid "arrival date" -msgstr "Anfangsdatum" - -#: apps/food/models.py:143 -msgid "Basic food" -msgstr "" - -#: apps/food/models.py:144 -msgid "Basic foods" -msgstr "" - -#: apps/food/models.py:152 -#, fuzzy -#| msgid "created at" -msgid "creation date" -msgstr "erschafft am" - -#: apps/food/models.py:160 -msgid "transformed ingredient" -msgstr "" - -#: apps/food/models.py:165 +#: apps/food/models.py:94 #, fuzzy #| msgid "active" msgid "is active" msgstr "Aktiv" -#: apps/food/models.py:170 +#: apps/food/models.py:106 +msgid "foods" +msgstr "" + +#: apps/food/models.py:122 +#, fuzzy +#| msgid "start date" +msgid "arrival date" +msgstr "Anfangsdatum" + +#: apps/food/models.py:152 +msgid "Basic food" +msgstr "" + +#: apps/food/models.py:153 +msgid "Basic foods" +msgstr "" + +#: apps/food/models.py:161 +#, fuzzy +#| msgid "created at" +msgid "creation date" +msgstr "erschafft am" + +#: apps/food/models.py:169 +msgid "transformed ingredient" +msgstr "" + +#: apps/food/models.py:174 msgid "shelf life" msgstr "" -#: apps/food/models.py:216 apps/food/views.py:263 +#: apps/food/models.py:225 apps/food/views.py:365 #, fuzzy #| msgid "Transfer money" msgid "Transformed food" msgstr "Geld überweisen" -#: apps/food/models.py:217 +#: apps/food/models.py:226 msgid "Transformed foods" msgstr "" #: apps/food/templates/food/basicfood_detail.html:14 #: apps/food/templates/food/qrcode_detail.html:15 #: apps/food/templates/food/transformedfood_detail.html:14 -#: apps/note/templates/note/transaction_form.html:132 -#: apps/treasury/models.py:60 -msgid "Name" -msgstr "Name" - -#: apps/food/templates/food/basicfood_detail.html:15 -#: apps/food/templates/food/transformedfood_detail.html:15 #, fuzzy #| msgid "Owned" msgid "Owner" msgstr "Besetzt" -#: apps/food/templates/food/basicfood_detail.html:16 +#: apps/food/templates/food/basicfood_detail.html:15 #, fuzzy #| msgid "start date" msgid "Arrival date" msgstr "Anfangsdatum" -#: apps/food/templates/food/basicfood_detail.html:17 -#: apps/food/templates/food/transformedfood_detail.html:17 +#: apps/food/templates/food/basicfood_detail.html:16 +#: apps/food/templates/food/qrcode_detail.html:16 +#: apps/food/templates/food/transformedfood_detail.html:19 #, fuzzy #| msgid "birth date" msgid "Expiry date" msgstr "Geburtsdatum" #: apps/food/templates/food/basicfood_detail.html:24 -#: apps/food/templates/food/qrcode_detail.html:17 -#: apps/food/templates/food/qrcode_detail.html:19 -#: apps/food/templates/food/transformedfood_detail.html:30 +#: apps/food/templates/food/transformedfood_detail.html:36 +#, fuzzy +#| msgid "active" +msgid "Active" +msgstr "Aktiv" + +#: apps/food/templates/food/basicfood_detail.html:25 +#: apps/food/templates/food/transformedfood_detail.html:37 +msgid "Eaten" +msgstr "" + +#: apps/food/templates/food/basicfood_detail.html:28 +#: apps/food/templates/food/qrcode_detail.html:20 +#: apps/food/templates/food/qrcode_detail.html:24 +#: apps/food/templates/food/transformedfood_detail.html:41 #, fuzzy #| msgid "Update bus" msgid "Update" msgstr "Bus bearbeiten" -#: apps/food/templates/food/qrcode_detail.html:21 apps/food/views.py:25 -msgid "Add the ingredient" -msgstr "" +#: apps/food/templates/food/basicfood_detail.html:32 +#: apps/food/templates/food/qrcode_detail.html:34 +#: apps/food/templates/food/transformedfood_detail.html:46 +#, fuzzy +#| msgid "Add team" +msgid "Add to a meal" +msgstr "Neue Team" + +#: apps/food/templates/food/create_qrcode_form.html:14 +#, fuzzy +#| msgid "Transfer money" +msgid "New basic food" +msgstr "Geld überweisen" + +#: apps/food/templates/food/qrcode_detail.html:10 +#, fuzzy +#| msgid "phone number" +msgid "number" +msgstr "Telefonnummer" + +#: apps/food/templates/food/qrcode_detail.html:14 +#: apps/note/templates/note/transaction_form.html:132 +#: apps/treasury/models.py:60 +msgid "Name" +msgstr "Name" + +#: apps/food/templates/food/qrcode_detail.html:29 +#, fuzzy +#| msgid "Profile detail" +msgid "View details" +msgstr "Profile detail" #: apps/food/templates/food/transformedfood_detail.html:16 +#: apps/food/templates/food/transformedfood_detail.html:35 +msgid "Ready" +msgstr "" + +#: apps/food/templates/food/transformedfood_detail.html:18 #, fuzzy #| msgid "created at" msgid "Creation date" msgstr "erschafft am" -#: apps/food/templates/food/transformedfood_detail.html:24 +#: apps/food/templates/food/transformedfood_detail.html:27 msgid "Ingredients" msgstr "" -#: apps/food/templates/food/transformedfood_list.html:12 +#: apps/food/templates/food/transformedfood_detail.html:34 +msgid "Shelf life" +msgstr "" + +#: apps/food/templates/food/transformedfood_list.html:11 +msgid "Meal served" +msgstr "" + +#: apps/food/templates/food/transformedfood_list.html:16 #, fuzzy #| msgid "New user" msgid "New meal" msgstr "Neue User" -#: apps/food/templates/food/transformedfood_list.html:16 +#: apps/food/templates/food/transformedfood_list.html:25 #, fuzzy -#| msgid "WEI registration" -msgid "In preparation" -msgstr "WEI Registrierung" +#| msgid "There is no results." +msgid "There is no meal served." +msgstr "Es gibt keine Ergebnisse." -#: apps/food/templates/food/transformedfood_list.html:20 -msgid "Free" +#: apps/food/templates/food/transformedfood_list.html:33 +msgid "Open" msgstr "" -#: apps/food/views.py:39 -msgid "The product isn't ready" +#: apps/food/templates/food/transformedfood_list.html:40 +#, fuzzy +#| msgid "There is no results." +msgid "There is no free meal." +msgstr "Es gibt keine Ergebnisse." + +#: apps/food/templates/food/transformedfood_list.html:48 +msgid "All meals" msgstr "" -#: apps/food/views.py:63 apps/food/views.py:86 -msgid "Add a new aliment" +#: apps/food/templates/food/transformedfood_list.html:55 +#, fuzzy +#| msgid "There is no results." +msgid "There is no meal." +msgstr "Es gibt keine Ergebnisse." + +#: apps/food/views.py:28 +msgid "Add the ingredient" msgstr "" -#: apps/food/views.py:94 +#: apps/food/views.py:42 +#, fuzzy +#| msgid "This credit is already validated." +msgid "The product is already prepared" +msgstr "Dieser Kredit ist bereits validiert." + +#: apps/food/views.py:70 +#, fuzzy +#| msgid "Update an invoice" +msgid "Update an aliment" +msgstr "Rechnung bearbeiten" + +#: apps/food/views.py:97 #, fuzzy #| msgid "WEI Detail" -msgid "Details" +msgid "Details of:" msgstr "WEI Infos" -#: apps/food/views.py:110 +#: apps/food/views.py:121 msgid "Add a new basic food with QRCode" msgstr "" -#: apps/food/views.py:153 +#: apps/food/views.py:185 msgid "Add a new QRCode" msgstr "" -#: apps/food/views.py:201 +#: apps/food/views.py:235 msgid "QRCode" msgstr "" -#: apps/food/views.py:220 +#: apps/food/views.py:271 msgid "Add a new meal" msgstr "" +#: apps/food/views.py:337 +#, fuzzy +#| msgid "Update team" +msgid "Update a meal" +msgstr "Team bearbeiten" + #: apps/logs/apps.py:11 msgid "Logs" msgstr "Logs" @@ -3908,9 +3989,14 @@ msgstr "" "Sie erhalten haben." #, fuzzy -#~| msgid "Transfer money" -#~ msgid "New transformed food" -#~ msgstr "Geld überweisen" +#~| msgid "Email validation" +#~ msgid "Enter a valid color." +#~ msgstr "Email validierung" + +#, fuzzy +#~| msgid "Email validation" +#~ msgid "Enter a valid value." +#~ msgstr "Email validierung" #, fuzzy #~| msgid "Invitation" @@ -3922,6 +4008,36 @@ msgstr "" #~ msgid "That page contains no results" #~ msgstr "Es gibt keine Ergebnisse." +#, fuzzy +#~| msgid "Email validation" +#~ msgid "Enter a valid URL." +#~ msgstr "Email validierung" + +#, fuzzy +#~| msgid "Email validation" +#~ msgid "Enter a valid integer." +#~ msgstr "Email validierung" + +#, fuzzy +#~| msgid "Email validation" +#~ msgid "Enter a valid email address." +#~ msgstr "Email validierung" + +#, fuzzy +#~| msgid "This activity is not validated yet." +#~ msgid "Enter a valid IPv4 address." +#~ msgstr "Diese Veranstaltung ist noch nicht bestätigt." + +#, fuzzy +#~| msgid "This activity is not validated yet." +#~ msgid "Enter a valid IPv6 address." +#~ msgstr "Diese Veranstaltung ist noch nicht bestätigt." + +#, fuzzy +#~| msgid "This activity is not validated yet." +#~ msgid "Enter a valid IPv4 or IPv6 address." +#~ msgstr "Diese Veranstaltung ist noch nicht bestätigt." + #, fuzzy #~| msgid "phone number" #~ msgid "Enter a number." @@ -3987,16 +4103,51 @@ msgstr "" #~ msgid "Universally unique identifier" #~ msgstr "Rechnungskennung" +#, fuzzy, python-format +#~| msgid "A template with this name already exist" +#~ msgid "%(model)s instance with %(field)s %(value)r does not exist." +#~ msgstr "Eine Vorlage mit diesem Namen ist bereits vorhanden" + #, fuzzy #~| msgid "phone number" #~ msgid "Enter a whole number." #~ msgstr "Telefonnummer" +#, fuzzy +#~| msgid "Email validation" +#~ msgid "Enter a valid date." +#~ msgstr "Email validierung" + +#, fuzzy +#~| msgid "Email validation" +#~ msgid "Enter a valid time." +#~ msgstr "Email validierung" + +#, fuzzy +#~| msgid "Email validation" +#~ msgid "Enter a valid date/time." +#~ msgstr "Email validierung" + #, fuzzy #~| msgid "Email validation" #~ msgid "Enter a valid duration." #~ msgstr "Email validierung" +#, fuzzy +#~| msgid "phone number" +#~ msgid "Enter a complete value." +#~ msgstr "Telefonnummer" + +#, fuzzy +#~| msgid "Email validation" +#~ msgid "Enter a valid UUID." +#~ msgstr "Email validierung" + +#, fuzzy, python-format +#~| msgid "This activity is not validated yet." +#~ msgid "\"%(pk)s\" is not a valid value." +#~ msgstr "Diese Veranstaltung ist noch nicht bestätigt." + #, fuzzy #~| msgid "Current activity" #~ msgid "Currently" @@ -4027,6 +4178,11 @@ msgstr "" #~ msgid "December" #~ msgstr "Mitglied" +#, fuzzy +#~| msgid "add" +#~ msgid "jan" +#~ msgstr "hinzufügen" + #, fuzzy #~| msgid "fee" #~ msgid "feb" @@ -4109,6 +4265,21 @@ msgstr "" #~ msgid "The access token is invalid." #~ msgstr "Diese Adresse muss gültig sein." +#, fuzzy +#~| msgid "This address must be valid." +#~ msgid "The access token has expired." +#~ msgstr "Diese Adresse muss gültig sein." + +#, fuzzy +#~| msgid "This address must be valid." +#~ msgid "The access token is valid but does not have enough scope." +#~ msgstr "Diese Adresse muss gültig sein." + +#, fuzzy +#~| msgid "WEI registration" +#~ msgid "In preparation" +#~ msgstr "WEI Registrierung" + #~ msgid "Join BDA Club" #~ msgstr "BDA Mitglieder werden" diff --git a/locale/es/LC_MESSAGES/django.po b/locale/es/LC_MESSAGES/django.po index a6b18b6e..d6575b40 100644 --- a/locale/es/LC_MESSAGES/django.po +++ b/locale/es/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-08-06 14:46+0200\n" +"POT-Creation-Date: 2024-08-17 11:57+0200\n" "PO-Revision-Date: 2022-04-11 23:12+0200\n" "Last-Translator: bleizi \n" "Language-Team: \n" @@ -331,10 +331,10 @@ msgid "Entry done!" msgstr "Entrada echa !" #: apps/activity/templates/activity/activity_form.html:16 -#: apps/food/templates/food/add_ingredient_form.html:17 -#: apps/food/templates/food/basic_food_form.html:17 -#: apps/food/templates/food/create_qrcode_form.html:20 -#: apps/food/templates/food/transformed_food_form.html:17 +#: apps/food/templates/food/add_ingredient_form.html:16 +#: apps/food/templates/food/basicfood_form.html:16 +#: apps/food/templates/food/create_qrcode_form.html:19 +#: apps/food/templates/food/transformedfood_form.html:16 #: apps/member/templates/member/add_members.html:46 #: apps/member/templates/member/club_form.html:16 #: apps/note/templates/note/transactiontemplate_form.html:18 @@ -442,16 +442,20 @@ msgstr "Entradas para la actividad \"{}\"" msgid "API" msgstr "API" -#: apps/food/apps.py:11 apps/food/models.py:96 +#: apps/food/apps.py:11 apps/food/models.py:105 msgid "food" msgstr "" -#: apps/food/forms.py:41 -msgid "pasta" +#: apps/food/forms.py:32 +msgid "Fully used" msgstr "" -#: apps/food/forms.py:85 -msgid "lasagna" +#: apps/food/forms.py:50 +msgid "Pasta METRO 5kg" +msgstr "" + +#: apps/food/forms.py:96 +msgid "Lasagna" msgstr "" #: apps/food/models.py:18 @@ -464,7 +468,7 @@ msgstr "número de teléfono" msgid "food container" msgstr "" -#: apps/food/models.py:30 apps/food/templates/food/qrcode_detail.html:14 +#: apps/food/models.py:30 msgid "QR-code" msgstr "" @@ -481,8 +485,8 @@ msgstr "" msgid "Allergen" msgstr "" -#: apps/food/models.py:48 apps/food/templates/food/basicfood_detail.html:18 -#: apps/food/templates/food/transformedfood_detail.html:18 +#: apps/food/models.py:48 apps/food/templates/food/basicfood_detail.html:17 +#: apps/food/templates/food/transformedfood_detail.html:20 msgid "Allergens" msgstr "" @@ -504,155 +508,232 @@ msgstr "fecha de nacimiento" msgid "was eaten" msgstr "" -#: apps/food/models.py:85 +#: apps/food/models.py:89 msgid "is ready" msgstr "" -#: apps/food/models.py:97 -msgid "foods" -msgstr "" - -#: apps/food/models.py:113 -#, fuzzy -#| msgid "invalidate" -msgid "arrival date" -msgstr "invalidar" - -#: apps/food/models.py:143 -msgid "Basic food" -msgstr "" - -#: apps/food/models.py:144 -msgid "Basic foods" -msgstr "" - -#: apps/food/models.py:152 -#, fuzzy -#| msgid "created at" -msgid "creation date" -msgstr "creada el" - -#: apps/food/models.py:160 -msgid "transformed ingredient" -msgstr "" - -#: apps/food/models.py:165 +#: apps/food/models.py:94 #, fuzzy #| msgid "active" msgid "is active" msgstr "activo" -#: apps/food/models.py:170 +#: apps/food/models.py:106 +msgid "foods" +msgstr "" + +#: apps/food/models.py:122 +#, fuzzy +#| msgid "invalidate" +msgid "arrival date" +msgstr "invalidar" + +#: apps/food/models.py:152 +msgid "Basic food" +msgstr "" + +#: apps/food/models.py:153 +msgid "Basic foods" +msgstr "" + +#: apps/food/models.py:161 +#, fuzzy +#| msgid "created at" +msgid "creation date" +msgstr "creada el" + +#: apps/food/models.py:169 +msgid "transformed ingredient" +msgstr "" + +#: apps/food/models.py:174 msgid "shelf life" msgstr "" -#: apps/food/models.py:216 apps/food/views.py:263 +#: apps/food/models.py:225 apps/food/views.py:365 #, fuzzy #| msgid "Transfer money" msgid "Transformed food" msgstr "Transferir dinero" -#: apps/food/models.py:217 +#: apps/food/models.py:226 msgid "Transformed foods" msgstr "" #: apps/food/templates/food/basicfood_detail.html:14 #: apps/food/templates/food/qrcode_detail.html:15 #: apps/food/templates/food/transformedfood_detail.html:14 -#: apps/note/templates/note/transaction_form.html:132 -#: apps/treasury/models.py:60 -msgid "Name" -msgstr "Nombre" - -#: apps/food/templates/food/basicfood_detail.html:15 -#: apps/food/templates/food/transformedfood_detail.html:15 #, fuzzy #| msgid "Owned" msgid "Owner" msgstr "Tenido" -#: apps/food/templates/food/basicfood_detail.html:16 +#: apps/food/templates/food/basicfood_detail.html:15 #, fuzzy #| msgid "invalidate" msgid "Arrival date" msgstr "invalidar" -#: apps/food/templates/food/basicfood_detail.html:17 -#: apps/food/templates/food/transformedfood_detail.html:17 +#: apps/food/templates/food/basicfood_detail.html:16 +#: apps/food/templates/food/qrcode_detail.html:16 +#: apps/food/templates/food/transformedfood_detail.html:19 #, fuzzy #| msgid "birth date" msgid "Expiry date" msgstr "fecha de nacimiento" #: apps/food/templates/food/basicfood_detail.html:24 -#: apps/food/templates/food/qrcode_detail.html:17 -#: apps/food/templates/food/qrcode_detail.html:19 -#: apps/food/templates/food/transformedfood_detail.html:30 +#: apps/food/templates/food/transformedfood_detail.html:36 +#, fuzzy +#| msgid "active" +msgid "Active" +msgstr "activo" + +#: apps/food/templates/food/basicfood_detail.html:25 +#: apps/food/templates/food/transformedfood_detail.html:37 +msgid "Eaten" +msgstr "" + +#: apps/food/templates/food/basicfood_detail.html:28 +#: apps/food/templates/food/qrcode_detail.html:20 +#: apps/food/templates/food/qrcode_detail.html:24 +#: apps/food/templates/food/transformedfood_detail.html:41 #, fuzzy #| msgid "Update bus" msgid "Update" msgstr "Modificar el bus" -#: apps/food/templates/food/qrcode_detail.html:21 apps/food/views.py:25 -msgid "Add the ingredient" -msgstr "" +#: apps/food/templates/food/basicfood_detail.html:32 +#: apps/food/templates/food/qrcode_detail.html:34 +#: apps/food/templates/food/transformedfood_detail.html:46 +#, fuzzy +#| msgid "Add team" +msgid "Add to a meal" +msgstr "Añadir un equipo" + +#: apps/food/templates/food/create_qrcode_form.html:14 +#, fuzzy +#| msgid "Transfer money" +msgid "New basic food" +msgstr "Transferir dinero" + +#: apps/food/templates/food/qrcode_detail.html:10 +#, fuzzy +#| msgid "phone number" +msgid "number" +msgstr "número de teléfono" + +#: apps/food/templates/food/qrcode_detail.html:14 +#: apps/note/templates/note/transaction_form.html:132 +#: apps/treasury/models.py:60 +msgid "Name" +msgstr "Nombre" + +#: apps/food/templates/food/qrcode_detail.html:29 +#, fuzzy +#| msgid "Profile detail" +msgid "View details" +msgstr "Detalles del usuario" #: apps/food/templates/food/transformedfood_detail.html:16 +#: apps/food/templates/food/transformedfood_detail.html:35 +msgid "Ready" +msgstr "" + +#: apps/food/templates/food/transformedfood_detail.html:18 #, fuzzy #| msgid "created at" msgid "Creation date" msgstr "creada el" -#: apps/food/templates/food/transformedfood_detail.html:24 +#: apps/food/templates/food/transformedfood_detail.html:27 msgid "Ingredients" msgstr "" -#: apps/food/templates/food/transformedfood_list.html:12 +#: apps/food/templates/food/transformedfood_detail.html:34 +msgid "Shelf life" +msgstr "" + +#: apps/food/templates/food/transformedfood_list.html:11 +msgid "Meal served" +msgstr "" + +#: apps/food/templates/food/transformedfood_list.html:16 #, fuzzy #| msgid "New user" msgid "New meal" msgstr "Nuevo usuario" -#: apps/food/templates/food/transformedfood_list.html:16 +#: apps/food/templates/food/transformedfood_list.html:25 #, fuzzy -#| msgid "WEI registration" -msgid "In preparation" -msgstr "Apuntación al WEI" +#| msgid "There is no results." +msgid "There is no meal served." +msgstr "No hay resultado." -#: apps/food/templates/food/transformedfood_list.html:20 -msgid "Free" +#: apps/food/templates/food/transformedfood_list.html:33 +msgid "Open" msgstr "" -#: apps/food/views.py:39 -msgid "The product isn't ready" +#: apps/food/templates/food/transformedfood_list.html:40 +#, fuzzy +#| msgid "There is no results." +msgid "There is no free meal." +msgstr "No hay resultado." + +#: apps/food/templates/food/transformedfood_list.html:48 +msgid "All meals" msgstr "" -#: apps/food/views.py:63 apps/food/views.py:86 -msgid "Add a new aliment" +#: apps/food/templates/food/transformedfood_list.html:55 +#, fuzzy +#| msgid "There is no results." +msgid "There is no meal." +msgstr "No hay resultado." + +#: apps/food/views.py:28 +msgid "Add the ingredient" msgstr "" -#: apps/food/views.py:94 +#: apps/food/views.py:42 +#, fuzzy +#| msgid "This credit is already validated." +msgid "The product is already prepared" +msgstr "Este crédito ya fue validado." + +#: apps/food/views.py:70 +#, fuzzy +#| msgid "Update an invoice" +msgid "Update an aliment" +msgstr "Modificar una factura" + +#: apps/food/views.py:97 #, fuzzy #| msgid "WEI Detail" -msgid "Details" +msgid "Details of:" msgstr "Detalles del WEI" -#: apps/food/views.py:110 +#: apps/food/views.py:121 msgid "Add a new basic food with QRCode" msgstr "" -#: apps/food/views.py:153 +#: apps/food/views.py:185 msgid "Add a new QRCode" msgstr "" -#: apps/food/views.py:201 +#: apps/food/views.py:235 msgid "QRCode" msgstr "" -#: apps/food/views.py:220 +#: apps/food/views.py:271 msgid "Add a new meal" msgstr "" +#: apps/food/views.py:337 +#, fuzzy +#| msgid "Update team" +msgid "Update a meal" +msgstr "Modificar el equipo" + #: apps/logs/apps.py:11 msgid "Logs" msgstr "Logs" @@ -3832,9 +3913,14 @@ msgstr "" "enlace que recibió." #, fuzzy -#~| msgid "Transfer money" -#~ msgid "New transformed food" -#~ msgstr "Transferir dinero" +#~| msgid "invalidate" +#~ msgid "Enter a valid color." +#~ msgstr "invalidar" + +#, fuzzy +#~| msgid "invalidate" +#~ msgid "Enter a valid value." +#~ msgstr "invalidar" #, fuzzy #~| msgid "Invitation" @@ -3846,6 +3932,36 @@ msgstr "" #~ msgid "That page contains no results" #~ msgstr "No hay resultado." +#, fuzzy +#~| msgid "invalidate" +#~ msgid "Enter a valid URL." +#~ msgstr "invalidar" + +#, fuzzy +#~| msgid "invalidate" +#~ msgid "Enter a valid integer." +#~ msgstr "invalidar" + +#, fuzzy +#~| msgid "invalidate" +#~ msgid "Enter a valid email address." +#~ msgstr "invalidar" + +#, fuzzy +#~| msgid "This activity is not validated yet." +#~ msgid "Enter a valid IPv4 address." +#~ msgstr "Esta actividad no fue validada por ahora." + +#, fuzzy +#~| msgid "This activity is not validated yet." +#~ msgid "Enter a valid IPv6 address." +#~ msgstr "Esta actividad no fue validada por ahora." + +#, fuzzy +#~| msgid "This activity is not validated yet." +#~ msgid "Enter a valid IPv4 or IPv6 address." +#~ msgstr "Esta actividad no fue validada por ahora." + #, fuzzy #~| msgid "phone number" #~ msgid "Enter a number." @@ -3911,6 +4027,11 @@ msgstr "" #~ msgid "Universally unique identifier" #~ msgstr "Numero de factura" +#, fuzzy, python-format +#~| msgid "A template with this name already exist" +#~ msgid "%(model)s instance with %(field)s %(value)r does not exist." +#~ msgstr "Un plantilla de transacción con un nombre similar ya existe" + #, fuzzy #~| msgid "phone number" #~ msgid "Enter a whole number." @@ -3921,11 +4042,41 @@ msgstr "" #~ msgid "Enter a valid date." #~ msgstr "invalidar" +#, fuzzy +#~| msgid "invalidate" +#~ msgid "Enter a valid time." +#~ msgstr "invalidar" + +#, fuzzy +#~| msgid "invalidate" +#~ msgid "Enter a valid date/time." +#~ msgstr "invalidar" + #, fuzzy #~| msgid "Email validation" #~ msgid "Enter a valid duration." #~ msgstr "Validación del correo electrónico" +#, fuzzy +#~| msgid "invalidate" +#~ msgid "Enter a list of values." +#~ msgstr "invalidar" + +#, fuzzy +#~| msgid "phone number" +#~ msgid "Enter a complete value." +#~ msgstr "número de teléfono" + +#, fuzzy +#~| msgid "invalidate" +#~ msgid "Enter a valid UUID." +#~ msgstr "invalidar" + +#, fuzzy, python-format +#~| msgid "This activity is not validated yet." +#~ msgid "\"%(pk)s\" is not a valid value." +#~ msgstr "Esta actividad no fue validada por ahora." + #, fuzzy #~| msgid "Current activity" #~ msgid "Currently" @@ -3956,6 +4107,11 @@ msgstr "" #~ msgid "December" #~ msgstr "miembro" +#, fuzzy +#~| msgid "add" +#~ msgid "jan" +#~ msgstr "añadir" + #, fuzzy #~| msgid "fee" #~ msgid "feb" @@ -4028,6 +4184,11 @@ msgstr "" #~ msgid "No week specified" #~ msgstr "Ningún motivo dado" +#, fuzzy +#~| msgid "Client secret" +#~ msgid "Confidential" +#~ msgstr "Secreto cliente" + #, fuzzy #~| msgid "Authorization:" #~ msgid "Authorization code" @@ -4048,6 +4209,11 @@ msgstr "" #~ msgid "The access token is invalid." #~ msgstr "Este correo tiene que ser valido." +#, fuzzy +#~| msgid "This address must be valid." +#~ msgid "The access token has expired." +#~ msgstr "Este correo tiene que ser valido." + #, fuzzy #~| msgid "The user does not have enough money." #~ msgid "The access token is valid but does not have enough scope." @@ -4058,6 +4224,11 @@ msgstr "" #~ msgid "Application requires following permissions" #~ msgstr "La aplicación necesita los derechos siguientes :" +#, fuzzy +#~| msgid "WEI registration" +#~ msgid "In preparation" +#~ msgstr "Apuntación al WEI" + #~ msgid "Join BDA Club" #~ msgstr "Afiliarse al club BDA" diff --git a/locale/fr/LC_MESSAGES/django.po b/locale/fr/LC_MESSAGES/django.po index 9793d6d9..ecd34415 100644 --- a/locale/fr/LC_MESSAGES/django.po +++ b/locale/fr/LC_MESSAGES/django.po @@ -7,7 +7,7 @@ msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2024-08-06 14:46+0200\n" +"POT-Creation-Date: 2024-08-17 11:57+0200\n" "PO-Revision-Date: 2022-04-11 22:05+0200\n" "Last-Translator: bleizi \n" "Language-Team: French \n" @@ -334,10 +334,10 @@ msgid "Entry done!" msgstr "Entrée effectuée !" #: apps/activity/templates/activity/activity_form.html:16 -#: apps/food/templates/food/add_ingredient_form.html:17 -#: apps/food/templates/food/basic_food_form.html:17 -#: apps/food/templates/food/create_qrcode_form.html:20 -#: apps/food/templates/food/transformed_food_form.html:17 +#: apps/food/templates/food/add_ingredient_form.html:16 +#: apps/food/templates/food/basicfood_form.html:16 +#: apps/food/templates/food/create_qrcode_form.html:19 +#: apps/food/templates/food/transformedfood_form.html:16 #: apps/member/templates/member/add_members.html:46 #: apps/member/templates/member/club_form.html:16 #: apps/note/templates/note/transactiontemplate_form.html:18 @@ -445,17 +445,21 @@ msgstr "Entrées pour l'activité « {} »" msgid "API" msgstr "API" -#: apps/food/apps.py:11 apps/food/models.py:96 +#: apps/food/apps.py:11 apps/food/models.py:105 msgid "food" msgstr "bouffe" -#: apps/food/forms.py:41 -msgid "pasta" -msgstr "pâtes" +#: apps/food/forms.py:32 +msgid "Fully used" +msgstr "Entièrement utilisé" -#: apps/food/forms.py:85 -msgid "lasagna" -msgstr "lasagnes" +#: apps/food/forms.py:50 +msgid "Pasta METRO 5kg" +msgstr "Pâtes METRO 5kg" + +#: apps/food/forms.py:96 +msgid "Lasagna" +msgstr "Lasagnes" #: apps/food/models.py:18 msgid "QR-code number" @@ -465,7 +469,7 @@ msgstr "numéro de QR-code" msgid "food container" msgstr "récipient" -#: apps/food/models.py:30 apps/food/templates/food/qrcode_detail.html:14 +#: apps/food/models.py:30 msgid "QR-code" msgstr "QR-code" @@ -482,8 +486,8 @@ msgstr "numéro du QR-code {qr_code_number}" msgid "Allergen" msgstr "Allergène" -#: apps/food/models.py:48 apps/food/templates/food/basicfood_detail.html:18 -#: apps/food/templates/food/transformedfood_detail.html:18 +#: apps/food/models.py:48 apps/food/templates/food/basicfood_detail.html:17 +#: apps/food/templates/food/transformedfood_detail.html:20 msgid "Allergens" msgstr "Allergènes" @@ -503,131 +507,188 @@ msgstr "date de péremption" msgid "was eaten" msgstr "a été mangé" -#: apps/food/models.py:85 +#: apps/food/models.py:89 msgid "is ready" msgstr "est prêt" -#: apps/food/models.py:97 -msgid "foods" -msgstr "bouffes" - -#: apps/food/models.py:113 -msgid "arrival date" -msgstr "date d'arrivée" - -#: apps/food/models.py:143 -msgid "Basic food" -msgstr "Bouffe basique" - -#: apps/food/models.py:144 -msgid "Basic foods" -msgstr "Bouffes basiques" - -#: apps/food/models.py:152 -msgid "creation date" -msgstr "date de création" - -#: apps/food/models.py:160 -msgid "transformed ingredient" -msgstr "ingrédients tranformées" - -#: apps/food/models.py:165 +#: apps/food/models.py:94 msgid "is active" msgstr "est en cours" -#: apps/food/models.py:170 +#: apps/food/models.py:106 +msgid "foods" +msgstr "bouffes" + +#: apps/food/models.py:122 +msgid "arrival date" +msgstr "date d'arrivée" + +#: apps/food/models.py:152 +msgid "Basic food" +msgstr "Aliment basique" + +#: apps/food/models.py:153 +msgid "Basic foods" +msgstr "Aliments basiques" + +#: apps/food/models.py:161 +msgid "creation date" +msgstr "date de création" + +#: apps/food/models.py:169 +msgid "transformed ingredient" +msgstr "ingrédients tranformées" + +#: apps/food/models.py:174 msgid "shelf life" msgstr "durée de vie" -#: apps/food/models.py:216 apps/food/views.py:263 +#: apps/food/models.py:225 apps/food/views.py:365 msgid "Transformed food" -msgstr "Bouffe transformée" +msgstr "Aliment transformé" -#: apps/food/models.py:217 +#: apps/food/models.py:226 msgid "Transformed foods" -msgstr "Bouffes transformées" +msgstr "Aliments transformés" #: apps/food/templates/food/basicfood_detail.html:14 #: apps/food/templates/food/qrcode_detail.html:15 #: apps/food/templates/food/transformedfood_detail.html:14 +msgid "Owner" +msgstr "Propriétaire" + +#: apps/food/templates/food/basicfood_detail.html:15 +msgid "Arrival date" +msgstr "Date d'arrivée" + +#: apps/food/templates/food/basicfood_detail.html:16 +#: apps/food/templates/food/qrcode_detail.html:16 +#: apps/food/templates/food/transformedfood_detail.html:19 +msgid "Expiry date" +msgstr "Date de péremption" + +#: apps/food/templates/food/basicfood_detail.html:24 +#: apps/food/templates/food/transformedfood_detail.html:36 +msgid "Active" +msgstr "Actif" + +#: apps/food/templates/food/basicfood_detail.html:25 +#: apps/food/templates/food/transformedfood_detail.html:37 +msgid "Eaten" +msgstr "Mangé" + +#: apps/food/templates/food/basicfood_detail.html:28 +#: apps/food/templates/food/qrcode_detail.html:20 +#: apps/food/templates/food/qrcode_detail.html:24 +#: apps/food/templates/food/transformedfood_detail.html:41 +msgid "Update" +msgstr "Modifier" + +#: apps/food/templates/food/basicfood_detail.html:32 +#: apps/food/templates/food/qrcode_detail.html:34 +#: apps/food/templates/food/transformedfood_detail.html:46 +msgid "Add to a meal" +msgstr "Ajouter à un plat" + +#: apps/food/templates/food/create_qrcode_form.html:14 +msgid "New basic food" +msgstr "Nouvel aliment basique" + +#: apps/food/templates/food/qrcode_detail.html:10 +msgid "number" +msgstr "numéro" + +#: apps/food/templates/food/qrcode_detail.html:14 #: apps/note/templates/note/transaction_form.html:132 #: apps/treasury/models.py:60 msgid "Name" msgstr "Nom" -#: apps/food/templates/food/basicfood_detail.html:15 -#: apps/food/templates/food/transformedfood_detail.html:15 -msgid "Owner" -msgstr "Propriétaire" - -#: apps/food/templates/food/basicfood_detail.html:16 -msgid "Arrival date" -msgstr "Date d'arrivée" - -#: apps/food/templates/food/basicfood_detail.html:17 -#: apps/food/templates/food/transformedfood_detail.html:17 -msgid "Expiry date" -msgstr "Date de péremption" - -#: apps/food/templates/food/basicfood_detail.html:24 -#: apps/food/templates/food/qrcode_detail.html:17 -#: apps/food/templates/food/qrcode_detail.html:19 -#: apps/food/templates/food/transformedfood_detail.html:30 -msgid "Update" -msgstr "Modifier" - -#: apps/food/templates/food/qrcode_detail.html:21 apps/food/views.py:25 -msgid "Add the ingredient" -msgstr "Ajouter un ingrédient" +#: apps/food/templates/food/qrcode_detail.html:29 +msgid "View details" +msgstr "Voir plus" #: apps/food/templates/food/transformedfood_detail.html:16 +#: apps/food/templates/food/transformedfood_detail.html:35 +msgid "Ready" +msgstr "Prêt" + +#: apps/food/templates/food/transformedfood_detail.html:18 msgid "Creation date" msgstr "Date de création" -#: apps/food/templates/food/transformedfood_detail.html:24 +#: apps/food/templates/food/transformedfood_detail.html:27 msgid "Ingredients" msgstr "Ingrédients" -#: apps/food/templates/food/transformedfood_list.html:12 +#: apps/food/templates/food/transformedfood_detail.html:34 +msgid "Shelf life" +msgstr "Durée de vie" + +#: apps/food/templates/food/transformedfood_list.html:11 +msgid "Meal served" +msgstr "Plat servis" + +#: apps/food/templates/food/transformedfood_list.html:16 msgid "New meal" msgstr "Nouveau plat" -#: apps/food/templates/food/transformedfood_list.html:16 -msgid "In preparation" -msgstr "En cours de préparation" +#: apps/food/templates/food/transformedfood_list.html:25 +msgid "There is no meal served." +msgstr "Il n'y a pas de plat servi." -#: apps/food/templates/food/transformedfood_list.html:20 -msgid "Free" +#: apps/food/templates/food/transformedfood_list.html:33 +msgid "Open" msgstr "Open" -#: apps/food/views.py:39 -msgid "The product isn't ready" -msgstr "Le produit n'est pas prêt" +#: apps/food/templates/food/transformedfood_list.html:40 +msgid "There is no free meal." +msgstr "Il n'y a pas de plat en open" -#: apps/food/views.py:63 apps/food/views.py:86 -msgid "Add a new aliment" -msgstr "Ajouter un nouvel aliment" +#: apps/food/templates/food/transformedfood_list.html:48 +msgid "All meals" +msgstr "Tout les plats" -#: apps/food/views.py:94 -msgid "Details" -msgstr "Détails" +#: apps/food/templates/food/transformedfood_list.html:55 +msgid "There is no meal." +msgstr "Il n'y a pas de plat" -#: apps/food/views.py:110 +#: apps/food/views.py:28 +msgid "Add the ingredient" +msgstr "Ajouter un ingrédient" + +#: apps/food/views.py:42 +msgid "The product is already prepared" +msgstr "Le produit est déjà prêt" + +#: apps/food/views.py:70 +msgid "Update an aliment" +msgstr "Modifier un aliment" + +#: apps/food/views.py:97 +msgid "Details of:" +msgstr "Détails de:" + +#: apps/food/views.py:121 msgid "Add a new basic food with QRCode" msgstr "Ajouter un nouvel ingrédient avec un QR-code" -#: apps/food/views.py:153 +#: apps/food/views.py:185 msgid "Add a new QRCode" msgstr "Ajouter un nouveau QR-code" -#: apps/food/views.py:201 +#: apps/food/views.py:235 msgid "QRCode" msgstr "QR-code" -#: apps/food/views.py:220 +#: apps/food/views.py:271 msgid "Add a new meal" msgstr "Ajouter un nouveau plat" +#: apps/food/views.py:337 +msgid "Update a meal" +msgstr "Modifier le plat" + #: apps/logs/apps.py:11 msgid "Logs" msgstr "Logs" @@ -3824,9 +3885,14 @@ msgstr "" "lien que vous avez reçu." #, fuzzy -#~| msgid "Transformed food" -#~ msgid "New transformed food" -#~ msgstr "Bouffe transformée" +#~| msgid "invalidate" +#~ msgid "Enter a valid color." +#~ msgstr "dévalider" + +#, fuzzy +#~| msgid "invalidate" +#~ msgid "Enter a valid value." +#~ msgstr "dévalider" #, fuzzy #~| msgid "Invitation" @@ -3838,6 +3904,36 @@ msgstr "" #~ msgid "That page contains no results" #~ msgstr "Il n'y a pas de résultat." +#, fuzzy +#~| msgid "invalidate" +#~ msgid "Enter a valid URL." +#~ msgstr "dévalider" + +#, fuzzy +#~| msgid "invalidate" +#~ msgid "Enter a valid integer." +#~ msgstr "dévalider" + +#, fuzzy +#~| msgid "invalidate" +#~ msgid "Enter a valid email address." +#~ msgstr "dévalider" + +#, fuzzy +#~| msgid "This activity is not validated yet." +#~ msgid "Enter a valid IPv4 address." +#~ msgstr "Cette activité n'est pas encore validée." + +#, fuzzy +#~| msgid "This activity is not validated yet." +#~ msgid "Enter a valid IPv6 address." +#~ msgstr "Cette activité n'est pas encore validée." + +#, fuzzy +#~| msgid "This activity is not validated yet." +#~ msgid "Enter a valid IPv4 or IPv6 address." +#~ msgstr "Cette activité n'est pas encore validée." + #, fuzzy #~| msgid "phone number" #~ msgid "Enter a number." @@ -3903,6 +3999,11 @@ msgstr "" #~ msgid "Universally unique identifier" #~ msgstr "Numéro de facture" +#, fuzzy, python-format +#~| msgid "A template with this name already exist" +#~ msgid "%(model)s instance with %(field)s %(value)r does not exist." +#~ msgstr "Un modèle de transaction avec un nom similaire existe déjà" + #, fuzzy #~| msgid "phone number" #~ msgid "Enter a whole number." @@ -3913,11 +4014,41 @@ msgstr "" #~ msgid "Enter a valid date." #~ msgstr "dévalider" +#, fuzzy +#~| msgid "invalidate" +#~ msgid "Enter a valid time." +#~ msgstr "dévalider" + +#, fuzzy +#~| msgid "invalidate" +#~ msgid "Enter a valid date/time." +#~ msgstr "dévalider" + #, fuzzy #~| msgid "Email validation" #~ msgid "Enter a valid duration." #~ msgstr "Validation de l'adresse mail" +#, fuzzy +#~| msgid "invalidate" +#~ msgid "Enter a list of values." +#~ msgstr "dévalider" + +#, fuzzy +#~| msgid "phone number" +#~ msgid "Enter a complete value." +#~ msgstr "numéro de téléphone" + +#, fuzzy +#~| msgid "invalidate" +#~ msgid "Enter a valid UUID." +#~ msgstr "dévalider" + +#, fuzzy, python-format +#~| msgid "This activity is not validated yet." +#~ msgid "\"%(pk)s\" is not a valid value." +#~ msgstr "Cette activité n'est pas encore validée." + #, fuzzy #~| msgid "Current activity" #~ msgid "Currently" @@ -3948,6 +4079,11 @@ msgstr "" #~ msgid "December" #~ msgstr "adhérent·e" +#, fuzzy +#~| msgid "add" +#~ msgid "jan" +#~ msgstr "ajouter" + #, fuzzy #~| msgid "fee" #~ msgid "feb" @@ -4020,6 +4156,11 @@ msgstr "" #~ msgid "No week specified" #~ msgstr "Pas de motif spécifié" +#, fuzzy +#~| msgid "Client secret" +#~ msgid "Confidential" +#~ msgstr "Secret client" + #, fuzzy #~| msgid "Authorization:" #~ msgid "Authorization code" @@ -4040,6 +4181,11 @@ msgstr "" #~ msgid "The access token is invalid." #~ msgstr "Cette adresse doit être valide." +#, fuzzy +#~| msgid "This address must be valid." +#~ msgid "The access token has expired." +#~ msgstr "Cette adresse doit être valide." + #, fuzzy #~| msgid "The user does not have enough money." #~ msgid "The access token is valid but does not have enough scope." @@ -4050,6 +4196,23 @@ msgstr "" #~ msgid "Application requires following permissions" #~ msgstr "L'application requiert les permissions suivantes :" +#~ msgid "pasta" +#~ msgstr "pâtes" + +#~ msgid "In preparation" +#~ msgstr "En cours de préparation" + +#~ msgid "Free" +#~ msgstr "Open" + +#~ msgid "Add a new aliment" +#~ msgstr "Ajouter un nouvel aliment" + +#, fuzzy +#~| msgid "Transformed food" +#~ msgid "New transformed food" +#~ msgstr "Bouffe transformée" + #, fuzzy #~| msgid "People having you as a friend" #~ msgid "You already have that person as a friend" From 213e9a8b1241b1942472d535a227360729b4f75c Mon Sep 17 00:00:00 2001 From: quark Date: Tue, 27 Aug 2024 10:45:53 +0200 Subject: [PATCH 27/29] Fix problem in addingredientform, change filter for container in QrcodeForm --- apps/food/forms.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/apps/food/forms.py b/apps/food/forms.py index 0b2532aa..7551a8c5 100644 --- a/apps/food/forms.py +++ b/apps/food/forms.py @@ -22,7 +22,6 @@ class AddIngredientForms(forms.ModelForm): super().__init__(*args, **kwargs) self.fields['ingredient'].queryset = self.fields['ingredient'].queryset.filter( polymorphic_ctype__model='transformedfood', - owner_id=self.instance.owner_id, is_ready=False, is_active=True, was_eaten=False, @@ -70,7 +69,11 @@ class QRCodeForms(forms.ModelForm): """ def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) - self.fields['food_container'].queryset = self.fields['food_container'].queryset.filter(is_ready=False) + self.fields['food_container'].queryset = self.fields['food_container'].queryset.filter( + is_active=True, + was_eaten=False, + polymorphic_ctype__model='transformedfood', + ) class Meta: model = QRCode From f2cb10b69f8a8065f74030bd2291596b4b9001f4 Mon Sep 17 00:00:00 2001 From: quark Date: Tue, 27 Aug 2024 10:45:53 +0200 Subject: [PATCH 28/29] Fix problem in addingredientform, change filter for container in QrcodeForm --- apps/permission/fixtures/initial.json | 110 +++++++++++++------------- 1 file changed, 55 insertions(+), 55 deletions(-) diff --git a/apps/permission/fixtures/initial.json b/apps/permission/fixtures/initial.json index 8ea1a452..b23a9e52 100644 --- a/apps/permission/fixtures/initial.json +++ b/apps/permission/fixtures/initial.json @@ -3113,7 +3113,7 @@ }, { "model": "permission.permission", - "pk": 199, + "pk": 211, "fields": { "model": [ "food", @@ -3129,7 +3129,7 @@ }, { "model": "permission.permission", - "pk": 200, + "pk": 212, "fields": { "model": [ "food", @@ -3145,7 +3145,7 @@ }, { "model": "permission.permission", - "pk": 201, + "pk": 213, "fields": { "model": [ "food", @@ -3161,7 +3161,7 @@ }, { "model": "permission.permission", - "pk": 202, + "pk": 214, "fields": { "model": [ "food", @@ -3177,7 +3177,7 @@ }, { "model": "permission.permission", - "pk": 203, + "pk": 215, "fields": { "model": [ "food", @@ -3193,7 +3193,7 @@ }, { "model": "permission.permission", - "pk": 204, + "pk": 216, "fields": { "model": [ "food", @@ -3209,7 +3209,7 @@ }, { "model": "permission.permission", - "pk": 205, + "pk": 217, "fields": { "model": [ "food", @@ -3225,7 +3225,7 @@ }, { "model": "permission.permission", - "pk": 206, + "pk": 218, "fields": { "model": [ "food", @@ -3241,7 +3241,7 @@ }, { "model": "permission.permission", - "pk": 207, + "pk": 219, "fields": { "model": [ "food", @@ -3257,7 +3257,7 @@ }, { "model": "permission.permission", - "pk": 208, + "pk": 220, "fields": { "model": [ "food", @@ -3273,7 +3273,7 @@ }, { "model": "permission.permission", - "pk": 209, + "pk": 221, "fields": { "model": [ "food", @@ -3289,7 +3289,7 @@ }, { "model": "permission.permission", - "pk": 210, + "pk": 222, "fields": { "model": [ "food", @@ -3305,7 +3305,7 @@ }, { "model": "permission.permission", - "pk": 211, + "pk": 223, "fields": { "model": [ "food", @@ -3321,7 +3321,7 @@ }, { "model": "permission.permission", - "pk": 212, + "pk": 224, "fields": { "model": [ "food", @@ -3337,7 +3337,7 @@ }, { "model": "permission.permission", - "pk": 213, + "pk": 225, "fields": { "model": [ "food", @@ -3353,7 +3353,7 @@ }, { "model": "permission.permission", - "pk": 214, + "pk": 226, "fields": { "model": [ "food", @@ -3369,7 +3369,7 @@ }, { "model": "permission.permission", - "pk": 215, + "pk": 227, "fields": { "model": [ "food", @@ -3385,7 +3385,7 @@ }, { "model": "permission.permission", - "pk": 216, + "pk": 228, "fields": { "model": [ "food", @@ -3401,7 +3401,7 @@ }, { "model": "permission.permission", - "pk": 217, + "pk": 229, "fields": { "model": [ "food", @@ -3417,7 +3417,7 @@ }, { "model": "permission.permission", - "pk": 218, + "pk": 230, "fields": { "model": [ "food", @@ -3433,7 +3433,7 @@ }, { "model": "permission.permission", - "pk": 219, + "pk": 231, "fields": { "model": [ "food", @@ -3449,7 +3449,7 @@ }, { "model": "permission.permission", - "pk": 220, + "pk": 232, "fields": { "model": [ "food", @@ -3465,7 +3465,7 @@ }, { "model": "permission.permission", - "pk": 221, + "pk": 233, "fields": { "model": [ "food", @@ -3481,7 +3481,7 @@ }, { "model": "permission.permission", - "pk" : 222, + "pk" : 234, "fields": { "model": [ "food", @@ -3497,7 +3497,7 @@ }, { "model": "permission.permission", - "pk": 223, + "pk": 235, "fields": { "model": [ "food", @@ -3513,7 +3513,7 @@ }, { "model": "permission.permission", - "pk": 224, + "pk": 236, "fields": { "model": [ "food", @@ -3529,7 +3529,7 @@ }, { "model": "permission.permission", - "pk": 225, + "pk": 237, "fields": { "model": [ "food", @@ -3545,7 +3545,7 @@ }, { "model": "permission.permission", - "pk": 226, + "pk": 238, "fields": { "model": [ "food", @@ -3639,7 +3639,7 @@ 158, 159, 160, - 201 + 213 ] } }, @@ -3666,16 +3666,16 @@ 50, 141, 169, - 200, - 202, - 203, - 207, - 210, 212, + 214, 215, - 221, + 219, 222, - 225 + 224, + 227, + 233, + 234, + 237 ] } }, @@ -3850,20 +3850,20 @@ 167, 168, 182, - 200, - 202, - 203, - 206, - 209, 212, 214, 215, - 216, - 217, 218, - 220, - 222, - 224 + 221, + 224, + 226, + 227, + 228, + 229, + 230, + 232, + 234, + 236 ] } }, @@ -4068,7 +4068,7 @@ 176, 177, 197, - 199 + 211 ] } }, @@ -4094,16 +4094,16 @@ "name": "Respo Bouffe", "permissions": [ 137, - 199, - 202, - 204, - 205, - 208, 211, - 213, - 219, + 214, + 216, + 217, + 220, 223, - 226 + 225, + 231, + 235, + 238 ] } }, From 9ccac36831bd8ee6b09a0b0db278fbf1f280a505 Mon Sep 17 00:00:00 2001 From: korenstin Date: Tue, 27 Aug 2024 18:01:13 +0200 Subject: [PATCH 29/29] Copy constructor --- .../templates/food/create_qrcode_form.html | 32 +++++++++++++++++++ apps/food/views.py | 14 ++++++-- locale/fr/LC_MESSAGES/django.po | 14 +++++--- 3 files changed, 53 insertions(+), 7 deletions(-) diff --git a/apps/food/templates/food/create_qrcode_form.html b/apps/food/templates/food/create_qrcode_form.html index 17fff49f..456b9970 100644 --- a/apps/food/templates/food/create_qrcode_form.html +++ b/apps/food/templates/food/create_qrcode_form.html @@ -2,6 +2,7 @@ {% comment %} SPDX-License-Identifier: GPL-3.0-or-later {% endcomment %} +{% load render_table from django_tables2 %} {% load i18n crispy_forms_tags %} {% block content %} @@ -18,6 +19,37 @@ SPDX-License-Identifier: GPL-3.0-or-later {{ form|crispy }} +
+

{% trans "Copy constructor" %}

+ + + + + + + + + + + {% for basic in last_basic %} + + + + + + + {% endfor %} + +
+ {% trans "Name" %} + + {% trans "Owner" %} + + {% trans "Arrival date" %} + + {% trans "Expiry date" %} +
{{ basic.name }}{{ basic.owner }}{{ basic.arrival_date }}{{ basic.expiry_date }}
+
{% endblock %} diff --git a/apps/food/views.py b/apps/food/views.py index 9123c757..88964a5f 100644 --- a/apps/food/views.py +++ b/apps/food/views.py @@ -150,6 +150,7 @@ class QRCodeBasicFoodCreateView(ProtectQuerysetMixin, ProtectedCreateView): 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(): @@ -172,6 +173,14 @@ class QRCodeBasicFoodCreateView(ProtectQuerysetMixin, ProtectedCreateView): form.fields['is_active'].widget = HiddenInput() form.fields['was_eaten'].widget = HiddenInput() + copy = self.request.GET.get('copy', None) + if copy is not None: + basic = BasicFood.objects.get(pk=copy) + for field in ['date_type', 'expiry_date', 'name', 'owner']: + form.fields[field].initial = getattr(basic, field) + for field in ['allergens']: + form.fields[field].initial = getattr(basic, field).all() + return context @@ -188,14 +197,15 @@ class QRCodeCreateView(ProtectQuerysetMixin, ProtectedCreateView): 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"] + + context["last_basic"] = BasicFood.objects.order_by('-pk').all()[:10] + return context @transaction.atomic diff --git a/locale/fr/LC_MESSAGES/django.po b/locale/fr/LC_MESSAGES/django.po index 298331b9..e3edfeea 100644 --- a/locale/fr/LC_MESSAGES/django.po +++ b/locale/fr/LC_MESSAGES/django.po @@ -562,16 +562,19 @@ msgstr "Aliment transformé" msgid "Transformed foods" msgstr "Aliments transformés" +#: apps/food/templates/food/create_qrcode_form.html:31 #: apps/food/templates/food/basicfood_detail.html:14 #: apps/food/templates/food/qrcode_detail.html:15 #: apps/food/templates/food/transformedfood_detail.html:14 msgid "Owner" msgstr "Propriétaire" +#: apps/food/templates/food/create_qrcode_form.html:34 #: apps/food/templates/food/basicfood_detail.html:15 msgid "Arrival date" msgstr "Date d'arrivée" +#: apps/food/templates/food/create_qrcode_form.html:37 #: apps/food/templates/food/basicfood_detail.html:16 #: apps/food/templates/food/qrcode_detail.html:16 #: apps/food/templates/food/transformedfood_detail.html:19 @@ -605,10 +608,15 @@ msgstr "Ajouter à un plat" msgid "New basic food" msgstr "Nouvel aliment basique" +#: apps/food/templates/food/create_qrcode_form.html:23 +msgid "Copy constructor" +msgstr "Constructeur de copie" + #: apps/food/templates/food/qrcode_detail.html:10 msgid "number" msgstr "numéro" +#: apps/food/templates/food/create_qrcode_form.html:28 #: apps/food/templates/food/qrcode_detail.html:14 #: apps/note/templates/note/transaction_form.html:132 #: apps/treasury/models.py:60 @@ -1644,13 +1652,9 @@ msgstr "" #: apps/note/models/notes.py:70 msgid "The note is blocked by the the BDE and can't be manually reactivated." msgstr "" -<<<<<<< HEAD -"La note est bloquée de force par le BDE et ne peut pas être débloquée par " -"le·a possesseur·ice de la note." -======= "La note est bloquée de force par le BDE et ne peut pas être débloquée par læ " "propriétaire de la note." ->>>>>>> main + #: apps/note/models/notes.py:78 msgid "notes"