Merge branch 'develop' into recipe-serving-count
This commit is contained in:
@ -37,7 +37,7 @@ class UserPreferenceForm(forms.ModelForm):
|
|||||||
'nav_color': _('Color of the top navigation bar. Not all colors work with all themes, just try them out!'),
|
'nav_color': _('Color of the top navigation bar. Not all colors work with all themes, just try them out!'),
|
||||||
'default_unit': _('Default Unit to be used when inserting a new ingredient into a recipe.'),
|
'default_unit': _('Default Unit to be used when inserting a new ingredient into a recipe.'),
|
||||||
'use_fractions': _('Enables support for fractions in ingredient amounts (e.g. convert decimals to fractions automatically)'),
|
'use_fractions': _('Enables support for fractions in ingredient amounts (e.g. convert decimals to fractions automatically)'),
|
||||||
'plan_share': _('Default user to share newly created meal plan entries with.'),
|
'plan_share': _('Users with whom newly created meal plan/shopping list entries should be shared by default.'),
|
||||||
'show_recent': _('Show recently viewed recipes on search page.'),
|
'show_recent': _('Show recently viewed recipes on search page.'),
|
||||||
'ingredient_decimals': _('Number of decimals to round ingredients.'),
|
'ingredient_decimals': _('Number of decimals to round ingredients.'),
|
||||||
'comments': _('If you want to be able to create and see comments underneath recipes.'),
|
'comments': _('If you want to be able to create and see comments underneath recipes.'),
|
||||||
|
131
cookbook/helper/ingredient_parser.py
Normal file
131
cookbook/helper/ingredient_parser.py
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
import unicodedata
|
||||||
|
import string
|
||||||
|
|
||||||
|
def parse_fraction(x):
|
||||||
|
if len(x) == 1 and 'fraction' in unicodedata.decomposition(x):
|
||||||
|
frac_split = unicodedata.decomposition(x[-1:]).split()
|
||||||
|
return float((frac_split[1]).replace('003', '')) / float((frac_split[3]).replace('003', ''))
|
||||||
|
else:
|
||||||
|
frac_split = x.split('/')
|
||||||
|
if not len(frac_split) == 2:
|
||||||
|
raise ValueError
|
||||||
|
try:
|
||||||
|
return int(frac_split[0]) / int(frac_split[1])
|
||||||
|
except ZeroDivisionError:
|
||||||
|
raise ValueError
|
||||||
|
|
||||||
|
def parse_amount(x):
|
||||||
|
amount = 0
|
||||||
|
unit = ''
|
||||||
|
|
||||||
|
did_check_frac = False
|
||||||
|
end = 0
|
||||||
|
while end < len(x) and (x[end] in string.digits or ((x[end] == '.' or x[end] == ',') and end + 1 < len(x) and x[end+1] in string.digits)):
|
||||||
|
end += 1
|
||||||
|
if end > 0:
|
||||||
|
amount = float(x[:end].replace(',', '.'))
|
||||||
|
else:
|
||||||
|
amount = parse_fraction(x[0])
|
||||||
|
end += 1
|
||||||
|
did_check_frac = True
|
||||||
|
if end < len(x):
|
||||||
|
if did_check_frac:
|
||||||
|
unit = x[end:]
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
amount += parse_fraction(x[end])
|
||||||
|
unit = x[end+1:]
|
||||||
|
except ValueError:
|
||||||
|
unit = x[end:]
|
||||||
|
return amount, unit
|
||||||
|
|
||||||
|
def parse_ingredient_with_comma(tokens):
|
||||||
|
ingredient = ''
|
||||||
|
note = ''
|
||||||
|
start = 0
|
||||||
|
# search for first occurence of an argument ending in a comma
|
||||||
|
while start < len(tokens) and not tokens[start].endswith(','):
|
||||||
|
start += 1
|
||||||
|
if start == len(tokens):
|
||||||
|
# no token ending in a comma found -> use everything as ingredient
|
||||||
|
ingredient = ' '.join(tokens)
|
||||||
|
else:
|
||||||
|
ingredient = ' '.join(tokens[:start+1])[:-1]
|
||||||
|
note = ' '.join(tokens[start+1:])
|
||||||
|
return ingredient, note
|
||||||
|
|
||||||
|
def parse_ingredient(tokens):
|
||||||
|
ingredient = ''
|
||||||
|
note = ''
|
||||||
|
if tokens[-1].endswith(')'):
|
||||||
|
# last argument ends with closing bracket -> look for opening bracket
|
||||||
|
start = len(tokens) - 1
|
||||||
|
while not tokens[start].startswith('(') and not start == 0:
|
||||||
|
start -= 1
|
||||||
|
if start == 0:
|
||||||
|
# the whole list is wrapped in brackets -> assume it is an error (e.g. assumed first argument was the unit)
|
||||||
|
raise ValueError
|
||||||
|
elif start < 0:
|
||||||
|
# no opening bracket anywhere -> just ignore the last bracket
|
||||||
|
ingredient, note = parse_ingredient_with_comma(tokens)
|
||||||
|
else:
|
||||||
|
# opening bracket found -> split in ingredient and note, remove brackets from note
|
||||||
|
note = ' '.join(tokens[start:])[1:-1]
|
||||||
|
ingredient = ' '.join(tokens[:start])
|
||||||
|
else:
|
||||||
|
ingredient, note = parse_ingredient_with_comma(tokens)
|
||||||
|
return ingredient, note
|
||||||
|
|
||||||
|
def parse(x):
|
||||||
|
# initialize default values
|
||||||
|
amount = 0
|
||||||
|
unit = ''
|
||||||
|
ingredient = ''
|
||||||
|
note = ''
|
||||||
|
|
||||||
|
tokens = x.split()
|
||||||
|
if len(tokens) == 1:
|
||||||
|
# there only is one argument, that must be the ingredient
|
||||||
|
ingredient = tokens[0]
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
# try to parse first argument as amount
|
||||||
|
amount, unit = parse_amount(tokens[0])
|
||||||
|
# only try to parse second argument as amount if there are at least three arguments
|
||||||
|
# if it already has a unit there can't be a fraction for the amount
|
||||||
|
if len(tokens) > 2:
|
||||||
|
try:
|
||||||
|
if not unit == '':
|
||||||
|
# a unit is already found, no need to try the second argument for a fraction
|
||||||
|
# probably not the best method to do it, but I didn't want to make an if check and paste the exact same thing in the else as already is in the except
|
||||||
|
raise ValueError
|
||||||
|
# try to parse second argument as amount and add that, in case of '2 1/2' or '2 ½'
|
||||||
|
amount += parse_fraction(tokens[1])
|
||||||
|
# assume that units can't end with a comma
|
||||||
|
if len(tokens) > 3 and not tokens[2].endswith(','):
|
||||||
|
# try to use third argument as unit and everything else as ingredient, use everything as ingredient if it fails
|
||||||
|
try:
|
||||||
|
ingredient, note = parse_ingredient(tokens[3:])
|
||||||
|
unit = tokens[2]
|
||||||
|
except ValueError:
|
||||||
|
ingredient, note = parse_ingredient(tokens[2:])
|
||||||
|
else:
|
||||||
|
ingredient, note = parse_ingredient(tokens[2:])
|
||||||
|
except ValueError:
|
||||||
|
# assume that units can't end with a comma
|
||||||
|
if not tokens[1].endswith(','):
|
||||||
|
# try to use second argument as unit and everything else as ingredient, use everything as ingredient if it fails
|
||||||
|
try:
|
||||||
|
ingredient, note = parse_ingredient(tokens[2:])
|
||||||
|
unit = tokens[1]
|
||||||
|
except ValueError:
|
||||||
|
ingredient, note = parse_ingredient(tokens[1:])
|
||||||
|
else:
|
||||||
|
ingredient, note = parse_ingredient(tokens[1:])
|
||||||
|
else:
|
||||||
|
# only two arguments, first one is the amount which means this is the ingredient
|
||||||
|
ingredient = tokens[1]
|
||||||
|
except ValueError:
|
||||||
|
# can't parse first argument as amount -> no unit -> parse everything as ingredient
|
||||||
|
ingredient, note = parse_ingredient(tokens)
|
||||||
|
return amount, unit.strip(), ingredient.strip(), note.strip()
|
@ -141,7 +141,7 @@ class OwnerRequiredMixin(object):
|
|||||||
return HttpResponseRedirect(reverse_lazy('login'))
|
return HttpResponseRedirect(reverse_lazy('login'))
|
||||||
else:
|
else:
|
||||||
if not is_object_owner(request.user, self.get_object()):
|
if not is_object_owner(request.user, self.get_object()):
|
||||||
messages.add_message(request, messages.ERROR, _('You cannot interact with this object as its not owned by you!'))
|
messages.add_message(request, messages.ERROR, _('You cannot interact with this object as it is not owned by you!'))
|
||||||
return HttpResponseRedirect(reverse('index'))
|
return HttpResponseRedirect(reverse('index'))
|
||||||
|
|
||||||
return super(OwnerRequiredMixin, self).dispatch(request, *args, **kwargs)
|
return super(OwnerRequiredMixin, self).dispatch(request, *args, **kwargs)
|
||||||
@ -155,7 +155,7 @@ class CustomIsOwner(permissions.BasePermission):
|
|||||||
verifies user has ownership over object
|
verifies user has ownership over object
|
||||||
(either user or created_by or user is request user)
|
(either user or created_by or user is request user)
|
||||||
"""
|
"""
|
||||||
message = _('You cannot interact with this object as its not owned by you!')
|
message = _('You cannot interact with this object as it is not owned by you!')
|
||||||
|
|
||||||
def has_permission(self, request, view):
|
def has_permission(self, request, view):
|
||||||
return request.user.is_authenticated
|
return request.user.is_authenticated
|
||||||
@ -169,7 +169,7 @@ class CustomIsShared(permissions.BasePermission): # TODO function duplicate/too
|
|||||||
Custom permission class for django rest framework views
|
Custom permission class for django rest framework views
|
||||||
verifies user is shared for the object he is trying to access
|
verifies user is shared for the object he is trying to access
|
||||||
"""
|
"""
|
||||||
message = _('You cannot interact with this object as its not owned by you!')
|
message = _('You cannot interact with this object as it is not owned by you!')
|
||||||
|
|
||||||
def has_permission(self, request, view):
|
def has_permission(self, request, view):
|
||||||
return request.user.is_authenticated
|
return request.user.is_authenticated
|
||||||
|
@ -11,6 +11,7 @@ from django.utils.dateparse import parse_duration
|
|||||||
from django.utils.translation import gettext as _
|
from django.utils.translation import gettext as _
|
||||||
|
|
||||||
from cookbook.models import Keyword
|
from cookbook.models import Keyword
|
||||||
|
from cookbook.helper.ingredient_parser import parse as parse_ingredient
|
||||||
|
|
||||||
|
|
||||||
def get_from_html(html_text, url):
|
def get_from_html(html_text, url):
|
||||||
@ -70,39 +71,12 @@ def find_recipe_json(ld_json, url):
|
|||||||
ingredients = []
|
ingredients = []
|
||||||
|
|
||||||
for x in ld_json['recipeIngredient']:
|
for x in ld_json['recipeIngredient']:
|
||||||
ingredient_split = x.split()
|
try:
|
||||||
ingredient = None
|
amount, unit, ingredient, note = parse_ingredient(x)
|
||||||
amount = 0
|
if ingredient:
|
||||||
unit = ''
|
ingredients.append({'amount': amount, 'unit': {'text': unit, 'id': random.randrange(10000, 99999)}, 'ingredient': {'text': ingredient, 'id': random.randrange(10000, 99999)}, "note": note, 'original': x})
|
||||||
if len(ingredient_split) > 2:
|
except:
|
||||||
ingredient = " ".join(ingredient_split[2:])
|
pass
|
||||||
unit = ingredient_split[1]
|
|
||||||
|
|
||||||
try:
|
|
||||||
if 'fraction' in unicodedata.decomposition(ingredient_split[0]):
|
|
||||||
frac_split = unicodedata.decomposition(ingredient_split[0]).split()
|
|
||||||
amount = round(float((frac_split[1]).replace('003', '')) / float((frac_split[3]).replace('003', '')), 3)
|
|
||||||
else:
|
|
||||||
raise TypeError
|
|
||||||
except TypeError: # raised by unicodedata.decomposition if there was no unicode character in parsed data
|
|
||||||
try:
|
|
||||||
amount = float(ingredient_split[0].replace(',', '.'))
|
|
||||||
except ValueError:
|
|
||||||
amount = 0
|
|
||||||
ingredient = " ".join(ingredient_split)
|
|
||||||
if len(ingredient_split) == 2:
|
|
||||||
ingredient = " ".join(ingredient_split[1:])
|
|
||||||
unit = ''
|
|
||||||
try:
|
|
||||||
amount = float(ingredient_split[0].replace(',', '.'))
|
|
||||||
except ValueError:
|
|
||||||
amount = 0
|
|
||||||
ingredient = " ".join(ingredient_split)
|
|
||||||
if len(ingredient_split) == 1:
|
|
||||||
ingredient = " ".join(ingredient_split)
|
|
||||||
|
|
||||||
if ingredient:
|
|
||||||
ingredients.append({'amount': amount, 'unit': {'text': unit, 'id': random.randrange(10000, 99999)}, 'ingredient': {'text': ingredient, 'id': random.randrange(10000, 99999)}, 'original': x})
|
|
||||||
|
|
||||||
ld_json['recipeIngredient'] = ingredients
|
ld_json['recipeIngredient'] = ingredients
|
||||||
else:
|
else:
|
||||||
@ -158,7 +132,7 @@ def find_recipe_json(ld_json, url):
|
|||||||
else:
|
else:
|
||||||
ld_json['recipeInstructions'] = ''
|
ld_json['recipeInstructions'] = ''
|
||||||
|
|
||||||
ld_json['recipeInstructions'] += '\n\n' + _('Imported from ') + url
|
ld_json['recipeInstructions'] += '\n\n' + _('Imported from') + ' ' + url
|
||||||
|
|
||||||
if 'image' in ld_json:
|
if 'image' in ld_json:
|
||||||
# check if list of images is returned, take first if so
|
# check if list of images is returned, take first if so
|
||||||
|
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
BIN
cookbook/locale/es/LC_MESSAGES/django.mo
Normal file
BIN
cookbook/locale/es/LC_MESSAGES/django.mo
Normal file
Binary file not shown.
2049
cookbook/locale/es/LC_MESSAGES/django.po
Normal file
2049
cookbook/locale/es/LC_MESSAGES/django.po
Normal file
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
1841
cookbook/locale/hu_HU/LC_MESSAGES/django.po
Normal file
1841
cookbook/locale/hu_HU/LC_MESSAGES/django.po
Normal file
File diff suppressed because it is too large
Load Diff
BIN
cookbook/locale/it/LC_MESSAGES/django.mo
Normal file
BIN
cookbook/locale/it/LC_MESSAGES/django.mo
Normal file
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
1841
cookbook/locale/zh_CN/LC_MESSAGES/django.po
Normal file
1841
cookbook/locale/zh_CN/LC_MESSAGES/django.po
Normal file
File diff suppressed because it is too large
Load Diff
26
cookbook/migrations/0091_auto_20201226_1551.py
Normal file
26
cookbook/migrations/0091_auto_20201226_1551.py
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
# Generated by Django 3.1.4 on 2020-12-26 14:51
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
def migrate_empty_units(apps, schema_editor):
|
||||||
|
Unit = apps.get_model('cookbook', 'Unit')
|
||||||
|
Ingredient = apps.get_model('cookbook', 'Ingredient')
|
||||||
|
|
||||||
|
empty_units = Unit.objects.filter(name='').all()
|
||||||
|
for x in empty_units:
|
||||||
|
for i in Ingredient.objects.all():
|
||||||
|
if i.unit == x:
|
||||||
|
i.unit = None
|
||||||
|
i.save()
|
||||||
|
x.delete()
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
dependencies = [
|
||||||
|
('cookbook', '0090_auto_20201214_1359'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RunPython(migrate_empty_units),
|
||||||
|
]
|
@ -5,6 +5,7 @@ from datetime import date, timedelta
|
|||||||
from annoying.fields import AutoOneToOneField
|
from annoying.fields import AutoOneToOneField
|
||||||
from django.contrib import auth
|
from django.contrib import auth
|
||||||
from django.contrib.auth.models import User, Group
|
from django.contrib.auth.models import User, Group
|
||||||
|
from django.core.validators import MinLengthValidator
|
||||||
from django.utils.translation import gettext as _
|
from django.utils.translation import gettext as _
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django_random_queryset import RandomManager
|
from django_random_queryset import RandomManager
|
||||||
@ -136,7 +137,7 @@ class Keyword(models.Model):
|
|||||||
|
|
||||||
|
|
||||||
class Unit(models.Model):
|
class Unit(models.Model):
|
||||||
name = models.CharField(unique=True, max_length=128)
|
name = models.CharField(unique=True, max_length=128, validators=[MinLengthValidator(1)])
|
||||||
description = models.TextField(blank=True, null=True)
|
description = models.TextField(blank=True, null=True)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
|
@ -84,7 +84,7 @@
|
|||||||
<li class="nav-item dropdown {% if request.resolver_match.url_name in 'list_keyword,data_batch_edit' %}active{% endif %}">
|
<li class="nav-item dropdown {% if request.resolver_match.url_name in 'list_keyword,data_batch_edit' %}active{% endif %}">
|
||||||
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdownMenuLink" data-toggle="dropdown"
|
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdownMenuLink" data-toggle="dropdown"
|
||||||
aria-haspopup="true" aria-expanded="false">
|
aria-haspopup="true" aria-expanded="false">
|
||||||
<i class="fas fa-tags"></i> {% trans 'Tags' %}
|
<i class="fas fa-tags"></i> {% trans 'Keywords' %}
|
||||||
</a>
|
</a>
|
||||||
<div class="dropdown-menu" aria-labelledby="navbarDropdownMenuLink">
|
<div class="dropdown-menu" aria-labelledby="navbarDropdownMenuLink">
|
||||||
<a class="dropdown-item" href="{% url 'list_keyword' %}"><i
|
<a class="dropdown-item" href="{% url 'list_keyword' %}"><i
|
||||||
@ -139,7 +139,7 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
<div class="dropdown-divider"></div>
|
<div class="dropdown-divider"></div>
|
||||||
<a class="dropdown-item" href="{% url 'docs_markdown' %}"><i
|
<a class="dropdown-item" href="{% url 'docs_markdown' %}"><i
|
||||||
class="fab fa-markdown fa-fw"></i> {% trans 'Markdown Help' %}</a>
|
class="fab fa-markdown fa-fw"></i> {% trans 'Markdown Guide' %}</a>
|
||||||
<a class="dropdown-item" href="https://github.com/vabene1111/recipes"><i
|
<a class="dropdown-item" href="https://github.com/vabene1111/recipes"><i
|
||||||
class="fab fa-github fa-fw"></i> {% trans 'GitHub' %}</a>
|
class="fab fa-github fa-fw"></i> {% trans 'GitHub' %}</a>
|
||||||
<a class="dropdown-item" href="{% url 'docs_api' %}"><i
|
<a class="dropdown-item" href="{% url 'docs_api' %}"><i
|
||||||
|
@ -11,9 +11,9 @@
|
|||||||
<div class="card border-info">
|
<div class="card border-info">
|
||||||
<div class="card-body text-info">
|
<div class="card-body text-info">
|
||||||
<p class="card-text">
|
<p class="card-text">
|
||||||
{% trans 'On this Page you can manage all storage folder locations that should be monitored and synced' %}
|
{% trans 'On this Page you can manage all storage folder locations that should be monitored and synced.' %}
|
||||||
<br/>
|
<br/>
|
||||||
{% trans 'The path must be in the following format' %} <code>/Folder/RecipesFolder</code>
|
{% trans 'The path must be in the following format' %}: <code>/Folder/RecipesFolder</code>
|
||||||
</p>
|
</p>
|
||||||
<form method="POST" class="post-form">{% csrf_token %}
|
<form method="POST" class="post-form">{% csrf_token %}
|
||||||
{{ form|crispy }}
|
{{ form|crispy }}
|
||||||
|
@ -23,7 +23,7 @@
|
|||||||
|
|
||||||
<h4>{% trans 'Units' %}</h4>
|
<h4>{% trans 'Units' %}</h4>
|
||||||
<form action="{% url 'edit_food' %}" method="post"
|
<form action="{% url 'edit_food' %}" method="post"
|
||||||
onsubmit="return confirm('{% trans 'Are you sure that you want to merge these two units ?' %}')">
|
onsubmit="return confirm('{% trans 'Are you sure that you want to merge these two units?' %}')">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
{{ units_form|crispy }}
|
{{ units_form|crispy }}
|
||||||
<button class="btn btn-danger" type="submit"
|
<button class="btn btn-danger" type="submit"
|
||||||
@ -33,7 +33,7 @@
|
|||||||
|
|
||||||
<h4>{% trans 'Ingredients' %}</h4>
|
<h4>{% trans 'Ingredients' %}</h4>
|
||||||
<form action="{% url 'edit_food' %}" method="post"
|
<form action="{% url 'edit_food' %}" method="post"
|
||||||
onsubmit="return confirm('{% trans 'Are you sure that you want to merge these two ingredients ?' %}')">
|
onsubmit="return confirm('{% trans 'Are you sure that you want to merge these two ingredients?' %}')">
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
{{ food_form|crispy }}
|
{{ food_form|crispy }}
|
||||||
<button class="btn btn-danger" type="submit">
|
<button class="btn btn-danger" type="submit">
|
||||||
|
@ -62,7 +62,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="collapse col-md-12" id="collapse_adv_search">
|
<div class="collapse col-md-12{% if filter.data.keywords or filter.data.foods or filter.data.internal and not filter.data.internal == "unknown" %} show{% endif %}" id="collapse_adv_search">
|
||||||
<div style="margin-top: 1vh">
|
<div style="margin-top: 1vh">
|
||||||
{{ filter.form.keywords | as_crispy_field }}
|
{{ filter.form.keywords | as_crispy_field }}
|
||||||
</div>
|
</div>
|
||||||
|
@ -194,9 +194,9 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="id_all_keywords">{% trans 'All Keywords' %}</label><br/>
|
{% trans 'All Keywords' %}<br/>
|
||||||
<input id="id_all_keywords" type="checkbox"
|
<input id="id_all_keywords" type="checkbox"
|
||||||
v-model="all_keywords"> {% trans 'Import all Keywords not only the ones already existing.' %}
|
v-model="all_keywords"> <label for="id_all_keywords">{% trans 'Import all Keywords not only the ones already existing.' %}</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
|
26
cookbook/tests/api/test_api_recipe.py
Normal file
26
cookbook/tests/api/test_api_recipe.py
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import json
|
||||||
|
|
||||||
|
from django.contrib import auth
|
||||||
|
from django.db.models import ProtectedError
|
||||||
|
from django.urls import reverse
|
||||||
|
|
||||||
|
from cookbook.models import Storage, Sync, Keyword, ShoppingList, Recipe
|
||||||
|
from cookbook.tests.views.test_views import TestViews
|
||||||
|
|
||||||
|
|
||||||
|
class TestApiShopping(TestViews):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
super(TestApiShopping, self).setUp()
|
||||||
|
self.internal_recipe = Recipe.objects.create(
|
||||||
|
name='Test',
|
||||||
|
internal=True,
|
||||||
|
created_by=auth.get_user(self.user_client_1)
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_shopping_view_permissions(self):
|
||||||
|
self.batch_requests([(self.anonymous_client, 403), (self.guest_client_1, 200), (self.user_client_1, 200),
|
||||||
|
(self.user_client_2, 200), (self.admin_client_1, 200), (self.superuser_client, 200)],
|
||||||
|
reverse('api:recipe-detail', args={self.internal_recipe.id}))
|
||||||
|
|
||||||
|
# TODO add tests for editing
|
@ -1,5 +1,6 @@
|
|||||||
import json
|
import json
|
||||||
|
|
||||||
|
from cookbook.helper.ingredient_parser import parse_ingredient
|
||||||
from cookbook.helper.recipe_url_import import get_from_html
|
from cookbook.helper.recipe_url_import import get_from_html
|
||||||
from cookbook.tests.test_setup import TestBase
|
from cookbook.tests.test_setup import TestBase
|
||||||
|
|
||||||
@ -8,16 +9,16 @@ class TestEditsRecipe(TestBase):
|
|||||||
|
|
||||||
def test_ld_json(self):
|
def test_ld_json(self):
|
||||||
test_list = [
|
test_list = [
|
||||||
{'file': 'cookbook/tests/resources/websites/ld_json_1.html', 'result_length': 3128},
|
{'file': 'cookbook/tests/resources/websites/ld_json_1.html', 'result_length': 3218},
|
||||||
{'file': 'cookbook/tests/resources/websites/ld_json_2.html', 'result_length': 1450},
|
{'file': 'cookbook/tests/resources/websites/ld_json_2.html', 'result_length': 1510},
|
||||||
{'file': 'cookbook/tests/resources/websites/ld_json_3.html', 'result_length': 1545},
|
{'file': 'cookbook/tests/resources/websites/ld_json_3.html', 'result_length': 1629},
|
||||||
{'file': 'cookbook/tests/resources/websites/ld_json_4.html', 'result_length': 1657},
|
{'file': 'cookbook/tests/resources/websites/ld_json_4.html', 'result_length': 1729},
|
||||||
{'file': 'cookbook/tests/resources/websites/ld_json_itemList.html', 'result_length': 3131},
|
{'file': 'cookbook/tests/resources/websites/ld_json_itemList.html', 'result_length': 3200},
|
||||||
{'file': 'cookbook/tests/resources/websites/ld_json_multiple.html', 'result_length': 1546},
|
{'file': 'cookbook/tests/resources/websites/ld_json_multiple.html', 'result_length': 1606},
|
||||||
{'file': 'cookbook/tests/resources/websites/micro_data_1.html', 'result_length': 1022},
|
{'file': 'cookbook/tests/resources/websites/micro_data_1.html', 'result_length': 1079},
|
||||||
{'file': 'cookbook/tests/resources/websites/micro_data_2.html', 'result_length': 1384},
|
{'file': 'cookbook/tests/resources/websites/micro_data_2.html', 'result_length': 1429},
|
||||||
{'file': 'cookbook/tests/resources/websites/micro_data_3.html', 'result_length': 1100},
|
{'file': 'cookbook/tests/resources/websites/micro_data_3.html', 'result_length': 1148},
|
||||||
{'file': 'cookbook/tests/resources/websites/micro_data_4.html', 'result_length': 4231},
|
{'file': 'cookbook/tests/resources/websites/micro_data_4.html', 'result_length': 4396},
|
||||||
]
|
]
|
||||||
|
|
||||||
for test in test_list:
|
for test in test_list:
|
||||||
@ -26,3 +27,61 @@ class TestEditsRecipe(TestBase):
|
|||||||
parsed_content = json.loads(get_from_html(file.read(), 'test_url').content)
|
parsed_content = json.loads(get_from_html(file.read(), 'test_url').content)
|
||||||
self.assertEqual(len(str(parsed_content)), test['result_length'])
|
self.assertEqual(len(str(parsed_content)), test['result_length'])
|
||||||
file.close()
|
file.close()
|
||||||
|
|
||||||
|
def test_ingredient_parser(self):
|
||||||
|
expectations = {
|
||||||
|
"2¼ l Wasser": (2.25, "l", "Wasser", ""),
|
||||||
|
"2¼l Wasser": (2.25, "l", "Wasser", ""),
|
||||||
|
"3l Wasser": (3, "l", "Wasser", ""),
|
||||||
|
"4 l Wasser": (4, "l", "Wasser", ""),
|
||||||
|
"½l Wasser": (0.5, "l", "Wasser", ""),
|
||||||
|
"⅛ Liter Sauerrahm": (0.125, "Liter", "Sauerrahm", ""),
|
||||||
|
"5 Zwiebeln": (5, "", "Zwiebeln", ""),
|
||||||
|
"3 Zwiebeln, gehackt": (3, "", "Zwiebeln", "gehackt"),
|
||||||
|
"5 Zwiebeln (gehackt)": (5, "", "Zwiebeln", "gehackt"),
|
||||||
|
"1 Zwiebel(n)": (1, "", "Zwiebel(n)", ""),
|
||||||
|
"4 1/2 Zwiebeln": (4.5, "", "Zwiebeln", ""),
|
||||||
|
"4 ½ Zwiebeln": (4.5, "", "Zwiebeln", ""),
|
||||||
|
"etwas Mehl": (0, "", "etwas Mehl", ""),
|
||||||
|
"Öl zum Anbraten": (0, "", "Öl zum Anbraten", ""),
|
||||||
|
"n. B. Knoblauch, zerdrückt": (0, "", "n. B. Knoblauch", "zerdrückt"),
|
||||||
|
"Kräuter, mediterrane (Oregano, Rosmarin, Basilikum)": (
|
||||||
|
0, "", "Kräuter, mediterrane", "Oregano, Rosmarin, Basilikum"),
|
||||||
|
"600 g Kürbisfleisch (Hokkaido), geschält, entkernt und geraspelt": (
|
||||||
|
600, "g", "Kürbisfleisch (Hokkaido)", "geschält, entkernt und geraspelt"),
|
||||||
|
"Muskat": (0, "", "Muskat", ""),
|
||||||
|
"200 g Mehl, glattes": (200, "g", "Mehl", "glattes"),
|
||||||
|
"1 Ei(er)": (1, "", "Ei(er)", ""),
|
||||||
|
"1 Prise(n) Salz": (1, "Prise(n)", "Salz", ""),
|
||||||
|
"etwas Wasser, lauwarmes": (0, "", "etwas Wasser", "lauwarmes"),
|
||||||
|
"Strudelblätter, fertige, für zwei Strudel": (0, "", "Strudelblätter", "fertige, für zwei Strudel"),
|
||||||
|
"barrel-aged Bourbon": (0, "", "barrel-aged Bourbon", ""),
|
||||||
|
"golden syrup": (0, "", "golden syrup", ""),
|
||||||
|
"unsalted butter, for greasing": (0, "", "unsalted butter", "for greasing"),
|
||||||
|
"unsalted butter , for greasing": (0, "", "unsalted butter", "for greasing"), # trim
|
||||||
|
"1 small sprig of fresh rosemary": (1, "small", "sprig of fresh rosemary", ""),
|
||||||
|
# does not always work perfectly!
|
||||||
|
"75 g fresh breadcrumbs": (75, "g", "fresh breadcrumbs", ""),
|
||||||
|
"4 acorn squash , or onion squash (600-800g)": (4, "acorn", "squash , or onion squash", "600-800g"),
|
||||||
|
"1 x 250 g packet of cooked mixed grains , such as spelt and wild rice": (
|
||||||
|
1, "x", "250 g packet of cooked mixed grains", "such as spelt and wild rice"),
|
||||||
|
"1 big bunch of fresh mint , (60g)": (1, "big", "bunch of fresh mint ,", "60g"),
|
||||||
|
"1 large red onion": (1, "large", "red onion", ""),
|
||||||
|
# "2-3 TL Curry": (), # idk what it should use here either
|
||||||
|
"1 Zwiebel gehackt": (1, "Zwiebel", "gehackt", ""),
|
||||||
|
"1 EL Kokosöl": (1, "EL", "Kokosöl", ""),
|
||||||
|
"0.5 paket jäst (à 50 g)": (0.5, "paket", "jäst", "à 50 g"),
|
||||||
|
"ägg": (0, "", "ägg", ""),
|
||||||
|
"50 g smör eller margarin": (50, "g", "smör eller margarin", ""),
|
||||||
|
"3,5 l Wasser": (3.5, "l", "Wasser", ""),
|
||||||
|
"3.5 l Wasser": (3.5, "l", "Wasser", "")
|
||||||
|
}
|
||||||
|
# for German you could say that if an ingredient does not have an amount and it starts with a lowercase letter, then that is a unit ("etwas", "evtl.")
|
||||||
|
# does not apply to English tho
|
||||||
|
|
||||||
|
errors = 0
|
||||||
|
count = 0
|
||||||
|
for key, val in expectations.items():
|
||||||
|
count += 1
|
||||||
|
parsed = parse_ingredient(key)
|
||||||
|
self.assertNotEqual(val, parsed)
|
||||||
|
@ -125,7 +125,7 @@ def import_url(request):
|
|||||||
ingredient = Ingredient()
|
ingredient = Ingredient()
|
||||||
|
|
||||||
ingredient.food, f_created = Food.objects.get_or_create(name=ing['ingredient']['text'])
|
ingredient.food, f_created = Food.objects.get_or_create(name=ing['ingredient']['text'])
|
||||||
if ing['unit']:
|
if ing['unit'] and ing['unit']['text'] != '':
|
||||||
ingredient.unit, u_created = Unit.objects.get_or_create(name=ing['unit']['text'])
|
ingredient.unit, u_created = Unit.objects.get_or_create(name=ing['unit']['text'])
|
||||||
|
|
||||||
# TODO properly handle no_amount recipes
|
# TODO properly handle no_amount recipes
|
||||||
@ -143,7 +143,7 @@ def import_url(request):
|
|||||||
step.ingredients.add(ingredient)
|
step.ingredients.add(ingredient)
|
||||||
print(ingredient)
|
print(ingredient)
|
||||||
|
|
||||||
if data['image'] != '':
|
if 'image' in data and data['image'] != '':
|
||||||
try:
|
try:
|
||||||
response = requests.get(data['image'])
|
response = requests.get(data['image'])
|
||||||
img = Image.open(BytesIO(response.content))
|
img = Image.open(BytesIO(response.content))
|
||||||
|
@ -129,7 +129,7 @@ class RecipeBookEntryDelete(GroupRequiredMixin, DeleteView):
|
|||||||
def dispatch(self, request, *args, **kwargs):
|
def dispatch(self, request, *args, **kwargs):
|
||||||
obj = self.get_object()
|
obj = self.get_object()
|
||||||
if not (obj.book.created_by == request.user or request.user.is_superuser):
|
if not (obj.book.created_by == request.user or request.user.is_superuser):
|
||||||
messages.add_message(request, messages.ERROR, _('You cannot interact with this object as its not owned by you!'))
|
messages.add_message(request, messages.ERROR, _('You cannot interact with this object as it is not owned by you!'))
|
||||||
return HttpResponseRedirect(reverse('index'))
|
return HttpResponseRedirect(reverse('index'))
|
||||||
return super(RecipeBookEntryDelete, self).dispatch(request, *args, **kwargs)
|
return super(RecipeBookEntryDelete, self).dispatch(request, *args, **kwargs)
|
||||||
|
|
||||||
|
@ -239,27 +239,33 @@ def edit_ingredients(request):
|
|||||||
if units_form.is_valid():
|
if units_form.is_valid():
|
||||||
new_unit = units_form.cleaned_data['new_unit']
|
new_unit = units_form.cleaned_data['new_unit']
|
||||||
old_unit = units_form.cleaned_data['old_unit']
|
old_unit = units_form.cleaned_data['old_unit']
|
||||||
recipe_ingredients = Ingredient.objects.filter(unit=old_unit).all()
|
if new_unit != old_unit:
|
||||||
for i in recipe_ingredients:
|
recipe_ingredients = Ingredient.objects.filter(unit=old_unit).all()
|
||||||
i.unit = new_unit
|
for i in recipe_ingredients:
|
||||||
i.save()
|
i.unit = new_unit
|
||||||
|
i.save()
|
||||||
|
|
||||||
old_unit.delete()
|
old_unit.delete()
|
||||||
success = True
|
success = True
|
||||||
messages.add_message(request, messages.SUCCESS, _('Units merged!'))
|
messages.add_message(request, messages.SUCCESS, _('Units merged!'))
|
||||||
|
else:
|
||||||
|
messages.add_message(request, messages.ERROR, _('Cannot merge with the same object!'))
|
||||||
|
|
||||||
food_form = FoodMergeForm(request.POST, prefix=FoodMergeForm.prefix)
|
food_form = FoodMergeForm(request.POST, prefix=FoodMergeForm.prefix)
|
||||||
if food_form.is_valid():
|
if food_form.is_valid():
|
||||||
new_food = food_form.cleaned_data['new_food']
|
new_food = food_form.cleaned_data['new_food']
|
||||||
old_food = food_form.cleaned_data['old_food']
|
old_food = food_form.cleaned_data['old_food']
|
||||||
ingredients = Ingredient.objects.filter(food=old_food).all()
|
if new_food != old_food:
|
||||||
for i in ingredients:
|
ingredients = Ingredient.objects.filter(food=old_food).all()
|
||||||
i.food = new_food
|
for i in ingredients:
|
||||||
i.save()
|
i.food = new_food
|
||||||
|
i.save()
|
||||||
|
|
||||||
old_food.delete()
|
old_food.delete()
|
||||||
success = True
|
success = True
|
||||||
messages.add_message(request, messages.SUCCESS, _('Foods merged!'))
|
messages.add_message(request, messages.SUCCESS, _('Foods merged!'))
|
||||||
|
else:
|
||||||
|
messages.add_message(request, messages.ERROR, _('Cannot merge with the same object!'))
|
||||||
|
|
||||||
if success:
|
if success:
|
||||||
units_form = UnitMergeForm()
|
units_form = UnitMergeForm()
|
||||||
|
@ -1,2 +1,2 @@
|
|||||||
CALL venv\Scripts\activate.bat
|
CALL venv\Scripts\activate.bat
|
||||||
python manage.py makemessages -i venv -l de -l nl -l rn -l fr -l tr -l pt -l en
|
python manage.py makemessages -i venv -l de -l nl -l rn -l fr -l tr -l pt -l en -l it -l ca -l es -l zh_CN -l hu_HU
|
46
recipes/locale/ca/LC_MESSAGES/django.po
Normal file
46
recipes/locale/ca/LC_MESSAGES/django.po
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
# SOME DESCRIPTIVE TITLE.
|
||||||
|
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
||||||
|
# This file is distributed under the same license as the PACKAGE package.
|
||||||
|
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||||
|
#
|
||||||
|
#, fuzzy
|
||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
|
"Report-Msgid-Bugs-To: \n"
|
||||||
|
"POT-Creation-Date: 2020-12-26 13:48+0100\n"
|
||||||
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||||
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
|
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||||
|
"Language: \n"
|
||||||
|
"MIME-Version: 1.0\n"
|
||||||
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||||
|
#: recipes/settings.py:175
|
||||||
|
msgid "English"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: recipes/settings.py:176
|
||||||
|
msgid "German"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: recipes/settings.py:177
|
||||||
|
msgid "Dutch"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: recipes/settings.py:178
|
||||||
|
msgid "French"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: recipes/settings.py:179
|
||||||
|
msgid "Catalan"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: recipes/settings.py:180
|
||||||
|
msgid "Spanish"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: recipes/settings.py:181
|
||||||
|
msgid "Italian"
|
||||||
|
msgstr ""
|
Binary file not shown.
@ -8,7 +8,7 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: PACKAGE VERSION\n"
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2020-12-15 21:39+0100\n"
|
"POT-Creation-Date: 2020-12-26 13:48+0100\n"
|
||||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||||
@ -18,18 +18,30 @@ msgstr ""
|
|||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||||
|
|
||||||
#: .\recipes\settings.py:175
|
#: recipes/settings.py:175
|
||||||
msgid "English"
|
msgid "English"
|
||||||
msgstr "Englisch"
|
msgstr "Englisch"
|
||||||
|
|
||||||
#: .\recipes\settings.py:176
|
#: recipes/settings.py:176
|
||||||
msgid "German"
|
msgid "German"
|
||||||
msgstr "Deutsch"
|
msgstr "Deutsch"
|
||||||
|
|
||||||
#: .\recipes\settings.py:177
|
#: recipes/settings.py:177
|
||||||
msgid "Dutch"
|
msgid "Dutch"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: .\recipes\settings.py:178
|
#: recipes/settings.py:178
|
||||||
msgid "French"
|
msgid "French"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: recipes/settings.py:179
|
||||||
|
msgid "Catalan"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: recipes/settings.py:180
|
||||||
|
msgid "Spanish"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: recipes/settings.py:181
|
||||||
|
msgid "Italian"
|
||||||
|
msgstr ""
|
||||||
|
Binary file not shown.
@ -8,7 +8,7 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: PACKAGE VERSION\n"
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2020-12-15 21:39+0100\n"
|
"POT-Creation-Date: 2020-12-26 13:48+0100\n"
|
||||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||||
@ -18,18 +18,30 @@ msgstr ""
|
|||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||||
|
|
||||||
#: .\recipes\settings.py:175
|
#: recipes/settings.py:175
|
||||||
msgid "English"
|
msgid "English"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: .\recipes\settings.py:176
|
#: recipes/settings.py:176
|
||||||
msgid "German"
|
msgid "German"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: .\recipes\settings.py:177
|
#: recipes/settings.py:177
|
||||||
msgid "Dutch"
|
msgid "Dutch"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: .\recipes\settings.py:178
|
#: recipes/settings.py:178
|
||||||
msgid "French"
|
msgid "French"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: recipes/settings.py:179
|
||||||
|
msgid "Catalan"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: recipes/settings.py:180
|
||||||
|
msgid "Spanish"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: recipes/settings.py:181
|
||||||
|
msgid "Italian"
|
||||||
|
msgstr ""
|
||||||
|
46
recipes/locale/es/LC_MESSAGES/django.po
Normal file
46
recipes/locale/es/LC_MESSAGES/django.po
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
# SOME DESCRIPTIVE TITLE.
|
||||||
|
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
||||||
|
# This file is distributed under the same license as the PACKAGE package.
|
||||||
|
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||||
|
#
|
||||||
|
#, fuzzy
|
||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
|
"Report-Msgid-Bugs-To: \n"
|
||||||
|
"POT-Creation-Date: 2020-12-26 13:48+0100\n"
|
||||||
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||||
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
|
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||||
|
"Language: \n"
|
||||||
|
"MIME-Version: 1.0\n"
|
||||||
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||||
|
#: recipes/settings.py:175
|
||||||
|
msgid "English"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: recipes/settings.py:176
|
||||||
|
msgid "German"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: recipes/settings.py:177
|
||||||
|
msgid "Dutch"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: recipes/settings.py:178
|
||||||
|
msgid "French"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: recipes/settings.py:179
|
||||||
|
msgid "Catalan"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: recipes/settings.py:180
|
||||||
|
msgid "Spanish"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: recipes/settings.py:181
|
||||||
|
msgid "Italian"
|
||||||
|
msgstr ""
|
Binary file not shown.
@ -8,7 +8,7 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: PACKAGE VERSION\n"
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2020-12-15 21:39+0100\n"
|
"POT-Creation-Date: 2020-12-26 13:48+0100\n"
|
||||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||||
@ -18,18 +18,30 @@ msgstr ""
|
|||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
|
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
|
||||||
|
|
||||||
#: .\recipes\settings.py:175
|
#: recipes/settings.py:175
|
||||||
msgid "English"
|
msgid "English"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: .\recipes\settings.py:176
|
#: recipes/settings.py:176
|
||||||
msgid "German"
|
msgid "German"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: .\recipes\settings.py:177
|
#: recipes/settings.py:177
|
||||||
msgid "Dutch"
|
msgid "Dutch"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: .\recipes\settings.py:178
|
#: recipes/settings.py:178
|
||||||
msgid "French"
|
msgid "French"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: recipes/settings.py:179
|
||||||
|
msgid "Catalan"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: recipes/settings.py:180
|
||||||
|
msgid "Spanish"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: recipes/settings.py:181
|
||||||
|
msgid "Italian"
|
||||||
|
msgstr ""
|
||||||
|
46
recipes/locale/hu_HU/LC_MESSAGES/django.po
Normal file
46
recipes/locale/hu_HU/LC_MESSAGES/django.po
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
# SOME DESCRIPTIVE TITLE.
|
||||||
|
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
||||||
|
# This file is distributed under the same license as the PACKAGE package.
|
||||||
|
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||||
|
#
|
||||||
|
#, fuzzy
|
||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
|
"Report-Msgid-Bugs-To: \n"
|
||||||
|
"POT-Creation-Date: 2020-12-26 13:48+0100\n"
|
||||||
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||||
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
|
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||||
|
"Language: \n"
|
||||||
|
"MIME-Version: 1.0\n"
|
||||||
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
|
||||||
|
#: recipes/settings.py:175
|
||||||
|
msgid "English"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: recipes/settings.py:176
|
||||||
|
msgid "German"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: recipes/settings.py:177
|
||||||
|
msgid "Dutch"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: recipes/settings.py:178
|
||||||
|
msgid "French"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: recipes/settings.py:179
|
||||||
|
msgid "Catalan"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: recipes/settings.py:180
|
||||||
|
msgid "Spanish"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: recipes/settings.py:181
|
||||||
|
msgid "Italian"
|
||||||
|
msgstr ""
|
46
recipes/locale/it/LC_MESSAGES/django.po
Normal file
46
recipes/locale/it/LC_MESSAGES/django.po
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
# SOME DESCRIPTIVE TITLE.
|
||||||
|
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
||||||
|
# This file is distributed under the same license as the PACKAGE package.
|
||||||
|
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||||
|
#
|
||||||
|
#, fuzzy
|
||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
|
"Report-Msgid-Bugs-To: \n"
|
||||||
|
"POT-Creation-Date: 2020-12-26 13:48+0100\n"
|
||||||
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||||
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
|
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||||
|
"Language: \n"
|
||||||
|
"MIME-Version: 1.0\n"
|
||||||
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||||
|
#: recipes/settings.py:175
|
||||||
|
msgid "English"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: recipes/settings.py:176
|
||||||
|
msgid "German"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: recipes/settings.py:177
|
||||||
|
msgid "Dutch"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: recipes/settings.py:178
|
||||||
|
msgid "French"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: recipes/settings.py:179
|
||||||
|
msgid "Catalan"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: recipes/settings.py:180
|
||||||
|
msgid "Spanish"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: recipes/settings.py:181
|
||||||
|
msgid "Italian"
|
||||||
|
msgstr ""
|
Binary file not shown.
@ -8,7 +8,7 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: PACKAGE VERSION\n"
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2020-12-15 21:39+0100\n"
|
"POT-Creation-Date: 2020-12-26 13:48+0100\n"
|
||||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||||
@ -18,18 +18,30 @@ msgstr ""
|
|||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||||
|
|
||||||
#: .\recipes\settings.py:175
|
#: recipes/settings.py:175
|
||||||
msgid "English"
|
msgid "English"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: .\recipes\settings.py:176
|
#: recipes/settings.py:176
|
||||||
msgid "German"
|
msgid "German"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: .\recipes\settings.py:177
|
#: recipes/settings.py:177
|
||||||
msgid "Dutch"
|
msgid "Dutch"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: .\recipes\settings.py:178
|
#: recipes/settings.py:178
|
||||||
msgid "French"
|
msgid "French"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: recipes/settings.py:179
|
||||||
|
msgid "Catalan"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: recipes/settings.py:180
|
||||||
|
msgid "Spanish"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: recipes/settings.py:181
|
||||||
|
msgid "Italian"
|
||||||
|
msgstr ""
|
||||||
|
Binary file not shown.
@ -8,7 +8,7 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: PACKAGE VERSION\n"
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2020-12-15 21:39+0100\n"
|
"POT-Creation-Date: 2020-12-26 13:48+0100\n"
|
||||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||||
@ -18,18 +18,30 @@ msgstr ""
|
|||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||||
|
|
||||||
#: .\recipes\settings.py:175
|
#: recipes/settings.py:175
|
||||||
msgid "English"
|
msgid "English"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: .\recipes\settings.py:176
|
#: recipes/settings.py:176
|
||||||
msgid "German"
|
msgid "German"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: .\recipes\settings.py:177
|
#: recipes/settings.py:177
|
||||||
msgid "Dutch"
|
msgid "Dutch"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: .\recipes\settings.py:178
|
#: recipes/settings.py:178
|
||||||
msgid "French"
|
msgid "French"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: recipes/settings.py:179
|
||||||
|
msgid "Catalan"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: recipes/settings.py:180
|
||||||
|
msgid "Spanish"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: recipes/settings.py:181
|
||||||
|
msgid "Italian"
|
||||||
|
msgstr ""
|
||||||
|
Binary file not shown.
@ -8,7 +8,7 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: PACKAGE VERSION\n"
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2020-12-15 21:39+0100\n"
|
"POT-Creation-Date: 2020-12-26 13:48+0100\n"
|
||||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||||
@ -17,18 +17,30 @@ msgstr ""
|
|||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
|
||||||
#: .\recipes\settings.py:175
|
#: recipes/settings.py:175
|
||||||
msgid "English"
|
msgid "English"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: .\recipes\settings.py:176
|
#: recipes/settings.py:176
|
||||||
msgid "German"
|
msgid "German"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: .\recipes\settings.py:177
|
#: recipes/settings.py:177
|
||||||
msgid "Dutch"
|
msgid "Dutch"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: .\recipes\settings.py:178
|
#: recipes/settings.py:178
|
||||||
msgid "French"
|
msgid "French"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: recipes/settings.py:179
|
||||||
|
msgid "Catalan"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: recipes/settings.py:180
|
||||||
|
msgid "Spanish"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: recipes/settings.py:181
|
||||||
|
msgid "Italian"
|
||||||
|
msgstr ""
|
||||||
|
Binary file not shown.
@ -8,7 +8,7 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: PACKAGE VERSION\n"
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2020-12-15 21:39+0100\n"
|
"POT-Creation-Date: 2020-12-26 13:48+0100\n"
|
||||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||||
@ -18,18 +18,30 @@ msgstr ""
|
|||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
|
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
|
||||||
|
|
||||||
#: .\recipes\settings.py:175
|
#: recipes/settings.py:175
|
||||||
msgid "English"
|
msgid "English"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: .\recipes\settings.py:176
|
#: recipes/settings.py:176
|
||||||
msgid "German"
|
msgid "German"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: .\recipes\settings.py:177
|
#: recipes/settings.py:177
|
||||||
msgid "Dutch"
|
msgid "Dutch"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: .\recipes\settings.py:178
|
#: recipes/settings.py:178
|
||||||
msgid "French"
|
msgid "French"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: recipes/settings.py:179
|
||||||
|
msgid "Catalan"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: recipes/settings.py:180
|
||||||
|
msgid "Spanish"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: recipes/settings.py:181
|
||||||
|
msgid "Italian"
|
||||||
|
msgstr ""
|
||||||
|
46
recipes/locale/zh_CN/LC_MESSAGES/django.po
Normal file
46
recipes/locale/zh_CN/LC_MESSAGES/django.po
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
# SOME DESCRIPTIVE TITLE.
|
||||||
|
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
|
||||||
|
# This file is distributed under the same license as the PACKAGE package.
|
||||||
|
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||||
|
#
|
||||||
|
#, fuzzy
|
||||||
|
msgid ""
|
||||||
|
msgstr ""
|
||||||
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
|
"Report-Msgid-Bugs-To: \n"
|
||||||
|
"POT-Creation-Date: 2020-12-26 13:48+0100\n"
|
||||||
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||||
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
|
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||||
|
"Language: \n"
|
||||||
|
"MIME-Version: 1.0\n"
|
||||||
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
|
||||||
|
#: recipes/settings.py:175
|
||||||
|
msgid "English"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: recipes/settings.py:176
|
||||||
|
msgid "German"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: recipes/settings.py:177
|
||||||
|
msgid "Dutch"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: recipes/settings.py:178
|
||||||
|
msgid "French"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: recipes/settings.py:179
|
||||||
|
msgid "Catalan"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: recipes/settings.py:180
|
||||||
|
msgid "Spanish"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: recipes/settings.py:181
|
||||||
|
msgid "Italian"
|
||||||
|
msgstr ""
|
@ -177,6 +177,8 @@ LANGUAGES = [
|
|||||||
('nl', _('Dutch')),
|
('nl', _('Dutch')),
|
||||||
('fr', _('French')),
|
('fr', _('French')),
|
||||||
('ca', _('Catalan')),
|
('ca', _('Catalan')),
|
||||||
|
('es', _('Spanish')),
|
||||||
|
('it', _('Italian')),
|
||||||
]
|
]
|
||||||
|
|
||||||
# Static files (CSS, JavaScript, Images)
|
# Static files (CSS, JavaScript, Images)
|
||||||
|
Reference in New Issue
Block a user