mirror of
https://gitlab.crans.org/bde/nk20
synced 2025-05-01 11:01:33 +00:00
Add manage ingredient feature, fix some bug
This commit is contained in:
parent
b4f3a158a6
commit
ad0a219ed3
@ -3,7 +3,7 @@
|
||||
|
||||
from rest_framework import serializers
|
||||
|
||||
from ..models import Allergen, BasicFood, TransformedFood, QRCode
|
||||
from ..models import Allergen, Food, BasicFood, TransformedFood, QRCode
|
||||
|
||||
|
||||
class AllergenSerializer(serializers.ModelSerializer):
|
||||
@ -16,6 +16,16 @@ class AllergenSerializer(serializers.ModelSerializer):
|
||||
fields = '__all__'
|
||||
|
||||
|
||||
class FoodSerializer(serializers.ModelSerializer):
|
||||
"""
|
||||
REST API Serializer for Food.
|
||||
The djangorestframework plugin will analyse the model `Food` and parse all fields in the API.
|
||||
"""
|
||||
class Meta:
|
||||
model = Food
|
||||
fields = '__all__'
|
||||
|
||||
|
||||
class BasicFoodSerializer(serializers.ModelSerializer):
|
||||
"""
|
||||
REST API Serializer for BasicFood.
|
||||
|
@ -1,7 +1,7 @@
|
||||
# Copyright (C) 2018-2025 by BDE ENS Paris-Saclay
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
from .views import AllergenViewSet, BasicFoodViewSet, TransformedFoodViewSet, QRCodeViewSet
|
||||
from .views import AllergenViewSet, FoodViewSet, BasicFoodViewSet, TransformedFoodViewSet, QRCodeViewSet
|
||||
|
||||
|
||||
def register_food_urls(router, path):
|
||||
@ -9,6 +9,7 @@ def register_food_urls(router, path):
|
||||
Configure router for Food REST API.
|
||||
"""
|
||||
router.register(path + '/allergen', AllergenViewSet)
|
||||
router.register(path + '/food', FoodViewSet)
|
||||
router.register(path + '/basicfood', BasicFoodViewSet)
|
||||
router.register(path + '/transformedfood', TransformedFoodViewSet)
|
||||
router.register(path + '/qrcode', QRCodeViewSet)
|
||||
|
@ -5,8 +5,8 @@ from api.viewsets import ReadProtectedModelViewSet
|
||||
from django_filters.rest_framework import DjangoFilterBackend
|
||||
from rest_framework.filters import SearchFilter
|
||||
|
||||
from .serializers import AllergenSerializer, BasicFoodSerializer, TransformedFoodSerializer, QRCodeSerializer
|
||||
from ..models import Allergen, BasicFood, TransformedFood, QRCode
|
||||
from .serializers import AllergenSerializer, FoodSerializer, BasicFoodSerializer, TransformedFoodSerializer, QRCodeSerializer
|
||||
from ..models import Allergen, Food, BasicFood, TransformedFood, QRCode
|
||||
|
||||
|
||||
class AllergenViewSet(ReadProtectedModelViewSet):
|
||||
@ -22,6 +22,19 @@ class AllergenViewSet(ReadProtectedModelViewSet):
|
||||
search_fields = ['$name', ]
|
||||
|
||||
|
||||
class FoodViewSet(ReadProtectedModelViewSet):
|
||||
"""
|
||||
REST API View set.
|
||||
The djangorestframework plugin will get all `Food` objects, serialize it to JSON with the given serializer,
|
||||
then render it on /api/food/food/
|
||||
"""
|
||||
queryset = Food.objects.order_by('id')
|
||||
serializer_class = FoodSerializer
|
||||
filter_backends = [DjangoFilterBackend, SearchFilter]
|
||||
filterset_fields = ['name', ]
|
||||
search_fields = ['$name', ]
|
||||
|
||||
|
||||
class BasicFoodViewSet(ReadProtectedModelViewSet):
|
||||
"""
|
||||
REST API View set.
|
||||
|
@ -12,7 +12,7 @@ from note_kfet.inputs import Autocomplete
|
||||
from note_kfet.middlewares import get_current_request
|
||||
from permission.backends import PermissionBackend
|
||||
|
||||
from .models import BasicFood, TransformedFood, QRCode
|
||||
from .models import Food, BasicFood, TransformedFood, QRCode
|
||||
|
||||
|
||||
class QRCodeForms(forms.ModelForm):
|
||||
@ -22,7 +22,6 @@ 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,
|
||||
end_of_life__isnull=True,
|
||||
polymorphic_ctype__model='transformedfood',
|
||||
).filter(PermissionBackend.filter_queryset(
|
||||
@ -151,3 +150,38 @@ class AddIngredientForms(forms.ModelForm):
|
||||
class Meta:
|
||||
model = TransformedFood
|
||||
fields = ('ingredients',)
|
||||
|
||||
|
||||
class ManageIngredientsForm(forms.Form):
|
||||
"""
|
||||
Form to manage ingredient
|
||||
"""
|
||||
fully_used = forms.BooleanField()
|
||||
fully_used.initial = True
|
||||
fully_used.required = True
|
||||
fully_used.label = _('Fully used')
|
||||
|
||||
name = forms.CharField()
|
||||
name.widget = Autocomplete(
|
||||
model=Food,
|
||||
resetable=True,
|
||||
attrs={"api_url": "/api/food/food",
|
||||
"class": "autocomplete"},
|
||||
)
|
||||
name.label = _('Name')
|
||||
|
||||
qrcode = forms.IntegerField()
|
||||
qrcode.widget = Autocomplete(
|
||||
model=QRCode,
|
||||
resetable=True,
|
||||
attrs={"api_url": "/api/food/qrcode/",
|
||||
"name_field": "qr_code_number",
|
||||
"class": "autocomplete"},
|
||||
)
|
||||
qrcode.label = _('QR code number')
|
||||
|
||||
|
||||
ManageIngredientsFormSet = forms.formset_factory(
|
||||
ManageIngredientsForm,
|
||||
extra=1,
|
||||
)
|
||||
|
@ -39,6 +39,11 @@ SPDX-License-Identifier: GPL-3.0-or-later
|
||||
<a class="btn btn-sm btn-primary" href="{% url "food:add_ingredient" pk=food.pk %}">
|
||||
{% trans "Add to a meal" %}
|
||||
</a>
|
||||
{% endif %}
|
||||
{% if manage_ingredients %}
|
||||
<a class="btn btn-sm btn-secondary" href="{% url "food:manage_ingredients" pk=food.pk %}">
|
||||
{% trans "Manage ingredients" %}
|
||||
</a>
|
||||
{% endif %}
|
||||
<a class="btn btn-sm btn-primary" href="{% url "food:food_list" %}">
|
||||
{% trans "Return to the food list" %}
|
||||
|
116
apps/food/templates/food/manage_ingredients.html
Normal file
116
apps/food/templates/food/manage_ingredients.html
Normal file
@ -0,0 +1,116 @@
|
||||
{% extends "base.html" %}
|
||||
{% comment %}
|
||||
Copyright (C) 2018-2025 by BDE ENS Paris-Saclay
|
||||
SPDX-License-Identifier: GPL-3.0-or-later
|
||||
{% endcomment %}
|
||||
{% load i18n crispy_forms_tags %}
|
||||
|
||||
{% block content %}
|
||||
<div class="card bg-white mb-3">
|
||||
<h3 class="card-header text-center">
|
||||
{{ title }}
|
||||
</h3>
|
||||
<div class="card-body" id="form"></div>
|
||||
<form method="post" action="">
|
||||
{% csrf_token %}
|
||||
<table class="table table-condensed table-striped">
|
||||
{# Fill initial data #}
|
||||
{% for display, form in formset %}
|
||||
{% if forloop.first %}
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{{ form.name.label }}</th>
|
||||
<th>{{ form.qrcode.label }}</th>
|
||||
<th>{{ form.fully_used.label }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="form_body">
|
||||
{% endif %}
|
||||
{% if display %}
|
||||
<tr class="row-formset ingredients">
|
||||
{% else %}
|
||||
<tr class="row-formset ingredients" style="display: none">
|
||||
{% endif %}
|
||||
<td>{{ form.name }}</td>
|
||||
<td>{{ form.qrcode }}</td>
|
||||
<td>{{ form.fully_used }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
{# Display buttons to add and remove ingredients #}
|
||||
<div class="card-body">
|
||||
<div class="btn-group btn-block" role="group">
|
||||
<button type="button" id="add_more" class="btn btn-success">{% trans "Add ingredient" %}</button>
|
||||
<button type="button" id="remove_one" class="btn btn-danger">{% trans "Remove ingredient" %}</button>
|
||||
</div>
|
||||
<button class="btn btn-primary" type="submit">{% trans "Submit"%}</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
{% block extrajavascript %}
|
||||
<script>
|
||||
/* script that handles add and remove lines */
|
||||
|
||||
const foods = {{ ingredients | safe }};
|
||||
|
||||
function set_ingredient_id () {
|
||||
let ingredients = document.getElementsByClassName('ingredients');
|
||||
for (var i = 0; i < ingredients.length; i++) {
|
||||
ingredients[i].id = 'ingredients-' + parseInt(i);
|
||||
};
|
||||
}
|
||||
set_ingredient_id();
|
||||
|
||||
function prepopulate () {
|
||||
for (var i = 0; i < {{ ingredients_count }}; i++) {
|
||||
let prefix = 'id_form-' + parseInt(i) + '-';
|
||||
document.getElementById(prefix + 'name_pk').value = parseInt(foods[i]['food_pk']);
|
||||
document.getElementById(prefix + 'name').value = foods[i]['food_name'];
|
||||
document.getElementById(prefix + 'qrcode_pk').value = parseInt(foods[i]['qr_pk']);
|
||||
if (foods[i]['qr_number'] === '') {
|
||||
document.getElementById(prefix + 'qrcode').value = '';
|
||||
}
|
||||
else {
|
||||
document.getElementById(prefix + 'qrcode').value = parseInt(foods[i]['qr_number']);
|
||||
};
|
||||
document.getElementById(prefix + 'fully_used').checked = Boolean(foods[i]['fully_used']);
|
||||
};
|
||||
}
|
||||
prepopulate();
|
||||
|
||||
function delete_form_data (form_id) {
|
||||
let prefix = "id_form-" + parseInt(form_id) + "-";
|
||||
document.getElementById(prefix + "name_pk").value = "";
|
||||
document.getElementById(prefix + "name").value = "";
|
||||
document.getElementById(prefix + "qrcode_pk").value = "";
|
||||
document.getElementById(prefix + "qrcode").value = "";
|
||||
document.getElementById(prefix + "fully_used").checked = true;
|
||||
}
|
||||
var form_count = {{ ingredients_count }} + 1;
|
||||
|
||||
$('#add_more').click(function () {
|
||||
let ingredient_form = document.getElementById('ingredients-' + parseInt(form_count));
|
||||
if (ingredient_form === null) {
|
||||
addMsg(gettext("You can't add more ingredient"), "danger", 5000);
|
||||
return;};
|
||||
ingredient_form.style = "display: true";
|
||||
form_count += 1;
|
||||
});
|
||||
|
||||
$('#remove_one').click(function () {
|
||||
let ingredient_form = document.getElementById('ingredients-' + parseInt(form_count - 1));
|
||||
if (ingredient_form === null) {
|
||||
return;};
|
||||
ingredient_form.style = "display: none";
|
||||
delete_form_data(form_count - 1);
|
||||
form_count -= 1;
|
||||
});
|
||||
|
||||
addMsg(gettext("Add ingredient with their name or their qrcode, if two different priority is given to qrcode"), "warning");
|
||||
|
||||
</script>
|
||||
{% endblock %}
|
87
apps/food/templates/food/transformedfood_update.html
Normal file
87
apps/food/templates/food/transformedfood_update.html
Normal file
@ -0,0 +1,87 @@
|
||||
{% extends "base.html" %}
|
||||
{% comment %}
|
||||
Copyright (C) by BDE ENS Paris-Saclay
|
||||
SPDX-License-Identifier: GPL-3.0-or-later
|
||||
{% endcomment %}
|
||||
{% load i18n crispy_forms_tags %}
|
||||
|
||||
{% block content %}
|
||||
<div class="card bg-white mb-3">
|
||||
<h3 class="card-header text-center">
|
||||
{{ title }}
|
||||
</h3>
|
||||
<div class="card-body" id="form">
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
{{ form | crispy }}
|
||||
<table class="table table-condensed table-striped">
|
||||
{# Fill initial data #}
|
||||
{% for ingredient_form in formset %}
|
||||
{% if forloop.first %}
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{% trans "Name" %}</th>
|
||||
<th>{% trans "QR-code number" %}</th>
|
||||
<th>{% trans "Fully used" %}<th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="form_body">
|
||||
{% endif %}
|
||||
<tr class="row-formset">
|
||||
{{ ingredient_form | crispy }}
|
||||
<td>{{ ingredient_form.name }}</td>
|
||||
<td>{{ ingredient_form.qrcode }}</td>
|
||||
<td>{{ ingredient_form.fully_used }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{# Display buttons to add and remove products #}
|
||||
<div class="card-body">
|
||||
<div class="btn-group btn-block" role="group">
|
||||
<button type="button" id="add_more" class="btn btn-success">{% trans "Add ingredient" %}</button>
|
||||
<button type="button" id="remove_one" class="btn btn-danger">{% trans "Remove ingredient" %}</button>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-block btn-primary">{% trans "Submit" %}</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{# Hidden div that store an empty product form, to be copied into new forms #}
|
||||
<div id="empty_form" style="display: none;">
|
||||
<table class='no_error'>
|
||||
<tbody id="for_real">
|
||||
<tr class="row-formset">
|
||||
<td>{{ formset.empty_form.name }}</td>
|
||||
<td>{{ formset.empty_form.qrcode }}</td>
|
||||
<td>{{ formset.empty_form.fully_used }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% block extrajavascript %}
|
||||
<script>
|
||||
/* script that handles add and remove lines */
|
||||
IDS = {};
|
||||
|
||||
$("#id_form-TOTAL_FORMS").val($(".row-formset").length - 1);
|
||||
|
||||
$('#add_more').click(function () {
|
||||
let form_idx = $('#id_form-TOTAL_FORMS').val();
|
||||
$('#form_body').append($('#for_real').html().replace(/__prefix__/g, form_idx));
|
||||
$('#id_form-TOTAL_FORMS').val(parseInt(form_idx) + 1);
|
||||
$('#id_form-' + parseInt(form_idx) + '-id').val(IDS[parseInt(form_idx)]);
|
||||
});
|
||||
|
||||
$('#remove_one').click(function () {
|
||||
let form_idx = $('#id_form-TOTAL_FORMS').val();
|
||||
if (form_idx > 0) {
|
||||
IDS[parseInt(form_idx) - 1] = $('#id_form-' + (parseInt(form_idx) - 1) + '-id').val();
|
||||
$('#form_body tr:last-child').remove();
|
||||
$('#id_form-TOTAL_FORMS').val(parseInt(form_idx) - 1);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
@ -13,6 +13,7 @@ urlpatterns = [
|
||||
path('<int:slug>/add/basic', views.BasicFoodCreateView.as_view(), name='basicfood_create'),
|
||||
path('add/transformed', views.TransformedFoodCreateView.as_view(), name='transformedfood_create'),
|
||||
path('update/<int:pk>', views.FoodUpdateView.as_view(), name='food_update'),
|
||||
path('update/ingredients/<int:pk>', views.ManageIngredientsView.as_view(), name='manage_ingredients'),
|
||||
path('detail/<int:pk>', views.FoodDetailView.as_view(), name='food_view'),
|
||||
path('detail/basic/<int:pk>', views.BasicFoodDetailView.as_view(), name='basicfood_view'),
|
||||
path('detail/transformed/<int:pk>', views.TransformedFoodDetailView.as_view(), name='transformedfood_view'),
|
||||
|
@ -18,7 +18,9 @@ from permission.backends import PermissionBackend
|
||||
from permission.views import ProtectQuerysetMixin, ProtectedCreateView, LoginRequiredMixin
|
||||
|
||||
from .models import Food, BasicFood, TransformedFood, QRCode
|
||||
from .forms import AddIngredientForms, BasicFoodForms, TransformedFoodForms, BasicFoodUpdateForms, TransformedFoodUpdateForms, QRCodeForms
|
||||
from .forms import QRCodeForms, BasicFoodForms, TransformedFoodForms, \
|
||||
ManageIngredientsForm, ManageIngredientsFormSet, AddIngredientForms, \
|
||||
BasicFoodUpdateForms, TransformedFoodUpdateForms
|
||||
from .tables import FoodTable
|
||||
from .utils import pretty_duration
|
||||
|
||||
@ -73,7 +75,7 @@ class FoodListView(ProtectQuerysetMixin, LoginRequiredMixin, MultiTableMixin, Li
|
||||
PermissionBackend.filter_queryset(self.request, Food, 'view'))
|
||||
# table served
|
||||
served_table = self.get_queryset().order_by('-pk').filter(
|
||||
end_of_life='', is_ready=True).filter(
|
||||
end_of_life='', is_ready=True).exclude(
|
||||
Q(polymorphic_ctype__model='basicfood',
|
||||
basicfood__date_type='DLC',
|
||||
expiry_date__lte=timezone.now(),)
|
||||
@ -106,7 +108,7 @@ class FoodListView(ProtectQuerysetMixin, LoginRequiredMixin, MultiTableMixin, Li
|
||||
return context
|
||||
|
||||
|
||||
class QRCodeCreateView(ProtectQuerysetMixin, CreateView):
|
||||
class QRCodeCreateView(ProtectQuerysetMixin, LoginRequiredMixin, CreateView):
|
||||
"""
|
||||
A view to add qrcode
|
||||
"""
|
||||
@ -238,12 +240,82 @@ class TransformedFoodCreateView(ProtectQuerysetMixin, ProtectedCreateView):
|
||||
form.instance.is_ready = False
|
||||
return super().form_valid(form)
|
||||
|
||||
def get_context_data(self, *args, **kwargs):
|
||||
context = super().get_context_data(*args, **kwargs)
|
||||
context['title'] += ' ' + self.object.name
|
||||
return context
|
||||
|
||||
def get_success_url(self, **kwargs):
|
||||
self.object.refresh_from_db()
|
||||
return reverse_lazy('food:transformedfood_view', kwargs={"pk": self.object.pk})
|
||||
|
||||
|
||||
class AddIngredientView(ProtectQuerysetMixin, UpdateView):
|
||||
MAX_FORMS = 10
|
||||
|
||||
|
||||
class ManageIngredientsView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView):
|
||||
"""
|
||||
A view to manage ingredient for a transformed food
|
||||
"""
|
||||
model = TransformedFood
|
||||
fields = ['ingredients']
|
||||
extra_context = {"title": _("Manage ingredients of:")}
|
||||
template_name = 'food/manage_ingredients.html'
|
||||
|
||||
@transaction.atomic
|
||||
def form_valid(self, form):
|
||||
old_ingredients = list(self.object.ingredients.all()).copy()
|
||||
old_allergens = list(self.object.allergens.all()).copy()
|
||||
self.object.ingredients.clear()
|
||||
for i in range(self.object.ingredients.all().count() + 1 + MAX_FORMS):
|
||||
prefix = 'form-' + str(i) + '-'
|
||||
if form.data[prefix + 'qrcode'] not in ['0', '']:
|
||||
ingredient = QRCode.objects.get(pk=form.data[prefix + 'qrcode']).food_container
|
||||
self.object.ingredients.add(ingredient)
|
||||
if (prefix + 'fully_used') in form.data and form.data[prefix + 'fully_used'] == 'on':
|
||||
ingredient.end_of_life = _('Fully used in {meal}'.format(
|
||||
meal=self.object.name))
|
||||
ingredient.save()
|
||||
|
||||
elif form.data[prefix + 'name'] != '':
|
||||
ingredient = Food.objects.get(pk=form.data[prefix + 'name'])
|
||||
self.object.ingredients.add(ingredient)
|
||||
if (prefix + 'fully_used') in form.data and form.data[prefix + 'fully_used'] == 'on':
|
||||
ingredient.end_of_life = _('Fully used in {meal}'.format(
|
||||
meal=self.object.name))
|
||||
ingredient.save()
|
||||
|
||||
self.object.save(old_ingredients=old_ingredients, old_allergens=old_allergens)
|
||||
return HttpResponseRedirect(self.get_success_url())
|
||||
|
||||
def get_context_data(self, *args, **kwargs):
|
||||
context = super().get_context_data(*args, **kwargs)
|
||||
context['title'] += ' ' + self.object.name
|
||||
formset = ManageIngredientsFormSet()
|
||||
ingredients = self.object.ingredients.all()
|
||||
formset.extra += ingredients.count() + MAX_FORMS
|
||||
context['form'] = ManageIngredientsForm()
|
||||
context['ingredients_count'] = ingredients.count()
|
||||
display = [True] * (1 + ingredients.count()) + [False] * (formset.extra - ingredients.count() - 1)
|
||||
context['formset'] = zip(display, formset)
|
||||
context['ingredients'] = []
|
||||
for ingredient in ingredients:
|
||||
qr = QRCode.objects.filter(food_container=ingredient)
|
||||
|
||||
context['ingredients'].append({
|
||||
'food_pk': ingredient.pk,
|
||||
'food_name': ingredient.name,
|
||||
'qr_pk': '' if qr.count() == 0 else qr[0].pk,
|
||||
'qr_number': '' if qr.count() == 0 else qr[0].qr_code_number,
|
||||
'fully_used': 'true' if ingredient.end_of_life else '',
|
||||
})
|
||||
return context
|
||||
|
||||
def get_success_url(self, **kwargs):
|
||||
return reverse_lazy('food:transformedfood_view', kwargs={"pk": self.object.pk})
|
||||
|
||||
|
||||
class AddIngredientView(ProtectQuerysetMixin, LoginRequiredMixin, UpdateView):
|
||||
"""
|
||||
A view to add ingredient to a meal
|
||||
"""
|
||||
@ -404,6 +476,7 @@ class TransformedFoodDetailView(FoodDetailView):
|
||||
pretty_duration(self.object.shelf_life)
|
||||
))
|
||||
context["foods"] = self.object.ingredients.all()
|
||||
context["manage_ingredients"] = True
|
||||
return context
|
||||
|
||||
def get(self, *args, **kwargs):
|
||||
|
@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2025-04-24 18:22+0200\n"
|
||||
"POT-Creation-Date: 2025-04-30 11:44+0200\n"
|
||||
"PO-Revision-Date: 2022-04-11 22:05+0200\n"
|
||||
"Last-Translator: bleizi <bleizi@crans.org>\n"
|
||||
"Language-Team: French <http://translate.ynerant.fr/projects/nk20/nk20/fr/>\n"
|
||||
@ -383,7 +383,9 @@ msgstr "Entrée effectuée !"
|
||||
|
||||
#: apps/activity/templates/activity/activity_form.html:16
|
||||
#: apps/food/templates/food/food_update.html:17
|
||||
#: apps/food/templates/food/manage_ingredients.html:48
|
||||
#: apps/food/templates/food/qrcode.html:18
|
||||
#: apps/food/templates/food/transformedfood_update.html:45
|
||||
#: apps/member/templates/member/add_members.html:46
|
||||
#: apps/member/templates/member/club_form.html:16
|
||||
#: apps/note/templates/note/transactiontemplate_form.html:18
|
||||
@ -495,26 +497,40 @@ msgstr "API"
|
||||
msgid "food"
|
||||
msgstr "bouffe"
|
||||
|
||||
#: apps/food/forms.py:50
|
||||
#: apps/food/forms.py:49
|
||||
msgid "Pasta METRO 5kg"
|
||||
msgstr "Pâtes METRO 5kg"
|
||||
|
||||
#: apps/food/forms.py:54 apps/food/forms.py:82
|
||||
#: apps/food/forms.py:53 apps/food/forms.py:81
|
||||
msgid "Specific order given to GCKs"
|
||||
msgstr ""
|
||||
|
||||
#: apps/food/forms.py:78
|
||||
#: apps/food/forms.py:77
|
||||
msgid "Lasagna"
|
||||
msgstr "Lasagnes"
|
||||
|
||||
#: apps/food/forms.py:117
|
||||
#: apps/food/forms.py:116
|
||||
msgid "Shelf life (in hours)"
|
||||
msgstr "Durée de vie (en heure)"
|
||||
|
||||
#: apps/food/forms.py:139
|
||||
#: apps/food/forms.py:138 apps/food/forms.py:162
|
||||
#: apps/food/templates/food/transformedfood_update.html:25
|
||||
msgid "Fully used"
|
||||
msgstr "Entièrement utilisé"
|
||||
|
||||
#: apps/food/forms.py:171 apps/food/templates/food/qrcode.html:29
|
||||
#: apps/food/templates/food/transformedfood_update.html:23
|
||||
#: apps/note/templates/note/transaction_form.html:132
|
||||
#: apps/treasury/models.py:61
|
||||
msgid "Name"
|
||||
msgstr "Nom"
|
||||
|
||||
#: apps/food/forms.py:181
|
||||
#, fuzzy
|
||||
#| msgid "QR-code number"
|
||||
msgid "QR code number"
|
||||
msgstr "numéro de QR-code"
|
||||
|
||||
#: apps/food/models.py:23
|
||||
msgid "Allergen"
|
||||
msgstr "Allergène"
|
||||
@ -547,7 +563,7 @@ msgstr "est prêt"
|
||||
msgid "order"
|
||||
msgstr "consigne"
|
||||
|
||||
#: apps/food/models.py:107 apps/food/views.py:32
|
||||
#: apps/food/models.py:107 apps/food/views.py:34
|
||||
#: note_kfet/templates/base.html:72
|
||||
msgid "Food"
|
||||
msgstr "Bouffe"
|
||||
@ -605,6 +621,7 @@ msgid "QR-codes"
|
||||
msgstr "QR-codes"
|
||||
|
||||
#: apps/food/models.py:286
|
||||
#: apps/food/templates/food/transformedfood_update.html:24
|
||||
msgid "QR-code number"
|
||||
msgstr "numéro de QR-code"
|
||||
|
||||
@ -624,7 +641,11 @@ msgstr "Modifier"
|
||||
msgid "Add to a meal"
|
||||
msgstr "Ajouter à un plat"
|
||||
|
||||
#: apps/food/templates/food/food_detail.html:44
|
||||
#: apps/food/templates/food/food_detail.html:45
|
||||
msgid "Manage ingredients"
|
||||
msgstr "Gérer les ingrédients"
|
||||
|
||||
#: apps/food/templates/food/food_detail.html:49
|
||||
msgid "Return to the food list"
|
||||
msgstr "Retour à la liste de nourriture"
|
||||
|
||||
@ -660,6 +681,16 @@ msgstr "Bouffe du club"
|
||||
msgid "Yours club has not food yet."
|
||||
msgstr "Ton club n'a pas de bouffe pour l'instant"
|
||||
|
||||
#: apps/food/templates/food/manage_ingredients.html:45
|
||||
#: apps/food/templates/food/transformedfood_update.html:42
|
||||
msgid "Add ingredient"
|
||||
msgstr "Ajouter un ingrédient"
|
||||
|
||||
#: apps/food/templates/food/manage_ingredients.html:46
|
||||
#: apps/food/templates/food/transformedfood_update.html:43
|
||||
msgid "Remove ingredient"
|
||||
msgstr "Enlever un ingrédient"
|
||||
|
||||
#: apps/food/templates/food/qrcode.html:22
|
||||
msgid "Copy constructor"
|
||||
msgstr "Constructeur de copie"
|
||||
@ -668,12 +699,6 @@ msgstr "Constructeur de copie"
|
||||
msgid "New food"
|
||||
msgstr "Nouvel aliment"
|
||||
|
||||
#: apps/food/templates/food/qrcode.html:29
|
||||
#: apps/note/templates/note/transaction_form.html:132
|
||||
#: apps/treasury/models.py:61
|
||||
msgid "Name"
|
||||
msgstr "Nom"
|
||||
|
||||
#: apps/food/templates/food/qrcode.html:32
|
||||
msgid "Owner"
|
||||
msgstr "Propriétaire"
|
||||
@ -726,40 +751,49 @@ msgstr "semaines"
|
||||
msgid "and"
|
||||
msgstr "et"
|
||||
|
||||
#: apps/food/views.py:116
|
||||
#: apps/food/views.py:118
|
||||
msgid "Add a new QRCode"
|
||||
msgstr "Ajouter un nouveau QR-code"
|
||||
|
||||
#: apps/food/views.py:165
|
||||
#: apps/food/views.py:167
|
||||
msgid "Add an aliment"
|
||||
msgstr "Ajouter un nouvel aliment"
|
||||
|
||||
#: apps/food/views.py:224
|
||||
#: apps/food/views.py:226
|
||||
msgid "Add a meal"
|
||||
msgstr "Ajouter un plat"
|
||||
|
||||
#: apps/food/views.py:251
|
||||
#: apps/food/views.py:262
|
||||
msgid "Manage ingredients of:"
|
||||
msgstr "Gestion des ingrédienrs de :"
|
||||
|
||||
#: apps/food/views.py:276 apps/food/views.py:284
|
||||
#, python-brace-format
|
||||
msgid "Fully used in {meal}"
|
||||
msgstr "Aliment entièrement utilisé dans : {meal}"
|
||||
|
||||
#: apps/food/views.py:323
|
||||
msgid "Add the ingredient:"
|
||||
msgstr "Ajouter l'ingrédient"
|
||||
|
||||
#: apps/food/views.py:275
|
||||
#: apps/food/views.py:349
|
||||
#, python-brace-format
|
||||
msgid "Food fully used in : {meal.name}"
|
||||
msgstr "Aliment entièrement utilisé dans : {meal.name}"
|
||||
|
||||
#: apps/food/views.py:294
|
||||
#: apps/food/views.py:368
|
||||
msgid "Update an aliment"
|
||||
msgstr "Modifier un aliment"
|
||||
|
||||
#: apps/food/views.py:342
|
||||
#: apps/food/views.py:416
|
||||
msgid "Details of:"
|
||||
msgstr "Détails de :"
|
||||
|
||||
#: apps/food/views.py:352 apps/treasury/tables.py:149
|
||||
#: apps/food/views.py:426 apps/treasury/tables.py:149
|
||||
msgid "Yes"
|
||||
msgstr "Oui"
|
||||
|
||||
#: apps/food/views.py:354 apps/member/models.py:99 apps/treasury/tables.py:149
|
||||
#: apps/food/views.py:428 apps/member/models.py:99 apps/treasury/tables.py:149
|
||||
msgid "No"
|
||||
msgstr "Non"
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user