Merge branch 'develop' into feature/unit-conversion
# Conflicts: # requirements.txt
This commit is contained in:
commit
89e3e85d1e
@ -158,6 +158,7 @@ REVERSE_PROXY_AUTH=0
|
||||
#AUTH_LDAP_BIND_PASSWORD=
|
||||
#AUTH_LDAP_USER_SEARCH_BASE_DN=
|
||||
#AUTH_LDAP_TLS_CACERTFILE=
|
||||
#AUTH_LDAP_START_TLS=
|
||||
|
||||
# Enables exporting PDF (see export docs)
|
||||
# Disabled by default, uncomment to enable
|
||||
|
6
.github/workflows/build-docker.yml
vendored
6
.github/workflows/build-docker.yml
vendored
@ -115,13 +115,17 @@ jobs:
|
||||
needs: build-container
|
||||
if: startsWith(github.ref, 'refs/tags/')
|
||||
steps:
|
||||
- name: Set tag name
|
||||
run: |
|
||||
# Strip "refs/tags/" prefix
|
||||
echo "VERSION=${GITHUB_REF#refs/tags/}" >> $GITHUB_ENV
|
||||
# Send stable discord notification
|
||||
- name: Discord notification
|
||||
env:
|
||||
DISCORD_WEBHOOK: ${{ secrets.DISCORD_RELEASE_WEBHOOK }}
|
||||
uses: Ilshidur/action-discord@0.3.2
|
||||
with:
|
||||
args: '🚀 Version {{ EVENT_PAYLOAD.release.tag_name }} of tandoor has been released 🥳 Check it out https://github.com/vabene1111/recipes/releases/tag/{{ EVENT_PAYLOAD.release.tag_name }}'
|
||||
args: '🚀 Version {{ VERSION }} of tandoor has been released 🥳 Check it out https://github.com/vabene1111/recipes/releases/tag/{{ VERSION }}'
|
||||
|
||||
notify-beta:
|
||||
name: Notify Beta
|
||||
|
@ -3,9 +3,9 @@ from collections import Counter
|
||||
from datetime import date, timedelta
|
||||
|
||||
from django.contrib.postgres.search import SearchQuery, SearchRank, SearchVector, TrigramSimilarity
|
||||
from django.core.cache import cache
|
||||
from django.core.cache import caches
|
||||
from django.db.models import (Avg, Case, Count, Exists, F, Func, Max, OuterRef, Q, Subquery, Value, When, FilteredRelation)
|
||||
from django.core.cache import cache, caches
|
||||
from django.db.models import (Avg, Case, Count, Exists, F, Func, Max, OuterRef, Q, Subquery, Value,
|
||||
When)
|
||||
from django.db.models.functions import Coalesce, Lower, Substr
|
||||
from django.utils import timezone, translation
|
||||
from django.utils.translation import gettext as _
|
||||
@ -20,7 +20,8 @@ from recipes import settings
|
||||
# TODO create extensive tests to make sure ORs ANDs and various filters, sorting, etc work as expected
|
||||
# TODO consider creating a simpleListRecipe API that only includes minimum of recipe info and minimal filtering
|
||||
class RecipeSearch():
|
||||
_postgres = settings.DATABASES['default']['ENGINE'] in ['django.db.backends.postgresql_psycopg2', 'django.db.backends.postgresql']
|
||||
_postgres = settings.DATABASES['default']['ENGINE'] in [
|
||||
'django.db.backends.postgresql_psycopg2', 'django.db.backends.postgresql']
|
||||
|
||||
def __init__(self, request, **params):
|
||||
self._request = request
|
||||
@ -45,7 +46,8 @@ class RecipeSearch():
|
||||
cache.set(CACHE_KEY, self._search_prefs, timeout=10)
|
||||
else:
|
||||
self._search_prefs = SearchPreference()
|
||||
self._string = self._params.get('query').strip() if self._params.get('query', None) else None
|
||||
self._string = self._params.get('query').strip(
|
||||
) if self._params.get('query', None) else None
|
||||
self._rating = self._params.get('rating', None)
|
||||
self._keywords = {
|
||||
'or': self._params.get('keywords_or', None) or self._params.get('keywords', None),
|
||||
@ -74,7 +76,8 @@ class RecipeSearch():
|
||||
self._random = str2bool(self._params.get('random', False))
|
||||
self._new = str2bool(self._params.get('new', False))
|
||||
self._num_recent = int(self._params.get('num_recent', 0))
|
||||
self._include_children = str2bool(self._params.get('include_children', None))
|
||||
self._include_children = str2bool(
|
||||
self._params.get('include_children', None))
|
||||
self._timescooked = self._params.get('timescooked', None)
|
||||
self._cookedon = self._params.get('cookedon', None)
|
||||
self._createdon = self._params.get('createdon', None)
|
||||
@ -95,18 +98,24 @@ class RecipeSearch():
|
||||
self._search_type = self._search_prefs.search or 'plain'
|
||||
if self._string:
|
||||
if self._postgres:
|
||||
self._unaccent_include = self._search_prefs.unaccent.values_list('field', flat=True)
|
||||
self._unaccent_include = self._search_prefs.unaccent.values_list(
|
||||
'field', flat=True)
|
||||
else:
|
||||
self._unaccent_include = []
|
||||
self._icontains_include = [x + '__unaccent' if x in self._unaccent_include else x for x in self._search_prefs.icontains.values_list('field', flat=True)]
|
||||
self._istartswith_include = [x + '__unaccent' if x in self._unaccent_include else x for x in self._search_prefs.istartswith.values_list('field', flat=True)]
|
||||
self._icontains_include = [
|
||||
x + '__unaccent' if x in self._unaccent_include else x for x in self._search_prefs.icontains.values_list('field', flat=True)]
|
||||
self._istartswith_include = [
|
||||
x + '__unaccent' if x in self._unaccent_include else x for x in self._search_prefs.istartswith.values_list('field', flat=True)]
|
||||
self._trigram_include = None
|
||||
self._fulltext_include = None
|
||||
self._trigram = False
|
||||
if self._postgres and self._string:
|
||||
self._language = DICTIONARY.get(translation.get_language(), 'simple')
|
||||
self._trigram_include = [x + '__unaccent' if x in self._unaccent_include else x for x in self._search_prefs.trigram.values_list('field', flat=True)]
|
||||
self._fulltext_include = self._search_prefs.fulltext.values_list('field', flat=True) or None
|
||||
self._language = DICTIONARY.get(
|
||||
translation.get_language(), 'simple')
|
||||
self._trigram_include = [
|
||||
x + '__unaccent' if x in self._unaccent_include else x for x in self._search_prefs.trigram.values_list('field', flat=True)]
|
||||
self._fulltext_include = self._search_prefs.fulltext.values_list(
|
||||
'field', flat=True) or None
|
||||
|
||||
if self._search_type not in ['websearch', 'raw'] and self._trigram_include:
|
||||
self._trigram = True
|
||||
@ -182,8 +191,10 @@ class RecipeSearch():
|
||||
# otherwise sort by the remaining order_by attributes or favorite by default
|
||||
else:
|
||||
order += default_order
|
||||
order[:] = [Lower('name').asc() if x == 'name' else x for x in order]
|
||||
order[:] = [Lower('name').desc() if x == '-name' else x for x in order]
|
||||
order[:] = [Lower('name').asc() if x ==
|
||||
'name' else x for x in order]
|
||||
order[:] = [Lower('name').desc() if x ==
|
||||
'-name' else x for x in order]
|
||||
self.orderby = order
|
||||
|
||||
def string_filters(self, string=None):
|
||||
@ -200,21 +211,28 @@ class RecipeSearch():
|
||||
for f in self._filters:
|
||||
query_filter |= f
|
||||
|
||||
self._queryset = self._queryset.filter(query_filter).distinct() # this creates duplicate records which can screw up other aggregates, see makenow for workaround
|
||||
# this creates duplicate records which can screw up other aggregates, see makenow for workaround
|
||||
self._queryset = self._queryset.filter(query_filter).distinct()
|
||||
if self._fulltext_include:
|
||||
if self._fuzzy_match is None:
|
||||
self._queryset = self._queryset.annotate(score=Coalesce(Max(self.search_rank), 0.0))
|
||||
self._queryset = self._queryset.annotate(
|
||||
score=Coalesce(Max(self.search_rank), 0.0))
|
||||
else:
|
||||
self._queryset = self._queryset.annotate(rank=Coalesce(Max(self.search_rank), 0.0))
|
||||
self._queryset = self._queryset.annotate(
|
||||
rank=Coalesce(Max(self.search_rank), 0.0))
|
||||
|
||||
if self._fuzzy_match is not None:
|
||||
simularity = self._fuzzy_match.filter(pk=OuterRef('pk')).values('simularity')
|
||||
simularity = self._fuzzy_match.filter(
|
||||
pk=OuterRef('pk')).values('simularity')
|
||||
if not self._fulltext_include:
|
||||
self._queryset = self._queryset.annotate(score=Coalesce(Subquery(simularity), 0.0))
|
||||
self._queryset = self._queryset.annotate(
|
||||
score=Coalesce(Subquery(simularity), 0.0))
|
||||
else:
|
||||
self._queryset = self._queryset.annotate(simularity=Coalesce(Subquery(simularity), 0.0))
|
||||
self._queryset = self._queryset.annotate(
|
||||
simularity=Coalesce(Subquery(simularity), 0.0))
|
||||
if self._sort_includes('score') and self._fulltext_include and self._fuzzy_match is not None:
|
||||
self._queryset = self._queryset.annotate(score=F('rank') + F('simularity'))
|
||||
self._queryset = self._queryset.annotate(
|
||||
score=F('rank') + F('simularity'))
|
||||
else:
|
||||
query_filter = Q()
|
||||
for f in [x + '__unaccent__iexact' if x in self._unaccent_include else x + '__iexact' for x in SearchFields.objects.all().values_list('field', flat=True)]:
|
||||
@ -223,7 +241,8 @@ class RecipeSearch():
|
||||
|
||||
def _cooked_on_filter(self, cooked_date=None):
|
||||
if self._sort_includes('lastcooked') or cooked_date:
|
||||
lessthan = self._sort_includes('-lastcooked') or '-' in (cooked_date or [])[:1]
|
||||
lessthan = self._sort_includes(
|
||||
'-lastcooked') or '-' in (cooked_date or [])[:1]
|
||||
if lessthan:
|
||||
default = timezone.now() - timedelta(days=100000)
|
||||
else:
|
||||
@ -233,32 +252,41 @@ class RecipeSearch():
|
||||
if cooked_date is None:
|
||||
return
|
||||
|
||||
cooked_date = date(*[int(x) for x in cooked_date.split('-') if x != ''])
|
||||
cooked_date = date(*[int(x)
|
||||
for x in cooked_date.split('-') if x != ''])
|
||||
|
||||
if lessthan:
|
||||
self._queryset = self._queryset.filter(lastcooked__date__lte=cooked_date).exclude(lastcooked=default)
|
||||
self._queryset = self._queryset.filter(
|
||||
lastcooked__date__lte=cooked_date).exclude(lastcooked=default)
|
||||
else:
|
||||
self._queryset = self._queryset.filter(lastcooked__date__gte=cooked_date).exclude(lastcooked=default)
|
||||
self._queryset = self._queryset.filter(
|
||||
lastcooked__date__gte=cooked_date).exclude(lastcooked=default)
|
||||
|
||||
def _created_on_filter(self, created_date=None):
|
||||
if created_date is None:
|
||||
return
|
||||
lessthan = '-' in created_date[:1]
|
||||
created_date = date(*[int(x) for x in created_date.split('-') if x != ''])
|
||||
created_date = date(*[int(x)
|
||||
for x in created_date.split('-') if x != ''])
|
||||
if lessthan:
|
||||
self._queryset = self._queryset.filter(created_at__date__lte=created_date)
|
||||
self._queryset = self._queryset.filter(
|
||||
created_at__date__lte=created_date)
|
||||
else:
|
||||
self._queryset = self._queryset.filter(created_at__date__gte=created_date)
|
||||
self._queryset = self._queryset.filter(
|
||||
created_at__date__gte=created_date)
|
||||
|
||||
def _updated_on_filter(self, updated_date=None):
|
||||
if updated_date is None:
|
||||
return
|
||||
lessthan = '-' in updated_date[:1]
|
||||
updated_date = date(*[int(x) for x in updated_date.split('-') if x != ''])
|
||||
updated_date = date(*[int(x)
|
||||
for x in updated_date.split('-') if x != ''])
|
||||
if lessthan:
|
||||
self._queryset = self._queryset.filter(updated_at__date__lte=updated_date)
|
||||
self._queryset = self._queryset.filter(
|
||||
updated_at__date__lte=updated_date)
|
||||
else:
|
||||
self._queryset = self._queryset.filter(updated_at__date__gte=updated_date)
|
||||
self._queryset = self._queryset.filter(
|
||||
updated_at__date__gte=updated_date)
|
||||
|
||||
def _viewed_on_filter(self, viewed_date=None):
|
||||
if self._sort_includes('lastviewed') or viewed_date:
|
||||
@ -268,12 +296,15 @@ class RecipeSearch():
|
||||
if viewed_date is None:
|
||||
return
|
||||
lessthan = '-' in viewed_date[:1]
|
||||
viewed_date = date(*[int(x) for x in viewed_date.split('-') if x != ''])
|
||||
viewed_date = date(*[int(x)
|
||||
for x in viewed_date.split('-') if x != ''])
|
||||
|
||||
if lessthan:
|
||||
self._queryset = self._queryset.filter(lastviewed__date__lte=viewed_date).exclude(lastviewed=longTimeAgo)
|
||||
self._queryset = self._queryset.filter(
|
||||
lastviewed__date__lte=viewed_date).exclude(lastviewed=longTimeAgo)
|
||||
else:
|
||||
self._queryset = self._queryset.filter(lastviewed__date__gte=viewed_date).exclude(lastviewed=longTimeAgo)
|
||||
self._queryset = self._queryset.filter(
|
||||
lastviewed__date__gte=viewed_date).exclude(lastviewed=longTimeAgo)
|
||||
|
||||
def _new_recipes(self, new_days=7):
|
||||
# TODO make new days a user-setting
|
||||
@ -293,27 +324,32 @@ class RecipeSearch():
|
||||
|
||||
num_recent_recipes = ViewLog.objects.filter(created_by=self._request.user, space=self._request.space).values(
|
||||
'recipe').annotate(recent=Max('created_at')).order_by('-recent')[:num_recent]
|
||||
self._queryset = self._queryset.annotate(recent=Coalesce(Max(Case(When(pk__in=num_recent_recipes.values('recipe'), then='viewlog__pk'))), Value(0)))
|
||||
self._queryset = self._queryset.annotate(recent=Coalesce(Max(Case(When(
|
||||
pk__in=num_recent_recipes.values('recipe'), then='viewlog__pk'))), Value(0)))
|
||||
|
||||
def _favorite_recipes(self, times_cooked=None):
|
||||
if self._sort_includes('favorite') or times_cooked:
|
||||
less_than = '-' in (times_cooked or []) or not self._sort_includes('-favorite')
|
||||
less_than = '-' in (times_cooked or []
|
||||
) and not self._sort_includes('-favorite')
|
||||
if less_than:
|
||||
default = 1000
|
||||
else:
|
||||
default = 0
|
||||
favorite_recipes = CookLog.objects.filter(created_by=self._request.user, space=self._request.space, recipe=OuterRef('pk')
|
||||
).values('recipe').annotate(count=Count('pk', distinct=True)).values('count')
|
||||
self._queryset = self._queryset.annotate(favorite=Coalesce(Subquery(favorite_recipes), default))
|
||||
self._queryset = self._queryset.annotate(
|
||||
favorite=Coalesce(Subquery(favorite_recipes), default))
|
||||
if times_cooked is None:
|
||||
return
|
||||
|
||||
if times_cooked == '0':
|
||||
self._queryset = self._queryset.filter(favorite=0)
|
||||
elif less_than:
|
||||
self._queryset = self._queryset.filter(favorite__lte=int(times_cooked[1:])).exclude(favorite=0)
|
||||
self._queryset = self._queryset.filter(favorite__lte=int(
|
||||
times_cooked.replace('-', ''))).exclude(favorite=0)
|
||||
else:
|
||||
self._queryset = self._queryset.filter(favorite__gte=int(times_cooked))
|
||||
self._queryset = self._queryset.filter(
|
||||
favorite__gte=int(times_cooked))
|
||||
|
||||
def keyword_filters(self, **kwargs):
|
||||
if all([kwargs[x] is None for x in kwargs]):
|
||||
@ -346,7 +382,8 @@ class RecipeSearch():
|
||||
else:
|
||||
self._queryset = self._queryset.filter(f_and)
|
||||
if 'not' in kw_filter:
|
||||
self._queryset = self._queryset.exclude(id__in=recipes.values('id'))
|
||||
self._queryset = self._queryset.exclude(
|
||||
id__in=recipes.values('id'))
|
||||
|
||||
def food_filters(self, **kwargs):
|
||||
if all([kwargs[x] is None for x in kwargs]):
|
||||
@ -360,7 +397,8 @@ class RecipeSearch():
|
||||
foods = Food.objects.filter(pk__in=kwargs[fd_filter])
|
||||
if 'or' in fd_filter:
|
||||
if self._include_children:
|
||||
f_or = Q(steps__ingredients__food__in=Food.include_descendants(foods))
|
||||
f_or = Q(
|
||||
steps__ingredients__food__in=Food.include_descendants(foods))
|
||||
else:
|
||||
f_or = Q(steps__ingredients__food__in=foods)
|
||||
|
||||
@ -372,7 +410,8 @@ class RecipeSearch():
|
||||
recipes = Recipe.objects.all()
|
||||
for food in foods:
|
||||
if self._include_children:
|
||||
f_and = Q(steps__ingredients__food__in=food.get_descendants_and_self())
|
||||
f_and = Q(
|
||||
steps__ingredients__food__in=food.get_descendants_and_self())
|
||||
else:
|
||||
f_and = Q(steps__ingredients__food=food)
|
||||
if 'not' in fd_filter:
|
||||
@ -380,7 +419,8 @@ class RecipeSearch():
|
||||
else:
|
||||
self._queryset = self._queryset.filter(f_and)
|
||||
if 'not' in fd_filter:
|
||||
self._queryset = self._queryset.exclude(id__in=recipes.values('id'))
|
||||
self._queryset = self._queryset.exclude(
|
||||
id__in=recipes.values('id'))
|
||||
|
||||
def unit_filters(self, units=None, operator=True):
|
||||
if operator != True:
|
||||
@ -389,7 +429,8 @@ class RecipeSearch():
|
||||
return
|
||||
if not isinstance(units, list):
|
||||
units = [units]
|
||||
self._queryset = self._queryset.filter(steps__ingredients__unit__in=units)
|
||||
self._queryset = self._queryset.filter(
|
||||
steps__ingredients__unit__in=units)
|
||||
|
||||
def rating_filter(self, rating=None):
|
||||
if rating or self._sort_includes('rating'):
|
||||
@ -399,14 +440,16 @@ class RecipeSearch():
|
||||
else:
|
||||
default = 0
|
||||
# TODO make ratings a settings user-only vs all-users
|
||||
self._queryset = self._queryset.annotate(rating=Round(Avg(Case(When(cooklog__created_by=self._request.user, then='cooklog__rating'), default=default))))
|
||||
self._queryset = self._queryset.annotate(rating=Round(Avg(Case(When(
|
||||
cooklog__created_by=self._request.user, then='cooklog__rating'), default=default))))
|
||||
if rating is None:
|
||||
return
|
||||
|
||||
if rating == '0':
|
||||
self._queryset = self._queryset.filter(rating=0)
|
||||
elif lessthan:
|
||||
self._queryset = self._queryset.filter(rating__lte=int(rating[1:])).exclude(rating=0)
|
||||
self._queryset = self._queryset.filter(
|
||||
rating__lte=int(rating[1:])).exclude(rating=0)
|
||||
else:
|
||||
self._queryset = self._queryset.filter(rating__gte=int(rating))
|
||||
|
||||
@ -434,11 +477,14 @@ class RecipeSearch():
|
||||
recipes = Recipe.objects.all()
|
||||
for book in kwargs[bk_filter]:
|
||||
if 'not' in bk_filter:
|
||||
recipes = recipes.filter(recipebookentry__book__id=book)
|
||||
recipes = recipes.filter(
|
||||
recipebookentry__book__id=book)
|
||||
else:
|
||||
self._queryset = self._queryset.filter(recipebookentry__book__id=book)
|
||||
self._queryset = self._queryset.filter(
|
||||
recipebookentry__book__id=book)
|
||||
if 'not' in bk_filter:
|
||||
self._queryset = self._queryset.exclude(id__in=recipes.values('id'))
|
||||
self._queryset = self._queryset.exclude(
|
||||
id__in=recipes.values('id'))
|
||||
|
||||
def step_filters(self, steps=None, operator=True):
|
||||
if operator != True:
|
||||
@ -446,7 +492,7 @@ class RecipeSearch():
|
||||
if not steps:
|
||||
return
|
||||
if not isinstance(steps, list):
|
||||
steps = [unistepsts]
|
||||
steps = [steps]
|
||||
self._queryset = self._queryset.filter(steps__id__in=steps)
|
||||
|
||||
def build_fulltext_filters(self, string=None):
|
||||
@ -457,20 +503,25 @@ class RecipeSearch():
|
||||
rank = []
|
||||
if 'name' in self._fulltext_include:
|
||||
vectors.append('name_search_vector')
|
||||
rank.append(SearchRank('name_search_vector', self.search_query, cover_density=True))
|
||||
rank.append(SearchRank('name_search_vector',
|
||||
self.search_query, cover_density=True))
|
||||
if 'description' in self._fulltext_include:
|
||||
vectors.append('desc_search_vector')
|
||||
rank.append(SearchRank('desc_search_vector', self.search_query, cover_density=True))
|
||||
rank.append(SearchRank('desc_search_vector',
|
||||
self.search_query, cover_density=True))
|
||||
if 'steps__instruction' in self._fulltext_include:
|
||||
vectors.append('steps__search_vector')
|
||||
rank.append(SearchRank('steps__search_vector', self.search_query, cover_density=True))
|
||||
rank.append(SearchRank('steps__search_vector',
|
||||
self.search_query, cover_density=True))
|
||||
if 'keywords__name' in self._fulltext_include:
|
||||
# explicitly settings unaccent on keywords and foods so that they behave the same as search_vector fields
|
||||
vectors.append('keywords__name__unaccent')
|
||||
rank.append(SearchRank('keywords__name__unaccent', self.search_query, cover_density=True))
|
||||
rank.append(SearchRank('keywords__name__unaccent',
|
||||
self.search_query, cover_density=True))
|
||||
if 'steps__ingredients__food__name' in self._fulltext_include:
|
||||
vectors.append('steps__ingredients__food__name__unaccent')
|
||||
rank.append(SearchRank('steps__ingredients__food__name', self.search_query, cover_density=True))
|
||||
rank.append(SearchRank('steps__ingredients__food__name',
|
||||
self.search_query, cover_density=True))
|
||||
|
||||
for r in rank:
|
||||
if self.search_rank is None:
|
||||
@ -478,7 +529,8 @@ class RecipeSearch():
|
||||
else:
|
||||
self.search_rank += r
|
||||
# modifying queryset will annotation creates duplicate results
|
||||
self._filters.append(Q(id__in=Recipe.objects.annotate(vector=SearchVector(*vectors)).filter(Q(vector=self.search_query))))
|
||||
self._filters.append(Q(id__in=Recipe.objects.annotate(
|
||||
vector=SearchVector(*vectors)).filter(Q(vector=self.search_query))))
|
||||
|
||||
def build_text_filters(self, string=None):
|
||||
if not string:
|
||||
@ -510,23 +562,30 @@ class RecipeSearch():
|
||||
def _makenow_filter(self, missing=None):
|
||||
if missing is None or (type(missing) == bool and missing == False):
|
||||
return
|
||||
shopping_users = [*self._request.user.get_shopping_share(), self._request.user]
|
||||
shopping_users = [
|
||||
*self._request.user.get_shopping_share(), self._request.user]
|
||||
|
||||
onhand_filter = (
|
||||
Q(steps__ingredients__food__onhand_users__in=shopping_users) # food onhand
|
||||
| Q(steps__ingredients__food__substitute__onhand_users__in=shopping_users) # or substitute food onhand
|
||||
# or substitute food onhand
|
||||
| Q(steps__ingredients__food__substitute__onhand_users__in=shopping_users)
|
||||
| Q(steps__ingredients__food__in=self.__children_substitute_filter(shopping_users))
|
||||
| Q(steps__ingredients__food__in=self.__sibling_substitute_filter(shopping_users))
|
||||
)
|
||||
makenow_recipes = Recipe.objects.annotate(
|
||||
count_food=Count('steps__ingredients__food__pk', filter=Q(steps__ingredients__food__isnull=False), distinct=True),
|
||||
count_onhand=Count('steps__ingredients__food__pk', filter=onhand_filter, distinct=True),
|
||||
count_food=Count('steps__ingredients__food__pk', filter=Q(
|
||||
steps__ingredients__food__isnull=False), distinct=True),
|
||||
count_onhand=Count('steps__ingredients__food__pk',
|
||||
filter=onhand_filter, distinct=True),
|
||||
count_ignore_shopping=Count('steps__ingredients__food__pk', filter=Q(steps__ingredients__food__ignore_shopping=True,
|
||||
steps__ingredients__food__recipe__isnull=True), distinct=True),
|
||||
has_child_sub=Case(When(steps__ingredients__food__in=self.__children_substitute_filter(shopping_users), then=Value(1)), default=Value(0)),
|
||||
has_sibling_sub=Case(When(steps__ingredients__food__in=self.__sibling_substitute_filter(shopping_users), then=Value(1)), default=Value(0))
|
||||
has_child_sub=Case(When(steps__ingredients__food__in=self.__children_substitute_filter(
|
||||
shopping_users), then=Value(1)), default=Value(0)),
|
||||
has_sibling_sub=Case(When(steps__ingredients__food__in=self.__sibling_substitute_filter(
|
||||
shopping_users), then=Value(1)), default=Value(0))
|
||||
).annotate(missingfood=F('count_food') - F('count_onhand') - F('count_ignore_shopping')).filter(missingfood=missing)
|
||||
self._queryset = self._queryset.distinct().filter(id__in=makenow_recipes.values('id'))
|
||||
self._queryset = self._queryset.distinct().filter(
|
||||
id__in=makenow_recipes.values('id'))
|
||||
|
||||
@staticmethod
|
||||
def __children_substitute_filter(shopping_users=None):
|
||||
@ -547,7 +606,8 @@ class RecipeSearch():
|
||||
@staticmethod
|
||||
def __sibling_substitute_filter(shopping_users=None):
|
||||
sibling_onhand_subquery = Food.objects.filter(
|
||||
path__startswith=Substr(OuterRef('path'), 1, Food.steplen * (OuterRef('depth') - 1)),
|
||||
path__startswith=Substr(
|
||||
OuterRef('path'), 1, Food.steplen * (OuterRef('depth') - 1)),
|
||||
depth=OuterRef('depth'),
|
||||
onhand_users__in=shopping_users
|
||||
)
|
||||
@ -586,7 +646,8 @@ class RecipeFacet():
|
||||
self.Recent = self._cache.get('Recent', None)
|
||||
|
||||
if self._queryset is not None:
|
||||
self._recipe_list = list(self._queryset.values_list('id', flat=True))
|
||||
self._recipe_list = list(
|
||||
self._queryset.values_list('id', flat=True))
|
||||
self._search_params = {
|
||||
'keyword_list': self._request.query_params.getlist('keywords', []),
|
||||
'food_list': self._request.query_params.getlist('foods', []),
|
||||
@ -618,7 +679,8 @@ class RecipeFacet():
|
||||
'Books': self.Books
|
||||
|
||||
}
|
||||
caches['default'].set(self._SEARCH_CACHE_KEY, self._cache, self._cache_timeout)
|
||||
caches['default'].set(self._SEARCH_CACHE_KEY,
|
||||
self._cache, self._cache_timeout)
|
||||
|
||||
def get_facets(self, from_cache=False):
|
||||
if from_cache:
|
||||
@ -655,13 +717,16 @@ class RecipeFacet():
|
||||
def get_keywords(self):
|
||||
if self.Keywords is None:
|
||||
if self._search_params['search_keywords_or']:
|
||||
keywords = Keyword.objects.filter(space=self._request.space).distinct()
|
||||
keywords = Keyword.objects.filter(
|
||||
space=self._request.space).distinct()
|
||||
else:
|
||||
keywords = Keyword.objects.filter(Q(recipe__in=self._recipe_list) | Q(depth=1)).filter(space=self._request.space).distinct()
|
||||
keywords = Keyword.objects.filter(Q(recipe__in=self._recipe_list) | Q(
|
||||
depth=1)).filter(space=self._request.space).distinct()
|
||||
|
||||
# set keywords to root objects only
|
||||
keywords = self._keyword_queryset(keywords)
|
||||
self.Keywords = [{**x, 'children': None} if x['numchild'] > 0 else x for x in list(keywords)]
|
||||
self.Keywords = [{**x, 'children': None}
|
||||
if x['numchild'] > 0 else x for x in list(keywords)]
|
||||
self.set_cache('Keywords', self.Keywords)
|
||||
return self.Keywords
|
||||
|
||||
@ -669,28 +734,28 @@ class RecipeFacet():
|
||||
if self.Foods is None:
|
||||
# # if using an OR search, will annotate all keywords, otherwise, just those that appear in results
|
||||
if self._search_params['search_foods_or']:
|
||||
foods = Food.objects.filter(space=self._request.space).distinct()
|
||||
foods = Food.objects.filter(
|
||||
space=self._request.space).distinct()
|
||||
else:
|
||||
foods = Food.objects.filter(Q(ingredient__step__recipe__in=self._recipe_list) | Q(depth=1)).filter(space=self._request.space).distinct()
|
||||
foods = Food.objects.filter(Q(ingredient__step__recipe__in=self._recipe_list) | Q(
|
||||
depth=1)).filter(space=self._request.space).distinct()
|
||||
|
||||
# set keywords to root objects only
|
||||
foods = self._food_queryset(foods)
|
||||
|
||||
self.Foods = [{**x, 'children': None} if x['numchild'] > 0 else x for x in list(foods)]
|
||||
self.Foods = [{**x, 'children': None}
|
||||
if x['numchild'] > 0 else x for x in list(foods)]
|
||||
self.set_cache('Foods', self.Foods)
|
||||
return self.Foods
|
||||
|
||||
def get_books(self):
|
||||
if self.Books is None:
|
||||
self.Books = []
|
||||
return self.Books
|
||||
|
||||
def get_ratings(self):
|
||||
if self.Ratings is None:
|
||||
if not self._request.space.demo and self._request.space.show_facet_count:
|
||||
if self._queryset is None:
|
||||
self._queryset = Recipe.objects.filter(id__in=self._recipe_list)
|
||||
rating_qs = self._queryset.annotate(rating=Round(Avg(Case(When(cooklog__created_by=self._request.user, then='cooklog__rating'), default=Value(0)))))
|
||||
self._queryset = Recipe.objects.filter(
|
||||
id__in=self._recipe_list)
|
||||
rating_qs = self._queryset.annotate(rating=Round(Avg(Case(When(
|
||||
cooklog__created_by=self._request.user, then='cooklog__rating'), default=Value(0)))))
|
||||
self.Ratings = dict(Counter(r.rating for r in rating_qs))
|
||||
else:
|
||||
self.Rating = {}
|
||||
@ -715,10 +780,13 @@ class RecipeFacet():
|
||||
foods = self._food_queryset(food.get_children(), food)
|
||||
deep_search = self.Foods
|
||||
for node in nodes:
|
||||
index = next((i for i, x in enumerate(deep_search) if x["id"] == node.id), None)
|
||||
index = next((i for i, x in enumerate(
|
||||
deep_search) if x["id"] == node.id), None)
|
||||
deep_search = deep_search[index]['children']
|
||||
index = next((i for i, x in enumerate(deep_search) if x["id"] == food.id), None)
|
||||
deep_search[index]['children'] = [{**x, 'children': None} if x['numchild'] > 0 else x for x in list(foods)]
|
||||
index = next((i for i, x in enumerate(
|
||||
deep_search) if x["id"] == food.id), None)
|
||||
deep_search[index]['children'] = [
|
||||
{**x, 'children': None} if x['numchild'] > 0 else x for x in list(foods)]
|
||||
self.set_cache('Foods', self.Foods)
|
||||
return self.get_facets()
|
||||
|
||||
@ -731,10 +799,13 @@ class RecipeFacet():
|
||||
keywords = self._keyword_queryset(keyword.get_children(), keyword)
|
||||
deep_search = self.Keywords
|
||||
for node in nodes:
|
||||
index = next((i for i, x in enumerate(deep_search) if x["id"] == node.id), None)
|
||||
index = next((i for i, x in enumerate(
|
||||
deep_search) if x["id"] == node.id), None)
|
||||
deep_search = deep_search[index]['children']
|
||||
index = next((i for i, x in enumerate(deep_search) if x["id"] == keyword.id), None)
|
||||
deep_search[index]['children'] = [{**x, 'children': None} if x['numchild'] > 0 else x for x in list(keywords)]
|
||||
index = next((i for i, x in enumerate(deep_search)
|
||||
if x["id"] == keyword.id), None)
|
||||
deep_search[index]['children'] = [
|
||||
{**x, 'children': None} if x['numchild'] > 0 else x for x in list(keywords)]
|
||||
self.set_cache('Keywords', self.Keywords)
|
||||
return self.get_facets()
|
||||
|
||||
|
@ -1,8 +1,8 @@
|
||||
import random
|
||||
# import random
|
||||
import re
|
||||
from html import unescape
|
||||
from unicodedata import decomposition
|
||||
|
||||
from django.core.cache import caches
|
||||
from django.utils.dateparse import parse_duration
|
||||
from django.utils.translation import gettext as _
|
||||
from isodate import parse_duration as iso_parse_duration
|
||||
@ -10,9 +10,11 @@ from isodate.isoerror import ISO8601Error
|
||||
from pytube import YouTube
|
||||
from recipe_scrapers._utils import get_host_name, get_minutes
|
||||
|
||||
from cookbook.helper import recipe_url_import as helper
|
||||
# from cookbook.helper import recipe_url_import as helper
|
||||
from cookbook.helper.ingredient_parser import IngredientParser
|
||||
from cookbook.models import Keyword, Automation
|
||||
from cookbook.models import Automation, Keyword
|
||||
|
||||
# from unicodedata import decomposition
|
||||
|
||||
|
||||
# from recipe_scrapers._utils import get_minutes ## temporary until/unless upstream incorporates get_minutes() PR
|
||||
@ -127,7 +129,7 @@ def get_from_scraper(scrape, request):
|
||||
try:
|
||||
if scrape.author():
|
||||
keywords.append(scrape.author())
|
||||
except:
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
try:
|
||||
@ -367,10 +369,28 @@ def parse_time(recipe_time):
|
||||
|
||||
def parse_keywords(keyword_json, space):
|
||||
keywords = []
|
||||
keyword_aliases = {}
|
||||
# retrieve keyword automation cache if it exists, otherwise build from database
|
||||
KEYWORD_CACHE_KEY = f'automation_keyword_alias_{space.pk}'
|
||||
if c := caches['default'].get(KEYWORD_CACHE_KEY, None):
|
||||
self.food_aliases = c
|
||||
caches['default'].touch(KEYWORD_CACHE_KEY, 30)
|
||||
else:
|
||||
for a in Automation.objects.filter(space=space, disabled=False, type=Automation.KEYWORD_ALIAS).only('param_1', 'param_2').order_by('order').all():
|
||||
keyword_aliases[a.param_1] = a.param_2
|
||||
caches['default'].set(KEYWORD_CACHE_KEY, keyword_aliases, 30)
|
||||
|
||||
# keywords as list
|
||||
for kw in keyword_json:
|
||||
kw = normalize_string(kw)
|
||||
# if alias exists use that instead
|
||||
|
||||
if len(kw) != 0:
|
||||
if keyword_aliases:
|
||||
try:
|
||||
kw = keyword_aliases[kw]
|
||||
except KeyError:
|
||||
pass
|
||||
if k := Keyword.objects.filter(name=kw, space=space).first():
|
||||
keywords.append({'label': str(k), 'name': k.name, 'id': k.id})
|
||||
else:
|
||||
|
Binary file not shown.
@ -8,8 +8,8 @@ msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2022-04-29 18:42+0200\n"
|
||||
"PO-Revision-Date: 2022-05-10 15:32+0000\n"
|
||||
"Last-Translator: zeon <zeonbg@gmail.com>\n"
|
||||
"PO-Revision-Date: 2023-04-12 11:55+0000\n"
|
||||
"Last-Translator: noxonad <noxonad@proton.me>\n"
|
||||
"Language-Team: Bulgarian <http://translate.tandoor.dev/projects/tandoor/"
|
||||
"recipes-backend/bg/>\n"
|
||||
"Language: bg\n"
|
||||
@ -17,7 +17,7 @@ msgstr ""
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=n != 1;\n"
|
||||
"X-Generator: Weblate 4.10.1\n"
|
||||
"X-Generator: Weblate 4.15\n"
|
||||
|
||||
#: .\cookbook\filters.py:23 .\cookbook\templates\forms\ingredients.html:34
|
||||
#: .\cookbook\templates\space.html:49 .\cookbook\templates\stats.html:28
|
||||
@ -1433,7 +1433,7 @@ msgstr ""
|
||||
|
||||
#: .\cookbook\templates\index.html:29
|
||||
msgid "Search recipe ..."
|
||||
msgstr "Търсете рецепта..."
|
||||
msgstr "Търсете рецепта ..."
|
||||
|
||||
#: .\cookbook\templates\index.html:44
|
||||
msgid "New Recipe"
|
||||
|
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Binary file not shown.
@ -8,8 +8,8 @@ msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2022-04-29 18:42+0200\n"
|
||||
"PO-Revision-Date: 2023-03-06 10:55+0000\n"
|
||||
"Last-Translator: Anders Obro <oebro@duck.com>\n"
|
||||
"PO-Revision-Date: 2023-04-12 11:55+0000\n"
|
||||
"Last-Translator: noxonad <noxonad@proton.me>\n"
|
||||
"Language-Team: Danish <http://translate.tandoor.dev/projects/tandoor/"
|
||||
"recipes-backend/da/>\n"
|
||||
"Language: da\n"
|
||||
|
Binary file not shown.
File diff suppressed because it is too large
Load Diff
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.
Binary file not shown.
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@ -8,8 +8,8 @@ msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2021-04-11 15:09+0200\n"
|
||||
"PO-Revision-Date: 2021-04-11 15:23+0000\n"
|
||||
"Last-Translator: Allan Nordhøy <epost@anotheragency.no>\n"
|
||||
"PO-Revision-Date: 2023-04-17 20:55+0000\n"
|
||||
"Last-Translator: Espen Sellevåg <buskmenn.drammer03@icloud.com>\n"
|
||||
"Language-Team: Norwegian Bokmål <http://translate.tandoor.dev/projects/"
|
||||
"tandoor/recipes-backend/nb_NO/>\n"
|
||||
"Language: nb_NO\n"
|
||||
@ -17,7 +17,7 @@ msgstr ""
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=n != 1;\n"
|
||||
"X-Generator: Weblate 4.5.3\n"
|
||||
"X-Generator: Weblate 4.15\n"
|
||||
|
||||
#: .\cookbook\filters.py:23 .\cookbook\templates\base.html:91
|
||||
#: .\cookbook\templates\forms\edit_internal_recipe.html:219
|
||||
@ -34,19 +34,23 @@ msgstr ""
|
||||
|
||||
#: .\cookbook\forms.py:46
|
||||
msgid "Default Unit to be used when inserting a new ingredient into a recipe."
|
||||
msgstr ""
|
||||
msgstr "Standard enhet når ny ingrediens legges til en oppskrift."
|
||||
|
||||
#: .\cookbook\forms.py:47
|
||||
msgid ""
|
||||
"Enables support for fractions in ingredient amounts (e.g. convert decimals "
|
||||
"to fractions automatically)"
|
||||
msgstr ""
|
||||
"Aktiverer støtte for deler av ingrediensmengde (konverterer feks. desimaler "
|
||||
"til deler automatisk)"
|
||||
|
||||
#: .\cookbook\forms.py:48
|
||||
msgid ""
|
||||
"Users with whom newly created meal plan/shopping list entries should be "
|
||||
"shared by default."
|
||||
msgstr ""
|
||||
"Brukere som oppretter nye måltidsplaner/handlelister, deler disse "
|
||||
"oppføringene som standard."
|
||||
|
||||
#: .\cookbook\forms.py:49
|
||||
msgid "Show recently viewed recipes on search page."
|
||||
@ -58,7 +62,7 @@ msgstr "Antall desimaler ingredienser skal avrundes til."
|
||||
|
||||
#: .\cookbook\forms.py:51
|
||||
msgid "If you want to be able to create and see comments underneath recipes."
|
||||
msgstr ""
|
||||
msgstr "Hvis du ønsker å opprette og se kommentarer under oppskrifter."
|
||||
|
||||
#: .\cookbook\forms.py:53
|
||||
msgid ""
|
||||
@ -67,6 +71,11 @@ msgid ""
|
||||
"Useful when shopping with multiple people but might use a little bit of "
|
||||
"mobile data. If lower than instance limit it is reset when saving."
|
||||
msgstr ""
|
||||
"0 vil deaktivere automatisk synkronisering. Når en handleliste vises, "
|
||||
"oppdateres listen med oppgitt antall sekunders mellomrom for å synkronisere "
|
||||
"endringer fra andre brukere. Nyttig dersom flere brukere handler samtidig. "
|
||||
"Datatrafikk oppstår når aktiv. Hvis verdien er lavere enn grensen, "
|
||||
"tilbakestilles den ved lagring."
|
||||
|
||||
#: .\cookbook\forms.py:56
|
||||
msgid "Makes the navbar stick to the top of the page."
|
||||
@ -100,11 +109,11 @@ msgstr ""
|
||||
|
||||
#: .\cookbook\forms.py:97 .\cookbook\forms.py:317
|
||||
msgid "Path"
|
||||
msgstr ""
|
||||
msgstr "Sti"
|
||||
|
||||
#: .\cookbook\forms.py:98
|
||||
msgid "Storage UID"
|
||||
msgstr ""
|
||||
msgstr "Lagring UID"
|
||||
|
||||
#: .\cookbook\forms.py:121
|
||||
msgid "Default"
|
||||
@ -129,7 +138,6 @@ msgid "Old Unit"
|
||||
msgstr "Gammel enhet"
|
||||
|
||||
#: .\cookbook\forms.py:156
|
||||
#, fuzzy
|
||||
msgid "Unit that should be replaced."
|
||||
msgstr "Enhet som skal erstattes."
|
||||
|
||||
@ -204,12 +212,11 @@ msgstr ""
|
||||
#: .\cookbook\views\views.py:112 .\cookbook\views\views.py:116
|
||||
#: .\cookbook\views\views.py:184
|
||||
msgid "You do not have the required permissions to view this page!"
|
||||
msgstr "Du har ikke påkrevd tilgang for å vise denne siden."
|
||||
msgstr "Du har ikke påkrevd tilgang for å vise denne siden!"
|
||||
|
||||
#: .\cookbook\helper\permission_helper.py:141
|
||||
#, fuzzy
|
||||
msgid "You are not logged in and therefore cannot view this page!"
|
||||
msgstr "Du er ikke innlogget og kan derfor ikke vise siden."
|
||||
msgstr "Du er ikke innlogget og kan derfor ikke vise siden!"
|
||||
|
||||
#: .\cookbook\helper\permission_helper.py:145
|
||||
#: .\cookbook\helper\permission_helper.py:167
|
||||
@ -379,7 +386,7 @@ msgstr "Finner ikke siden du leter etter."
|
||||
|
||||
#: .\cookbook\templates\404.html:33
|
||||
msgid "Take me Home"
|
||||
msgstr ""
|
||||
msgstr "Tilbake til Startsiden"
|
||||
|
||||
#: .\cookbook\templates\404.html:35
|
||||
msgid "Report a Bug"
|
||||
@ -388,12 +395,12 @@ msgstr "Rapporter en feil"
|
||||
#: .\cookbook\templates\account\login.html:7
|
||||
#: .\cookbook\templates\base.html:170
|
||||
msgid "Login"
|
||||
msgstr ""
|
||||
msgstr "Logg inn"
|
||||
|
||||
#: .\cookbook\templates\account\login.html:13
|
||||
#: .\cookbook\templates\account\login.html:28
|
||||
msgid "Sign In"
|
||||
msgstr ""
|
||||
msgstr "Opprett bruker"
|
||||
|
||||
#: .\cookbook\templates\account\login.html:38
|
||||
msgid "Social Login"
|
||||
@ -401,7 +408,7 @@ msgstr "Sosial innlogging"
|
||||
|
||||
#: .\cookbook\templates\account\login.html:39
|
||||
msgid "You can use any of the following providers to sign in."
|
||||
msgstr ""
|
||||
msgstr "Velg en av følgende leverandører for å logge på."
|
||||
|
||||
#: .\cookbook\templates\account\logout.html:5
|
||||
#: .\cookbook\templates\account\logout.html:9
|
||||
@ -416,20 +423,20 @@ msgstr "Er du sikker på at du vil logge ut?"
|
||||
#: .\cookbook\templates\account\password_reset.html:5
|
||||
#: .\cookbook\templates\account\password_reset_done.html:5
|
||||
msgid "Password Reset"
|
||||
msgstr ""
|
||||
msgstr "Nullstill passord"
|
||||
|
||||
#: .\cookbook\templates\account\password_reset.html:9
|
||||
#: .\cookbook\templates\account\password_reset_done.html:9
|
||||
msgid "Password reset is not implemented for the time being!"
|
||||
msgstr ""
|
||||
msgstr "Det er foreløpig ikke implementert funksjon for å nullstille passord!"
|
||||
|
||||
#: .\cookbook\templates\account\signup.html:5
|
||||
msgid "Register"
|
||||
msgstr ""
|
||||
msgstr "Registrer"
|
||||
|
||||
#: .\cookbook\templates\account\signup.html:9
|
||||
msgid "Create your Account"
|
||||
msgstr "Opprett din konto"
|
||||
msgstr "Opprett konto"
|
||||
|
||||
#: .\cookbook\templates\account\signup.html:14
|
||||
msgid "Create User"
|
||||
@ -442,11 +449,11 @@ msgstr "API-dokumentasjon"
|
||||
|
||||
#: .\cookbook\templates\base.html:78
|
||||
msgid "Utensils"
|
||||
msgstr ""
|
||||
msgstr "Redskaper"
|
||||
|
||||
#: .\cookbook\templates\base.html:88
|
||||
msgid "Shopping"
|
||||
msgstr ""
|
||||
msgstr "Handle"
|
||||
|
||||
#: .\cookbook\templates\base.html:102 .\cookbook\views\delete.py:84
|
||||
#: .\cookbook\views\edit.py:93 .\cookbook\views\lists.py:26
|
||||
@ -456,27 +463,27 @@ msgstr "Nøkkelord"
|
||||
|
||||
#: .\cookbook\templates\base.html:104
|
||||
msgid "Batch Edit"
|
||||
msgstr ""
|
||||
msgstr "Oppdatere flere"
|
||||
|
||||
#: .\cookbook\templates\base.html:109
|
||||
msgid "Storage Data"
|
||||
msgstr ""
|
||||
msgstr "Datalagring"
|
||||
|
||||
#: .\cookbook\templates\base.html:113
|
||||
msgid "Storage Backends"
|
||||
msgstr ""
|
||||
msgstr "Lagringsplasser"
|
||||
|
||||
#: .\cookbook\templates\base.html:115
|
||||
msgid "Configure Sync"
|
||||
msgstr ""
|
||||
msgstr "Konfigurer synkronisering"
|
||||
|
||||
#: .\cookbook\templates\base.html:117
|
||||
msgid "Discovered Recipes"
|
||||
msgstr ""
|
||||
msgstr "Oppdagede oppskrifter"
|
||||
|
||||
#: .\cookbook\templates\base.html:119
|
||||
msgid "Discovery Log"
|
||||
msgstr ""
|
||||
msgstr "Logg Oppdagelser"
|
||||
|
||||
#: .\cookbook\templates\base.html:121 .\cookbook\templates\stats.html:10
|
||||
msgid "Statistics"
|
||||
@ -484,7 +491,7 @@ msgstr "Statistikk"
|
||||
|
||||
#: .\cookbook\templates\base.html:123
|
||||
msgid "Units & Ingredients"
|
||||
msgstr ""
|
||||
msgstr "Enheter & Ingredienser"
|
||||
|
||||
#: .\cookbook\templates\base.html:125
|
||||
msgid "Import Recipe"
|
||||
@ -521,58 +528,61 @@ msgid "API Browser"
|
||||
msgstr "API-utforsker"
|
||||
|
||||
#: .\cookbook\templates\base.html:165
|
||||
#, fuzzy
|
||||
msgid "Logout"
|
||||
msgstr "Logg ut"
|
||||
|
||||
#: .\cookbook\templates\batch\edit.html:6
|
||||
msgid "Batch edit Category"
|
||||
msgstr ""
|
||||
msgstr "Oppdater flere kategorier"
|
||||
|
||||
#: .\cookbook\templates\batch\edit.html:15
|
||||
msgid "Batch edit Recipes"
|
||||
msgstr ""
|
||||
msgstr "Oppdater flere oppskrifter"
|
||||
|
||||
#: .\cookbook\templates\batch\edit.html:20
|
||||
msgid "Add the specified keywords to all recipes containing a word"
|
||||
msgstr ""
|
||||
msgstr "Legg til spesifikt nøkkelord til alle oppskrifter som inneholder et ord"
|
||||
|
||||
#: .\cookbook\templates\batch\monitor.html:6 .\cookbook\views\edit.py:76
|
||||
msgid "Sync"
|
||||
msgstr ""
|
||||
msgstr "Synkronisering"
|
||||
|
||||
#: .\cookbook\templates\batch\monitor.html:10
|
||||
msgid "Manage watched Folders"
|
||||
msgstr ""
|
||||
msgstr "Behandle overvåkede mapper"
|
||||
|
||||
#: .\cookbook\templates\batch\monitor.html:14
|
||||
msgid ""
|
||||
"On this Page you can manage all storage folder locations that should be "
|
||||
"monitored and synced."
|
||||
msgstr ""
|
||||
"Her kan du behandle alle lagringsmapper og plasseringer for monitorering og "
|
||||
"synkronisering."
|
||||
|
||||
#: .\cookbook\templates\batch\monitor.html:16
|
||||
msgid "The path must be in the following format"
|
||||
msgstr ""
|
||||
msgstr "Stien må være i følgende format"
|
||||
|
||||
#: .\cookbook\templates\batch\monitor.html:27
|
||||
msgid "Sync Now!"
|
||||
msgstr ""
|
||||
msgstr "Synkroniser nå!"
|
||||
|
||||
#: .\cookbook\templates\batch\waiting.html:4
|
||||
#: .\cookbook\templates\batch\waiting.html:10
|
||||
msgid "Importing Recipes"
|
||||
msgstr ""
|
||||
msgstr "Importerer oppskrifter"
|
||||
|
||||
#: .\cookbook\templates\batch\waiting.html:23
|
||||
msgid ""
|
||||
"This can take a few minutes, depending on the number of recipes in sync, "
|
||||
"please wait."
|
||||
msgstr ""
|
||||
"Dette kan ta noen minutter, avhenging av antall oppskrifter som skal "
|
||||
"synkroniseres. Vennligst vent."
|
||||
|
||||
#: .\cookbook\templates\books.html:5 .\cookbook\templates\books.html:11
|
||||
msgid "Recipe Books"
|
||||
msgstr ""
|
||||
msgstr "Oppskriftsbøker"
|
||||
|
||||
#: .\cookbook\templates\books.html:15
|
||||
msgid "New Book"
|
||||
@ -584,32 +594,32 @@ msgstr "av"
|
||||
|
||||
#: .\cookbook\templates\books.html:34
|
||||
msgid "Toggle Recipes"
|
||||
msgstr ""
|
||||
msgstr "Veksle oppskrifter"
|
||||
|
||||
#: .\cookbook\templates\books.html:54
|
||||
#: .\cookbook\templates\meal_plan_entry.html:48
|
||||
#: .\cookbook\templates\recipes_table.html:64
|
||||
msgid "Last cooked"
|
||||
msgstr ""
|
||||
msgstr "Forrige tilbereding"
|
||||
|
||||
#: .\cookbook\templates\books.html:71
|
||||
msgid "There are no recipes in this book yet."
|
||||
msgstr ""
|
||||
msgstr "Det er foreløpig ingen oppskrifter i denne boken."
|
||||
|
||||
#: .\cookbook\templates\export.html:6 .\cookbook\templates\test2.html:6
|
||||
msgid "Export Recipes"
|
||||
msgstr ""
|
||||
msgstr "Eksporter oppskrifter"
|
||||
|
||||
#: .\cookbook\templates\export.html:14 .\cookbook\templates\export.html:20
|
||||
#: .\cookbook\templates\shopping_list.html:347
|
||||
#: .\cookbook\templates\test2.html:14 .\cookbook\templates\test2.html:20
|
||||
msgid "Export"
|
||||
msgstr ""
|
||||
msgstr "Eksporter"
|
||||
|
||||
#: .\cookbook\templates\forms\edit_import_recipe.html:5
|
||||
#: .\cookbook\templates\forms\edit_import_recipe.html:9
|
||||
msgid "Import new Recipe"
|
||||
msgstr ""
|
||||
msgstr "Importer ny oppskrift"
|
||||
|
||||
#: .\cookbook\templates\forms\edit_import_recipe.html:14
|
||||
#: .\cookbook\templates\forms\edit_internal_recipe.html:389
|
||||
@ -635,29 +645,29 @@ msgstr "Beskrivelse"
|
||||
|
||||
#: .\cookbook\templates\forms\edit_internal_recipe.html:72
|
||||
msgid "Waiting Time"
|
||||
msgstr ""
|
||||
msgstr "Ventetid"
|
||||
|
||||
#: .\cookbook\templates\forms\edit_internal_recipe.html:78
|
||||
msgid "Servings Text"
|
||||
msgstr ""
|
||||
msgstr "Porsjon beskrivelse"
|
||||
|
||||
#: .\cookbook\templates\forms\edit_internal_recipe.html:89
|
||||
msgid "Select Keywords"
|
||||
msgstr ""
|
||||
msgstr "Velg nøkkelord"
|
||||
|
||||
#: .\cookbook\templates\forms\edit_internal_recipe.html:90
|
||||
#: .\cookbook\templates\url_import.html:212
|
||||
msgid "Add Keyword"
|
||||
msgstr ""
|
||||
msgstr "Legg til nøkkelord"
|
||||
|
||||
#: .\cookbook\templates\forms\edit_internal_recipe.html:108
|
||||
msgid "Nutrition"
|
||||
msgstr ""
|
||||
msgstr "Næringsinnhold"
|
||||
|
||||
#: .\cookbook\templates\forms\edit_internal_recipe.html:112
|
||||
#: .\cookbook\templates\forms\edit_internal_recipe.html:162
|
||||
msgid "Delete Step"
|
||||
msgstr ""
|
||||
msgstr "Fjern trinn"
|
||||
|
||||
#: .\cookbook\templates\forms\edit_internal_recipe.html:116
|
||||
msgid "Calories"
|
||||
@ -678,15 +688,15 @@ msgstr "Proteiner"
|
||||
#: .\cookbook\templates\forms\edit_internal_recipe.html:146
|
||||
#: .\cookbook\templates\forms\edit_internal_recipe.html:454
|
||||
msgid "Step"
|
||||
msgstr ""
|
||||
msgstr "Trinn"
|
||||
|
||||
#: .\cookbook\templates\forms\edit_internal_recipe.html:167
|
||||
msgid "Show as header"
|
||||
msgstr ""
|
||||
msgstr "Vis som overskrift"
|
||||
|
||||
#: .\cookbook\templates\forms\edit_internal_recipe.html:173
|
||||
msgid "Hide as header"
|
||||
msgstr ""
|
||||
msgstr "Skjul overskrift"
|
||||
|
||||
#: .\cookbook\templates\forms\edit_internal_recipe.html:178
|
||||
msgid "Move Up"
|
||||
@ -698,15 +708,15 @@ msgstr "Flytt nedover"
|
||||
|
||||
#: .\cookbook\templates\forms\edit_internal_recipe.html:192
|
||||
msgid "Step Name"
|
||||
msgstr ""
|
||||
msgstr "Trinn navn"
|
||||
|
||||
#: .\cookbook\templates\forms\edit_internal_recipe.html:196
|
||||
msgid "Step Type"
|
||||
msgstr ""
|
||||
msgstr "Trinn type"
|
||||
|
||||
#: .\cookbook\templates\forms\edit_internal_recipe.html:207
|
||||
msgid "Step time in Minutes"
|
||||
msgstr ""
|
||||
msgstr "Trinn tid i minutter"
|
||||
|
||||
#: .\cookbook\templates\forms\edit_internal_recipe.html:261
|
||||
#: .\cookbook\templates\shopping_list.html:183
|
||||
@ -740,7 +750,7 @@ msgstr "Velg mat"
|
||||
#: .\cookbook\templates\meal_plan.html:256
|
||||
#: .\cookbook\templates\url_import.html:171
|
||||
msgid "Note"
|
||||
msgstr ""
|
||||
msgstr "Notis"
|
||||
|
||||
#: .\cookbook\templates\forms\edit_internal_recipe.html:319
|
||||
msgid "Delete Ingredient"
|
||||
@ -748,7 +758,7 @@ msgstr "Slett ingrediens"
|
||||
|
||||
#: .\cookbook\templates\forms\edit_internal_recipe.html:325
|
||||
msgid "Make Header"
|
||||
msgstr ""
|
||||
msgstr "Bruk som overskrift"
|
||||
|
||||
#: .\cookbook\templates\forms\edit_internal_recipe.html:331
|
||||
msgid "Make Ingredient"
|
||||
@ -756,15 +766,15 @@ msgstr "Opprett ingrediens"
|
||||
|
||||
#: .\cookbook\templates\forms\edit_internal_recipe.html:337
|
||||
msgid "Disable Amount"
|
||||
msgstr ""
|
||||
msgstr "Deaktiver mengde"
|
||||
|
||||
#: .\cookbook\templates\forms\edit_internal_recipe.html:343
|
||||
msgid "Enable Amount"
|
||||
msgstr ""
|
||||
msgstr "Aktiver mengde"
|
||||
|
||||
#: .\cookbook\templates\forms\edit_internal_recipe.html:348
|
||||
msgid "Copy Template Reference"
|
||||
msgstr ""
|
||||
msgstr "Kopier mal-referanse"
|
||||
|
||||
#: .\cookbook\templates\forms\edit_internal_recipe.html:374
|
||||
#: .\cookbook\templates\url_import.html:196
|
||||
@ -773,29 +783,28 @@ msgstr "Instruksjoner"
|
||||
|
||||
#: .\cookbook\templates\forms\edit_internal_recipe.html:387
|
||||
#: .\cookbook\templates\forms\edit_internal_recipe.html:418
|
||||
#, fuzzy
|
||||
msgid "Save & View"
|
||||
msgstr "Lagre og vis"
|
||||
|
||||
#: .\cookbook\templates\forms\edit_internal_recipe.html:391
|
||||
#: .\cookbook\templates\forms\edit_internal_recipe.html:424
|
||||
msgid "Add Step"
|
||||
msgstr ""
|
||||
msgstr "Legg til trinn"
|
||||
|
||||
#: .\cookbook\templates\forms\edit_internal_recipe.html:394
|
||||
#: .\cookbook\templates\forms\edit_internal_recipe.html:428
|
||||
msgid "Add Nutrition"
|
||||
msgstr ""
|
||||
msgstr "Legg til næringsinnhold"
|
||||
|
||||
#: .\cookbook\templates\forms\edit_internal_recipe.html:396
|
||||
#: .\cookbook\templates\forms\edit_internal_recipe.html:430
|
||||
msgid "Remove Nutrition"
|
||||
msgstr ""
|
||||
msgstr "Fjern næringsinnhold"
|
||||
|
||||
#: .\cookbook\templates\forms\edit_internal_recipe.html:398
|
||||
#: .\cookbook\templates\forms\edit_internal_recipe.html:433
|
||||
msgid "View Recipe"
|
||||
msgstr ""
|
||||
msgstr "Vis oppskrift"
|
||||
|
||||
#: .\cookbook\templates\forms\edit_internal_recipe.html:400
|
||||
#: .\cookbook\templates\forms\edit_internal_recipe.html:435
|
||||
@ -804,11 +813,11 @@ msgstr "Slett oppskrift"
|
||||
|
||||
#: .\cookbook\templates\forms\edit_internal_recipe.html:441
|
||||
msgid "Steps"
|
||||
msgstr ""
|
||||
msgstr "Trinn"
|
||||
|
||||
#: .\cookbook\templates\forms\ingredients.html:15
|
||||
msgid "Edit Ingredients"
|
||||
msgstr ""
|
||||
msgstr "Rediger ingrediens"
|
||||
|
||||
#: .\cookbook\templates\forms\ingredients.html:16
|
||||
msgid ""
|
||||
@ -820,54 +829,61 @@ msgid ""
|
||||
"them.\n"
|
||||
" "
|
||||
msgstr ""
|
||||
"\n"
|
||||
" Følgende skjema kan brukes dersom, tilfeldigvis, to eller flere "
|
||||
"enheter eller ingredienser er opprettet,\n"
|
||||
" og burde være identiske.\n"
|
||||
" Det slår sammen to enheter eller ingredienser og oppdaterer alle "
|
||||
"oppskrifter som inneholder disse.\n"
|
||||
" "
|
||||
|
||||
#: .\cookbook\templates\forms\ingredients.html:24
|
||||
#: .\cookbook\templates\stats.html:26
|
||||
msgid "Units"
|
||||
msgstr ""
|
||||
msgstr "Enheter"
|
||||
|
||||
#: .\cookbook\templates\forms\ingredients.html:26
|
||||
msgid "Are you sure that you want to merge these two units?"
|
||||
msgstr ""
|
||||
msgstr "Er du sikker på at du vil slå sammen disse enhetene?"
|
||||
|
||||
#: .\cookbook\templates\forms\ingredients.html:31
|
||||
#: .\cookbook\templates\forms\ingredients.html:40
|
||||
msgid "Merge"
|
||||
msgstr "Flett"
|
||||
msgstr "Slå sammen"
|
||||
|
||||
#: .\cookbook\templates\forms\ingredients.html:36
|
||||
msgid "Are you sure that you want to merge these two ingredients?"
|
||||
msgstr ""
|
||||
msgstr "Er du sikker på at du vil slå sammen disse ingrediensene?"
|
||||
|
||||
#: .\cookbook\templates\generic\delete_template.html:18
|
||||
#, python-format
|
||||
msgid "Are you sure you want to delete the %(title)s: <b>%(object)s</b> "
|
||||
msgstr ""
|
||||
msgstr "Er du sikker på at du vil slette %(title)s: <b>%(object)s</b> "
|
||||
|
||||
#: .\cookbook\templates\generic\delete_template.html:21
|
||||
msgid "Confirm"
|
||||
msgstr ""
|
||||
msgstr "Bekreft"
|
||||
|
||||
#: .\cookbook\templates\generic\edit_template.html:30
|
||||
msgid "View"
|
||||
msgstr ""
|
||||
msgstr "Vis"
|
||||
|
||||
#: .\cookbook\templates\generic\edit_template.html:34
|
||||
msgid "Delete original file"
|
||||
msgstr ""
|
||||
msgstr "Slett opprinnelig fil"
|
||||
|
||||
#: .\cookbook\templates\generic\list_template.html:6
|
||||
#: .\cookbook\templates\generic\list_template.html:12
|
||||
msgid "List"
|
||||
msgstr ""
|
||||
msgstr "Liste"
|
||||
|
||||
#: .\cookbook\templates\generic\list_template.html:25
|
||||
msgid "Filter"
|
||||
msgstr ""
|
||||
msgstr "Filtrer"
|
||||
|
||||
#: .\cookbook\templates\generic\list_template.html:30
|
||||
msgid "Import all"
|
||||
msgstr ""
|
||||
msgstr "Importer alle"
|
||||
|
||||
#: .\cookbook\templates\generic\new_template.html:6
|
||||
#: .\cookbook\templates\generic\new_template.html:14
|
||||
@ -891,19 +907,19 @@ msgstr "Vis logg"
|
||||
|
||||
#: .\cookbook\templates\history.html:24
|
||||
msgid "Cook Log"
|
||||
msgstr ""
|
||||
msgstr "Tilberedingslogg"
|
||||
|
||||
#: .\cookbook\templates\import.html:6 .\cookbook\templates\test.html:6
|
||||
msgid "Import Recipes"
|
||||
msgstr ""
|
||||
msgstr "Importer oppskrifter"
|
||||
|
||||
#: .\cookbook\templates\include\log_cooking.html:7
|
||||
msgid "Log Recipe Cooking"
|
||||
msgstr ""
|
||||
msgstr "Loggfør tilberedt oppskrift"
|
||||
|
||||
#: .\cookbook\templates\include\log_cooking.html:13
|
||||
msgid "All fields are optional and can be left empty."
|
||||
msgstr ""
|
||||
msgstr "Alle felt er valgfri og kan stå tomme."
|
||||
|
||||
#: .\cookbook\templates\include\log_cooking.html:19
|
||||
msgid "Rating"
|
||||
@ -943,44 +959,53 @@ msgid ""
|
||||
"can be used.\n"
|
||||
" "
|
||||
msgstr ""
|
||||
"\n"
|
||||
" <b>Passord og nøkkelfeltene</b> er lagret som <b>ren tekst</b> i "
|
||||
"databasen.\n"
|
||||
" Dette er nødvendig for å kunne utføre API-forespørsler, men det øker "
|
||||
"samtidig risiko for\n"
|
||||
" uønsket tilgang til dem.<br/>\n"
|
||||
" For å begrense kosekvensene av uønsket tilgang, kan nøkler eller "
|
||||
"kontoer med begrenset tilgang benyttes.\n"
|
||||
" "
|
||||
|
||||
#: .\cookbook\templates\index.html:29
|
||||
msgid "Search recipe ..."
|
||||
msgstr ""
|
||||
msgstr "Søk etter oppskrift..."
|
||||
|
||||
#: .\cookbook\templates\index.html:44
|
||||
msgid "New Recipe"
|
||||
msgstr ""
|
||||
msgstr "Ny oppskrift"
|
||||
|
||||
#: .\cookbook\templates\index.html:47
|
||||
msgid "Website Import"
|
||||
msgstr ""
|
||||
msgstr "Importer fra nettside"
|
||||
|
||||
#: .\cookbook\templates\index.html:53
|
||||
msgid "Advanced Search"
|
||||
msgstr ""
|
||||
msgstr "Avansert søk"
|
||||
|
||||
#: .\cookbook\templates\index.html:57
|
||||
msgid "Reset Search"
|
||||
msgstr ""
|
||||
msgstr "Nullstill søk"
|
||||
|
||||
#: .\cookbook\templates\index.html:85
|
||||
msgid "Last viewed"
|
||||
msgstr ""
|
||||
msgstr "Sist sett"
|
||||
|
||||
#: .\cookbook\templates\index.html:87 .\cookbook\templates\meal_plan.html:178
|
||||
#: .\cookbook\templates\stats.html:22
|
||||
msgid "Recipes"
|
||||
msgstr ""
|
||||
msgstr "Oppskrifter"
|
||||
|
||||
#: .\cookbook\templates\index.html:94
|
||||
msgid "Log in to view recipes"
|
||||
msgstr ""
|
||||
msgstr "Logg inn for å se oppskrifter"
|
||||
|
||||
#: .\cookbook\templates\markdown_info.html:5
|
||||
#: .\cookbook\templates\markdown_info.html:13
|
||||
msgid "Markdown Info"
|
||||
msgstr ""
|
||||
msgstr "Markdown informasjon"
|
||||
|
||||
#: .\cookbook\templates\markdown_info.html:14
|
||||
msgid ""
|
||||
@ -997,43 +1022,56 @@ msgid ""
|
||||
"below.\n"
|
||||
" "
|
||||
msgstr ""
|
||||
"\n"
|
||||
" Markdown er et lettvekts markup språk som benyttes for å formatere "
|
||||
"ren tekst.\n"
|
||||
" Denne siden bruker biblioteket <a href=\"https://python-markdown."
|
||||
"github.io/\" target=\"_blank\">Python Markdown</a> for\n"
|
||||
" å konvertere teksten din til velformatert HTML. Fullstendig "
|
||||
"dokumentasjon for markdown finner du\n"
|
||||
" <a href=\"https://daringfireball.net/projects/markdown/syntax\" "
|
||||
"target=\"_blank\">her</a>.\n"
|
||||
" En ufullstendig, men sannsynligvis tilstrekkelig dokumentasjon "
|
||||
"finner du under her.\n"
|
||||
" "
|
||||
|
||||
#: .\cookbook\templates\markdown_info.html:25
|
||||
msgid "Headers"
|
||||
msgstr ""
|
||||
msgstr "Overskrifter"
|
||||
|
||||
#: .\cookbook\templates\markdown_info.html:54
|
||||
msgid "Formatting"
|
||||
msgstr ""
|
||||
msgstr "Formatering"
|
||||
|
||||
#: .\cookbook\templates\markdown_info.html:56
|
||||
#: .\cookbook\templates\markdown_info.html:72
|
||||
msgid "Line breaks are inserted by adding two spaces after the end of a line"
|
||||
msgstr ""
|
||||
"Linjeskift er satt inn ved å sette inn to mellomrom på slutten av en linje"
|
||||
|
||||
#: .\cookbook\templates\markdown_info.html:57
|
||||
#: .\cookbook\templates\markdown_info.html:73
|
||||
msgid "or by leaving a blank line inbetween."
|
||||
msgstr ""
|
||||
msgstr "eller ved å sette inn en tom linje mellom."
|
||||
|
||||
#: .\cookbook\templates\markdown_info.html:59
|
||||
#: .\cookbook\templates\markdown_info.html:74
|
||||
msgid "This text is bold"
|
||||
msgstr ""
|
||||
msgstr "Denne teksten er Fet"
|
||||
|
||||
#: .\cookbook\templates\markdown_info.html:60
|
||||
#: .\cookbook\templates\markdown_info.html:75
|
||||
msgid "This text is italic"
|
||||
msgstr ""
|
||||
msgstr "Denne teksten er Kursiv"
|
||||
|
||||
#: .\cookbook\templates\markdown_info.html:61
|
||||
#: .\cookbook\templates\markdown_info.html:77
|
||||
msgid "Blockquotes are also possible"
|
||||
msgstr ""
|
||||
msgstr "Det er også mulig å sitere avsnitt"
|
||||
|
||||
#: .\cookbook\templates\markdown_info.html:84
|
||||
msgid "Lists"
|
||||
msgstr ""
|
||||
msgstr "Lister"
|
||||
|
||||
#: .\cookbook\templates\markdown_info.html:85
|
||||
msgid ""
|
||||
@ -1264,7 +1302,7 @@ msgstr ""
|
||||
#: .\cookbook\templates\no_groups_info.html:5
|
||||
#: .\cookbook\templates\no_groups_info.html:12
|
||||
msgid "No Permissions"
|
||||
msgstr "Ingen tilganger."
|
||||
msgstr "Ingen tilgang"
|
||||
|
||||
#: .\cookbook\templates\no_groups_info.html:17
|
||||
msgid "You do not have any groups and therefor cannot use this application."
|
||||
@ -1298,12 +1336,11 @@ msgstr ""
|
||||
|
||||
#: .\cookbook\templates\offline.html:6
|
||||
msgid "Offline"
|
||||
msgstr "Frakoblet."
|
||||
msgstr "Frakoblet"
|
||||
|
||||
#: .\cookbook\templates\offline.html:19
|
||||
#, fuzzy
|
||||
msgid "You are currently offline!"
|
||||
msgstr "Du er ikke tilkoblet Internett."
|
||||
msgstr "Du er ikke tilkoblet!"
|
||||
|
||||
#: .\cookbook\templates\offline.html:20
|
||||
msgid ""
|
||||
@ -1366,7 +1403,7 @@ msgstr "Stil"
|
||||
|
||||
#: .\cookbook\templates\settings.html:79
|
||||
msgid "API Token"
|
||||
msgstr "API-symbol"
|
||||
msgstr "API nøkkel"
|
||||
|
||||
#: .\cookbook\templates\settings.html:80
|
||||
msgid ""
|
||||
@ -1389,9 +1426,8 @@ msgid "Cookbook Setup"
|
||||
msgstr "Kokeboksoppsett"
|
||||
|
||||
#: .\cookbook\templates\setup.html:14
|
||||
#, fuzzy
|
||||
msgid "Setup"
|
||||
msgstr "Sett opp"
|
||||
msgstr "Installering"
|
||||
|
||||
#: .\cookbook\templates\setup.html:15
|
||||
msgid ""
|
||||
@ -1424,11 +1460,11 @@ msgstr "Mengde"
|
||||
|
||||
#: .\cookbook\templates\shopping_list.html:226
|
||||
msgid "Supermarket"
|
||||
msgstr "Matbutikk"
|
||||
msgstr "Butikk"
|
||||
|
||||
#: .\cookbook\templates\shopping_list.html:236
|
||||
msgid "Select Supermarket"
|
||||
msgstr "Velg matbutikk"
|
||||
msgstr "Velg butikk"
|
||||
|
||||
#: .\cookbook\templates\shopping_list.html:260
|
||||
msgid "Select User"
|
||||
@ -1540,7 +1576,6 @@ msgstr ""
|
||||
|
||||
#: .\cookbook\templates\system.html:49 .\cookbook\templates\system.html:64
|
||||
#: .\cookbook\templates\system.html:80 .\cookbook\templates\system.html:95
|
||||
#, fuzzy
|
||||
msgid "Ok"
|
||||
msgstr "OK"
|
||||
|
||||
|
Binary file not shown.
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@ -8,8 +8,8 @@ msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2022-02-11 08:52+0100\n"
|
||||
"PO-Revision-Date: 2023-02-18 10:55+0000\n"
|
||||
"Last-Translator: Joachim Weber <joachim.weber@gmx.de>\n"
|
||||
"PO-Revision-Date: 2023-04-12 11:55+0000\n"
|
||||
"Last-Translator: noxonad <noxonad@proton.me>\n"
|
||||
"Language-Team: Portuguese (Brazil) <http://translate.tandoor.dev/projects/"
|
||||
"tandoor/recipes-backend/pt_BR/>\n"
|
||||
"Language: pt_BR\n"
|
||||
@ -2208,7 +2208,7 @@ msgstr ""
|
||||
|
||||
#: .\cookbook\templates\url_import.html:38
|
||||
msgid "URL"
|
||||
msgstr ""
|
||||
msgstr "URL"
|
||||
|
||||
#: .\cookbook\templates\url_import.html:40
|
||||
msgid "App"
|
||||
|
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.
@ -8,17 +8,17 @@ msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2021-09-13 22:40+0200\n"
|
||||
"PO-Revision-Date: 2022-11-30 19:09+0000\n"
|
||||
"Last-Translator: Alex <kovsharoff@gmail.com>\n"
|
||||
"PO-Revision-Date: 2023-04-12 11:55+0000\n"
|
||||
"Last-Translator: noxonad <noxonad@proton.me>\n"
|
||||
"Language-Team: Russian <http://translate.tandoor.dev/projects/tandoor/"
|
||||
"recipes-backend/ru/>\n"
|
||||
"Language: ru\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && n"
|
||||
"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
|
||||
"X-Generator: Weblate 4.14.1\n"
|
||||
"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && "
|
||||
"n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
|
||||
"X-Generator: Weblate 4.15\n"
|
||||
|
||||
#: .\cookbook\filters.py:23 .\cookbook\templates\base.html:125
|
||||
#: .\cookbook\templates\forms\ingredients.html:34
|
||||
@ -861,7 +861,7 @@ msgstr ""
|
||||
|
||||
#: .\cookbook\templates\base.html:220
|
||||
msgid "GitHub"
|
||||
msgstr ""
|
||||
msgstr "GitHub"
|
||||
|
||||
#: .\cookbook\templates\base.html:224
|
||||
msgid "API Browser"
|
||||
@ -1937,7 +1937,7 @@ msgstr ""
|
||||
|
||||
#: .\cookbook\templates\space.html:106
|
||||
msgid "user"
|
||||
msgstr ""
|
||||
msgstr "пользователь"
|
||||
|
||||
#: .\cookbook\templates\space.html:107
|
||||
msgid "guest"
|
||||
|
Binary file not shown.
@ -8,17 +8,17 @@ msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2021-11-08 16:27+0100\n"
|
||||
"PO-Revision-Date: 2022-02-02 15:31+0000\n"
|
||||
"Last-Translator: Mario Dvorsek <mario.dvorsek@gmail.com>\n"
|
||||
"PO-Revision-Date: 2023-04-12 11:55+0000\n"
|
||||
"Last-Translator: noxonad <noxonad@proton.me>\n"
|
||||
"Language-Team: Slovenian <http://translate.tandoor.dev/projects/tandoor/"
|
||||
"recipes-backend/sl/>\n"
|
||||
"Language: sl\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=4; plural=n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || n"
|
||||
"%100==4 ? 2 : 3;\n"
|
||||
"X-Generator: Weblate 4.10.1\n"
|
||||
"Plural-Forms: nplurals=4; plural=n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || "
|
||||
"n%100==4 ? 2 : 3;\n"
|
||||
"X-Generator: Weblate 4.15\n"
|
||||
|
||||
#: .\cookbook\filters.py:23 .\cookbook\templates\base.html:125
|
||||
#: .\cookbook\templates\forms\ingredients.html:34
|
||||
@ -2107,7 +2107,7 @@ msgstr ""
|
||||
|
||||
#: .\cookbook\templates\url_import.html:36
|
||||
msgid "URL"
|
||||
msgstr ""
|
||||
msgstr "URL"
|
||||
|
||||
#: .\cookbook\templates\url_import.html:38
|
||||
msgid "App"
|
||||
|
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@ -8,8 +8,8 @@ msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2022-04-29 18:42+0200\n"
|
||||
"PO-Revision-Date: 2023-02-09 13:55+0000\n"
|
||||
"Last-Translator: vertilo <vertilo.dev@gmail.com>\n"
|
||||
"PO-Revision-Date: 2023-04-12 11:55+0000\n"
|
||||
"Last-Translator: noxonad <noxonad@proton.me>\n"
|
||||
"Language-Team: Ukrainian <http://translate.tandoor.dev/projects/tandoor/"
|
||||
"recipes-backend/uk/>\n"
|
||||
"Language: uk\n"
|
||||
@ -1091,7 +1091,7 @@ msgstr ""
|
||||
|
||||
#: .\cookbook\templates\base.html:311
|
||||
msgid "GitHub"
|
||||
msgstr ""
|
||||
msgstr "GitHub"
|
||||
|
||||
#: .\cookbook\templates\base.html:313
|
||||
msgid "Translate Tandoor"
|
||||
|
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@ -6,7 +6,7 @@ from django.urls import reverse
|
||||
from django_scopes import scope, scopes_disabled
|
||||
from pytest_factoryboy import LazyFixture, register
|
||||
|
||||
from cookbook.models import Food, FoodInheritField, Ingredient, ShoppingList, ShoppingListEntry
|
||||
from cookbook.models import Food, Ingredient, ShoppingListEntry
|
||||
from cookbook.tests.factories import (FoodFactory, IngredientFactory, ShoppingListEntryFactory,
|
||||
SupermarketCategoryFactory)
|
||||
|
||||
@ -56,23 +56,32 @@ def obj_tree_1(request, space_1):
|
||||
params = request.param # request.param is a magic variable
|
||||
except AttributeError:
|
||||
params = {}
|
||||
objs = []
|
||||
inherit = params.pop('inherit', False)
|
||||
objs.extend(FoodFactory.create_batch(3, space=space_1, **params))
|
||||
FoodFactory.create_batch(3, space=space_1, **params)
|
||||
objs = Food.objects.values_list('id', flat=True)
|
||||
obj_id = objs[1]
|
||||
child_id = objs[0]
|
||||
parent_id = objs[2]
|
||||
|
||||
# set all foods to inherit everything
|
||||
if inherit:
|
||||
inherit = Food.inheritable_fields
|
||||
Through = Food.objects.filter(space=space_1).first().inherit_fields.through
|
||||
Through = Food.objects.filter(
|
||||
space=space_1).first().inherit_fields.through
|
||||
for i in inherit:
|
||||
Through.objects.bulk_create([
|
||||
Through(food_id=x, foodinheritfield_id=i.id)
|
||||
for x in Food.objects.filter(space=space_1).values_list('id', flat=True)
|
||||
])
|
||||
|
||||
objs[0].move(objs[1], node_location)
|
||||
objs[1].move(objs[2], node_location)
|
||||
return Food.objects.get(id=objs[1].id) # whenever you move/merge a tree it's safest to re-get the object
|
||||
Food.objects.get(id=child_id).move(
|
||||
Food.objects.get(id=obj_id), node_location)
|
||||
|
||||
Food.objects.get(id=obj_id).move(
|
||||
Food.objects.get(id=parent_id), node_location)
|
||||
|
||||
# whenever you move/merge a tree it's safest to re-get the object
|
||||
return Food.objects.get(id=obj_id)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("arg", [
|
||||
@ -107,19 +116,23 @@ def test_list_filter(obj_1, obj_2, u1_s1):
|
||||
assert obj_2.name in [x['name'] for x in response['results']]
|
||||
assert response['results'][0]['name'] < response['results'][1]['name']
|
||||
|
||||
response = json.loads(u1_s1.get(f'{reverse(LIST_URL)}?page_size=1').content)
|
||||
response = json.loads(
|
||||
u1_s1.get(f'{reverse(LIST_URL)}?page_size=1').content)
|
||||
assert len(response['results']) == 1
|
||||
|
||||
response = json.loads(u1_s1.get(f'{reverse(LIST_URL)}?limit=1').content)
|
||||
assert len(response['results']) == 1
|
||||
|
||||
response = json.loads(u1_s1.get(f'{reverse(LIST_URL)}?query=''&limit=1').content)
|
||||
response = json.loads(
|
||||
u1_s1.get(f'{reverse(LIST_URL)}?query=''&limit=1').content)
|
||||
assert len(response['results']) == 1
|
||||
|
||||
response = json.loads(u1_s1.get(f'{reverse(LIST_URL)}?query=chicken').content)
|
||||
response = json.loads(
|
||||
u1_s1.get(f'{reverse(LIST_URL)}?query=chicken').content)
|
||||
assert response['count'] == 0
|
||||
|
||||
response = json.loads(u1_s1.get(f'{reverse(LIST_URL)}?query={obj_1.name[:-4]}').content)
|
||||
response = json.loads(
|
||||
u1_s1.get(f'{reverse(LIST_URL)}?query={obj_1.name[:-4]}').content)
|
||||
assert response['count'] == 1
|
||||
|
||||
|
||||
@ -262,8 +275,9 @@ def test_integrity(u1_s1, recipe_1_s1):
|
||||
|
||||
def test_move(u1_s1, obj_tree_1, obj_2, obj_3, space_1):
|
||||
with scope(space=space_1):
|
||||
# for some reason the 'path' attribute changes between the factory and the test when using both obj_tree and obj
|
||||
obj_tree_1 = Food.objects.get(id=obj_tree_1.id)
|
||||
parent = obj_tree_1.get_parent()
|
||||
child = obj_tree_1.get_descendants()[0]
|
||||
assert parent.get_num_children() == 1
|
||||
assert parent.get_descendant_count() == 2
|
||||
assert Food.get_root_nodes().filter(space=space_1).count() == 2
|
||||
@ -295,8 +309,9 @@ def test_move(u1_s1, obj_tree_1, obj_2, obj_3, space_1):
|
||||
|
||||
def test_move_errors(u1_s1, obj_tree_1, obj_3, space_1):
|
||||
with scope(space=space_1):
|
||||
# for some reason the 'path' attribute changes between the factory and the test when using both obj_tree and obj
|
||||
obj_tree_1 = Food.objects.get(id=obj_tree_1.id)
|
||||
parent = obj_tree_1.get_parent()
|
||||
child = obj_tree_1.get_descendants()[0]
|
||||
# move child to root
|
||||
r = u1_s1.put(reverse(MOVE_URL, args=[obj_tree_1.id, 0]))
|
||||
assert r.status_code == 200
|
||||
@ -373,6 +388,8 @@ def test_merge_shopping_entries(obj_tree_1, u1_s1, space_1):
|
||||
|
||||
def test_merge(u1_s1, obj_tree_1, obj_1, obj_3, space_1):
|
||||
with scope(space=space_1):
|
||||
# for some reason the 'path' attribute changes between the factory and the test when using both obj_tree and obj
|
||||
obj_tree_1 = Food.objects.get(id=obj_tree_1.id)
|
||||
parent = obj_tree_1.get_parent()
|
||||
child = obj_tree_1.get_descendants()[0]
|
||||
assert parent.get_num_children() == 1
|
||||
@ -416,8 +433,9 @@ def test_merge(u1_s1, obj_tree_1, obj_1, obj_3, space_1):
|
||||
|
||||
def test_merge_errors(u1_s1, obj_tree_1, obj_3, space_1):
|
||||
with scope(space=space_1):
|
||||
# for some reason the 'path' attribute changes between the factory and the test when using both obj_tree and obj
|
||||
obj_tree_1 = Food.objects.get(id=obj_tree_1.id)
|
||||
parent = obj_tree_1.get_parent()
|
||||
child = obj_tree_1.get_descendants()[0]
|
||||
|
||||
# attempt to merge with non-existent parent
|
||||
r = u1_s1.put(
|
||||
@ -451,44 +469,63 @@ def test_merge_errors(u1_s1, obj_tree_1, obj_3, space_1):
|
||||
|
||||
def test_root_filter(obj_tree_1, obj_2, obj_3, u1_s1):
|
||||
with scope(space=obj_tree_1.space):
|
||||
# for some reason the 'path' attribute changes between the factory and the test when using both obj_tree and obj
|
||||
obj_tree_1 = Food.objects.get(id=obj_tree_1.id)
|
||||
parent = obj_tree_1.get_parent()
|
||||
child = obj_tree_1.get_descendants()[0]
|
||||
|
||||
# should return root objects in the space (obj_1, obj_2), ignoring query filters
|
||||
response = json.loads(u1_s1.get(f'{reverse(LIST_URL)}?root=0').content)
|
||||
assert len(response['results']) == 2
|
||||
|
||||
# django_tree bypasses ORM - best to retrieve all changed objects
|
||||
with scopes_disabled():
|
||||
obj_2.move(parent, node_location)
|
||||
obj_2 = Food.objects.get(id=obj_2.id)
|
||||
parent = Food.objects.get(id=parent.id)
|
||||
# should return direct children of parent (obj_tree_1, obj_2), ignoring query filters
|
||||
response = json.loads(u1_s1.get(f'{reverse(LIST_URL)}?root={parent.id}').content)
|
||||
response = json.loads(
|
||||
u1_s1.get(f'{reverse(LIST_URL)}?root={parent.id}').content)
|
||||
assert response['count'] == 2
|
||||
response = json.loads(u1_s1.get(f'{reverse(LIST_URL)}?root={parent.id}&query={obj_2.name[4:]}').content)
|
||||
response = json.loads(u1_s1.get(
|
||||
f'{reverse(LIST_URL)}?root={parent.id}&query={obj_2.name[4:]}').content)
|
||||
assert response['count'] == 2
|
||||
|
||||
|
||||
def test_tree_filter(obj_tree_1, obj_2, obj_3, u1_s1):
|
||||
with scope(space=obj_tree_1.space):
|
||||
# for some reason the 'path' attribute changes between the factory and the test when using both obj_tree and obj
|
||||
parent = obj_tree_1.get_parent()
|
||||
child = obj_tree_1.get_descendants()[0]
|
||||
obj_2.move(parent, node_location)
|
||||
obj_2 = Food.objects.get(id=obj_2.id)
|
||||
obj_tree_1 = Food.objects.get(id=obj_tree_1.id)
|
||||
parent = Food.objects.get(id=parent.id)
|
||||
|
||||
# should return full tree starting at parent (obj_tree_1, obj_2), ignoring query filters
|
||||
response = json.loads(u1_s1.get(f'{reverse(LIST_URL)}?tree={parent.id}').content)
|
||||
response = json.loads(
|
||||
u1_s1.get(f'{reverse(LIST_URL)}?tree={parent.id}').content)
|
||||
assert response['count'] == 4
|
||||
response = json.loads(u1_s1.get(f'{reverse(LIST_URL)}?tree={parent.id}&query={obj_2.name[4:]}').content)
|
||||
response = json.loads(u1_s1.get(
|
||||
f'{reverse(LIST_URL)}?tree={parent.id}&query={obj_2.name[4:]}').content)
|
||||
assert response['count'] == 4
|
||||
|
||||
|
||||
# This is more about the model than the API - should this be moved to a different test?
|
||||
@pytest.mark.parametrize("obj_tree_1, field, inherit, new_val", [
|
||||
({'has_category': True, 'inherit': True}, 'supermarket_category', True, 'cat_1'),
|
||||
({'has_category': True, 'inherit': False}, 'supermarket_category', False, 'cat_1'),
|
||||
({'has_category': True, 'inherit': True},
|
||||
'supermarket_category', True, 'cat_1'),
|
||||
({'has_category': True, 'inherit': False},
|
||||
'supermarket_category', False, 'cat_1'),
|
||||
({'ignore_shopping': True, 'inherit': True}, 'ignore_shopping', True, 'false'),
|
||||
({'ignore_shopping': True, 'inherit': False}, 'ignore_shopping', False, 'false'),
|
||||
({'substitute_children': True, 'inherit': True}, 'substitute_children', True, 'false'),
|
||||
({'substitute_children': True, 'inherit': False}, 'substitute_children', False, 'false'),
|
||||
({'substitute_siblings': True, 'inherit': True}, 'substitute_siblings', True, 'false'),
|
||||
({'substitute_siblings': True, 'inherit': False}, 'substitute_siblings', False, 'false'),
|
||||
({'ignore_shopping': True, 'inherit': False},
|
||||
'ignore_shopping', False, 'false'),
|
||||
({'substitute_children': True, 'inherit': True},
|
||||
'substitute_children', True, 'false'),
|
||||
({'substitute_children': True, 'inherit': False},
|
||||
'substitute_children', False, 'false'),
|
||||
({'substitute_siblings': True, 'inherit': True},
|
||||
'substitute_siblings', True, 'false'),
|
||||
({'substitute_siblings': True, 'inherit': False},
|
||||
'substitute_siblings', False, 'false'),
|
||||
], indirect=['obj_tree_1']) # indirect=True populates magic variable request.param of obj_tree_1 with the parameter
|
||||
def test_inherit(request, obj_tree_1, field, inherit, new_val, u1_s1):
|
||||
with scope(space=obj_tree_1.space):
|
||||
@ -498,8 +535,10 @@ def test_inherit(request, obj_tree_1, field, inherit, new_val, u1_s1):
|
||||
new_val = request.getfixturevalue(new_val)
|
||||
# if this test passes it demonstrates that inheritance works
|
||||
# when moving to a parent as each food is created with a different category
|
||||
assert (getattr(parent, field) == getattr(obj_tree_1, field)) in [inherit, True]
|
||||
assert (getattr(obj_tree_1, field) == getattr(child, field)) in [inherit, True]
|
||||
assert (getattr(parent, field) == getattr(
|
||||
obj_tree_1, field)) in [inherit, True]
|
||||
assert (getattr(obj_tree_1, field) == getattr(
|
||||
child, field)) in [inherit, True]
|
||||
# change parent to a new value
|
||||
setattr(parent, field, new_val)
|
||||
with scope(space=parent.space):
|
||||
@ -515,7 +554,8 @@ def test_inherit(request, obj_tree_1, field, inherit, new_val, u1_s1):
|
||||
|
||||
|
||||
@pytest.mark.parametrize("obj_tree_1", [
|
||||
({'has_category': True, 'inherit': False, 'ignore_shopping': True, 'substitute_children': True, 'substitute_siblings': True}),
|
||||
({'has_category': True, 'inherit': False, 'ignore_shopping': True,
|
||||
'substitute_children': True, 'substitute_siblings': True}),
|
||||
], indirect=['obj_tree_1'])
|
||||
@pytest.mark.parametrize("global_reset", [True, False])
|
||||
@pytest.mark.parametrize("field", ['ignore_shopping', 'substitute_children', 'substitute_siblings', 'supermarket_category'])
|
||||
@ -534,10 +574,13 @@ def test_reset_inherit_space_fields(obj_tree_1, space_1, global_reset, field):
|
||||
assert getattr(parent, field) != getattr(obj_tree_1, field)
|
||||
|
||||
if global_reset:
|
||||
space_1.food_inherit.add(*Food.inheritable_fields.values_list('id', flat=True)) # set default inherit fields
|
||||
# set default inherit fields
|
||||
space_1.food_inherit.add(
|
||||
*Food.inheritable_fields.values_list('id', flat=True))
|
||||
parent.reset_inheritance(space=space_1)
|
||||
else:
|
||||
obj_tree_1.child_inherit_fields.set(Food.inheritable_fields.values_list('id', flat=True))
|
||||
obj_tree_1.child_inherit_fields.set(
|
||||
Food.inheritable_fields.values_list('id', flat=True))
|
||||
obj_tree_1.save()
|
||||
parent.reset_inheritance(space=space_1, food=obj_tree_1)
|
||||
# djangotree bypasses ORM and need to be retrieved again
|
||||
@ -545,12 +588,14 @@ def test_reset_inherit_space_fields(obj_tree_1, space_1, global_reset, field):
|
||||
parent = Food.objects.get(id=parent.id)
|
||||
child = Food.objects.get(id=child.id)
|
||||
|
||||
assert (getattr(parent, field) == getattr(obj_tree_1, field)) == global_reset
|
||||
assert (getattr(parent, field) == getattr(
|
||||
obj_tree_1, field)) == global_reset
|
||||
assert getattr(obj_tree_1, field) == getattr(child, field)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("obj_tree_1", [
|
||||
({'has_category': True, 'inherit': False, 'ignore_shopping': True, 'substitute_children': True, 'substitute_siblings': True}),
|
||||
({'has_category': True, 'inherit': False, 'ignore_shopping': True,
|
||||
'substitute_children': True, 'substitute_siblings': True}),
|
||||
], indirect=['obj_tree_1'])
|
||||
@pytest.mark.parametrize("field", ['ignore_shopping', 'substitute_children', 'substitute_siblings', 'supermarket_category'])
|
||||
def test_reset_inherit_no_food_instances(obj_tree_1, space_1, field):
|
||||
@ -558,13 +603,17 @@ def test_reset_inherit_no_food_instances(obj_tree_1, space_1, field):
|
||||
parent = obj_tree_1.get_parent()
|
||||
Food.objects.all().delete()
|
||||
|
||||
space_1.food_inherit.add(*Food.inheritable_fields.values_list('id', flat=True)) # set default inherit fields
|
||||
# set default inherit fields
|
||||
space_1.food_inherit.add(
|
||||
*Food.inheritable_fields.values_list('id', flat=True))
|
||||
parent.reset_inheritance(space=space_1)
|
||||
|
||||
|
||||
def test_onhand(obj_1, u1_s1, u2_s1):
|
||||
assert json.loads(u1_s1.get(reverse(DETAIL_URL, args={obj_1.id})).content)['food_onhand'] == False
|
||||
assert json.loads(u2_s1.get(reverse(DETAIL_URL, args={obj_1.id})).content)['food_onhand'] == False
|
||||
assert json.loads(u1_s1.get(reverse(DETAIL_URL, args={obj_1.id})).content)[
|
||||
'food_onhand'] == False
|
||||
assert json.loads(u2_s1.get(reverse(DETAIL_URL, args={obj_1.id})).content)[
|
||||
'food_onhand'] == False
|
||||
|
||||
u1_s1.patch(
|
||||
reverse(
|
||||
@ -574,10 +623,13 @@ def test_onhand(obj_1, u1_s1, u2_s1):
|
||||
{'food_onhand': True},
|
||||
content_type='application/json'
|
||||
)
|
||||
assert json.loads(u1_s1.get(reverse(DETAIL_URL, args={obj_1.id})).content)['food_onhand'] == True
|
||||
assert json.loads(u2_s1.get(reverse(DETAIL_URL, args={obj_1.id})).content)['food_onhand'] == False
|
||||
assert json.loads(u1_s1.get(reverse(DETAIL_URL, args={obj_1.id})).content)[
|
||||
'food_onhand'] == True
|
||||
assert json.loads(u2_s1.get(reverse(DETAIL_URL, args={obj_1.id})).content)[
|
||||
'food_onhand'] == False
|
||||
|
||||
user1 = auth.get_user(u1_s1)
|
||||
user2 = auth.get_user(u2_s1)
|
||||
user1.userpreference.shopping_share.add(user2)
|
||||
assert json.loads(u2_s1.get(reverse(DETAIL_URL, args={obj_1.id})).content)['food_onhand'] == True
|
||||
assert json.loads(u2_s1.get(reverse(DETAIL_URL, args={obj_1.id})).content)[
|
||||
'food_onhand'] == True
|
||||
|
@ -1,20 +1,14 @@
|
||||
import json
|
||||
from datetime import timedelta
|
||||
|
||||
import factory
|
||||
import pytest
|
||||
# work around for bug described here https://stackoverflow.com/a/70312265/15762829
|
||||
from django.conf import settings
|
||||
from django.contrib import auth
|
||||
from django.forms import model_to_dict
|
||||
from django.urls import reverse
|
||||
from django.utils import timezone
|
||||
from django_scopes import scope, scopes_disabled
|
||||
from pytest_factoryboy import LazyFixture, register
|
||||
from django_scopes import scopes_disabled
|
||||
|
||||
from cookbook.models import Food, Ingredient, ShoppingListEntry, Step
|
||||
from cookbook.tests.factories import (IngredientFactory, MealPlanFactory, RecipeFactory,
|
||||
StepFactory, UserFactory)
|
||||
from cookbook.models import Food, Ingredient
|
||||
from cookbook.tests.factories import MealPlanFactory, RecipeFactory, StepFactory, UserFactory
|
||||
|
||||
if settings.DATABASES['default']['ENGINE'] in ['django.db.backends.postgresql_psycopg2',
|
||||
'django.db.backends.postgresql']:
|
||||
@ -32,9 +26,12 @@ def user2(request, u1_s1):
|
||||
except AttributeError:
|
||||
params = {}
|
||||
user = auth.get_user(u1_s1)
|
||||
user.userpreference.mealplan_autoadd_shopping = params.get('mealplan_autoadd_shopping', True)
|
||||
user.userpreference.mealplan_autoinclude_related = params.get('mealplan_autoinclude_related', True)
|
||||
user.userpreference.mealplan_autoexclude_onhand = params.get('mealplan_autoexclude_onhand', True)
|
||||
user.userpreference.mealplan_autoadd_shopping = params.get(
|
||||
'mealplan_autoadd_shopping', True)
|
||||
user.userpreference.mealplan_autoinclude_related = params.get(
|
||||
'mealplan_autoinclude_related', True)
|
||||
user.userpreference.mealplan_autoexclude_onhand = params.get(
|
||||
'mealplan_autoexclude_onhand', True)
|
||||
user.userpreference.save()
|
||||
return u1_s1
|
||||
|
||||
@ -50,7 +47,6 @@ def recipe(request, space_1, u1_s1):
|
||||
return RecipeFactory(**params)
|
||||
|
||||
|
||||
|
||||
@pytest.mark.parametrize("arg", [
|
||||
['g1_s1', 204],
|
||||
['u1_s1', 204],
|
||||
@ -59,9 +55,12 @@ def recipe(request, space_1, u1_s1):
|
||||
])
|
||||
@pytest.mark.parametrize("recipe, sle_count", [
|
||||
({}, 10),
|
||||
({'steps__recipe_count': 1}, 20), # shopping list from recipe with StepRecipe
|
||||
({'steps__food_recipe_count': {'step': 0, 'count': 1}}, 19), # shopping list from recipe with food recipe
|
||||
({'steps__food_recipe_count': {'step': 0, 'count': 1}, 'steps__recipe_count': 1}, 29), # shopping list from recipe with StepRecipe and food recipe
|
||||
# shopping list from recipe with StepRecipe
|
||||
({'steps__recipe_count': 1}, 20),
|
||||
# shopping list from recipe with food recipe
|
||||
({'steps__food_recipe_count': {'step': 0, 'count': 1}}, 19),
|
||||
# shopping list from recipe with StepRecipe and food recipe
|
||||
({'steps__food_recipe_count': {'step': 0, 'count': 1}, 'steps__recipe_count': 1}, 29),
|
||||
], indirect=['recipe'])
|
||||
def test_shopping_recipe_method(request, arg, recipe, sle_count, u1_s1, u2_s1):
|
||||
c = request.getfixturevalue(arg[0])
|
||||
@ -78,16 +77,20 @@ def test_shopping_recipe_method(request, arg, recipe, sle_count, u1_s1, u2_s1):
|
||||
if r.status_code == 204: # skip anonymous user
|
||||
|
||||
r = json.loads(c.get(reverse(SHOPPING_LIST_URL)).content)
|
||||
assert len(r) == sle_count # recipe factory creates 10 ingredients by default
|
||||
# recipe factory creates 10 ingredients by default
|
||||
assert len(r) == sle_count
|
||||
assert [x['created_by']['id'] for x in r].count(user.id) == sle_count
|
||||
# user in space can't see shopping list
|
||||
assert len(json.loads(u2_s1.get(reverse(SHOPPING_LIST_URL)).content)) == 0
|
||||
assert len(json.loads(
|
||||
u2_s1.get(reverse(SHOPPING_LIST_URL)).content)) == 0
|
||||
user.userpreference.shopping_share.add(auth.get_user(u2_s1))
|
||||
# after share, user in space can see shopping list
|
||||
assert len(json.loads(u2_s1.get(reverse(SHOPPING_LIST_URL)).content)) == sle_count
|
||||
assert len(json.loads(
|
||||
u2_s1.get(reverse(SHOPPING_LIST_URL)).content)) == sle_count
|
||||
# confirm that the author of the recipe doesn't have access to shopping list
|
||||
if c != u1_s1:
|
||||
assert len(json.loads(u1_s1.get(reverse(SHOPPING_LIST_URL)).content)) == 0
|
||||
assert len(json.loads(
|
||||
u1_s1.get(reverse(SHOPPING_LIST_URL)).content)) == 0
|
||||
|
||||
r = c.get(url)
|
||||
assert r.status_code == 405
|
||||
@ -99,9 +102,12 @@ def test_shopping_recipe_method(request, arg, recipe, sle_count, u1_s1, u2_s1):
|
||||
|
||||
@pytest.mark.parametrize("recipe, sle_count", [
|
||||
({}, 10),
|
||||
({'steps__recipe_count': 1}, 20), # shopping list from recipe with StepRecipe
|
||||
({'steps__food_recipe_count': {'step': 0, 'count': 1}}, 19), # shopping list from recipe with food recipe
|
||||
({'steps__food_recipe_count': {'step': 0, 'count': 1}, 'steps__recipe_count': 1}, 29), # shopping list from recipe with StepRecipe and food recipe
|
||||
# shopping list from recipe with StepRecipe
|
||||
({'steps__recipe_count': 1}, 20),
|
||||
# shopping list from recipe with food recipe
|
||||
({'steps__food_recipe_count': {'step': 0, 'count': 1}}, 19),
|
||||
# shopping list from recipe with StepRecipe and food recipe
|
||||
({'steps__food_recipe_count': {'step': 0, 'count': 1}, 'steps__recipe_count': 1}, 29),
|
||||
], indirect=['recipe'])
|
||||
@pytest.mark.parametrize("use_mealplan", [(False), (True), ])
|
||||
def test_shopping_recipe_edit(request, recipe, sle_count, use_mealplan, u1_s1, u2_s1):
|
||||
@ -115,31 +121,33 @@ def test_shopping_recipe_edit(request, recipe, sle_count, use_mealplan, u1_s1, u
|
||||
user.userpreference.save()
|
||||
|
||||
if use_mealplan:
|
||||
mealplan = MealPlanFactory(space=recipe.space, created_by=user, servings=recipe.servings, recipe=recipe)
|
||||
mealplan = MealPlanFactory(
|
||||
space=recipe.space, created_by=user, servings=recipe.servings, recipe=recipe)
|
||||
else:
|
||||
u1_s1.put(reverse(SHOPPING_RECIPE_URL, args={recipe.id}))
|
||||
r = json.loads(u1_s1.get(reverse(SHOPPING_LIST_URL)).content)
|
||||
assert [x['created_by']['id'] for x in r].count(user.id) == sle_count
|
||||
all_ing = [x['ingredient'] for x in r]
|
||||
keep_ing = all_ing[1:-1] # remove first and last element
|
||||
del keep_ing[int(len(keep_ing)/2)] # remove a middle element
|
||||
del keep_ing[int(len(keep_ing) / 2)] # remove a middle element
|
||||
list_recipe = r[0]['list_recipe']
|
||||
amount_sum = sum([x['amount'] for x in r])
|
||||
|
||||
# test modifying shopping list as different user
|
||||
# test increasing servings size of recipe shopping list
|
||||
if use_mealplan:
|
||||
mealplan.servings = 2*recipe.servings
|
||||
mealplan.servings = 2 * recipe.servings
|
||||
mealplan.save()
|
||||
else:
|
||||
u2_s1.put(reverse(SHOPPING_RECIPE_URL, args={recipe.id}),
|
||||
{'list_recipe': list_recipe, 'servings': 2*recipe.servings},
|
||||
{'list_recipe': list_recipe, 'servings': 2 * recipe.servings},
|
||||
content_type='application/json'
|
||||
)
|
||||
r = json.loads(u1_s1.get(reverse(SHOPPING_LIST_URL)).content)
|
||||
assert sum([x['amount'] for x in r]) == amount_sum * 2
|
||||
assert len(r) == sle_count
|
||||
assert len(json.loads(u2_s1.get(reverse(SHOPPING_LIST_URL)).content)) == sle_count
|
||||
assert len(json.loads(
|
||||
u2_s1.get(reverse(SHOPPING_LIST_URL)).content)) == sle_count
|
||||
|
||||
# testing decreasing servings size of recipe shopping list
|
||||
if use_mealplan:
|
||||
@ -153,7 +161,8 @@ def test_shopping_recipe_edit(request, recipe, sle_count, use_mealplan, u1_s1, u
|
||||
r = json.loads(u1_s1.get(reverse(SHOPPING_LIST_URL)).content)
|
||||
assert sum([x['amount'] for x in r]) == amount_sum * .5
|
||||
assert len(r) == sle_count
|
||||
assert len(json.loads(u2_s1.get(reverse(SHOPPING_LIST_URL)).content)) == sle_count
|
||||
assert len(json.loads(
|
||||
u2_s1.get(reverse(SHOPPING_LIST_URL)).content)) == sle_count
|
||||
|
||||
# test removing 3 items from shopping list
|
||||
u2_s1.put(reverse(SHOPPING_RECIPE_URL, args={recipe.id}),
|
||||
@ -162,7 +171,8 @@ def test_shopping_recipe_edit(request, recipe, sle_count, use_mealplan, u1_s1, u
|
||||
)
|
||||
r = json.loads(u1_s1.get(reverse(SHOPPING_LIST_URL)).content)
|
||||
assert len(r) == sle_count - 3
|
||||
assert len(json.loads(u2_s1.get(reverse(SHOPPING_LIST_URL)).content)) == sle_count - 3
|
||||
assert len(json.loads(
|
||||
u2_s1.get(reverse(SHOPPING_LIST_URL)).content)) == sle_count - 3
|
||||
|
||||
# add all ingredients to existing shopping list - don't change serving size
|
||||
u2_s1.put(reverse(SHOPPING_RECIPE_URL, args={recipe.id}),
|
||||
@ -172,14 +182,16 @@ def test_shopping_recipe_edit(request, recipe, sle_count, use_mealplan, u1_s1, u
|
||||
r = json.loads(u1_s1.get(reverse(SHOPPING_LIST_URL)).content)
|
||||
assert sum([x['amount'] for x in r]) == amount_sum * .5
|
||||
assert len(r) == sle_count
|
||||
assert len(json.loads(u2_s1.get(reverse(SHOPPING_LIST_URL)).content)) == sle_count
|
||||
assert len(json.loads(
|
||||
u2_s1.get(reverse(SHOPPING_LIST_URL)).content)) == sle_count
|
||||
|
||||
|
||||
@pytest.mark.parametrize("user2, sle_count", [
|
||||
({'mealplan_autoadd_shopping': False}, (0, 18)),
|
||||
({'mealplan_autoinclude_related': False}, (9, 9)),
|
||||
({'mealplan_autoexclude_onhand': False}, (20, 20)),
|
||||
({'mealplan_autoexclude_onhand': False, 'mealplan_autoinclude_related': False}, (10, 10)),
|
||||
({'mealplan_autoexclude_onhand': False,
|
||||
'mealplan_autoinclude_related': False}, (10, 10)),
|
||||
], indirect=['user2'])
|
||||
@pytest.mark.parametrize("use_mealplan", [(False), (True), ])
|
||||
@pytest.mark.parametrize("recipe", [({'steps__recipe_count': 1})], indirect=['recipe'])
|
||||
@ -191,20 +203,24 @@ def test_shopping_recipe_userpreference(recipe, sle_count, use_mealplan, user2):
|
||||
food = Food.objects.get(id=ingredients[2].food.id)
|
||||
food.onhand_users.add(user)
|
||||
food.save()
|
||||
food = recipe.steps.exclude(step_recipe=None).first().step_recipe.steps.first().ingredients.first().food
|
||||
food = recipe.steps.exclude(step_recipe=None).first(
|
||||
).step_recipe.steps.first().ingredients.first().food
|
||||
food = Food.objects.get(id=food.id)
|
||||
food.onhand_users.add(user)
|
||||
food.save()
|
||||
|
||||
if use_mealplan:
|
||||
mealplan = MealPlanFactory(space=recipe.space, created_by=user, servings=recipe.servings, recipe=recipe)
|
||||
assert len(json.loads(user2.get(reverse(SHOPPING_LIST_URL)).content)) == sle_count[0]
|
||||
MealPlanFactory(
|
||||
space=recipe.space, created_by=user, servings=recipe.servings, recipe=recipe)
|
||||
assert len(json.loads(
|
||||
user2.get(reverse(SHOPPING_LIST_URL)).content)) == sle_count[0]
|
||||
else:
|
||||
user2.put(reverse(SHOPPING_RECIPE_URL, args={recipe.id}))
|
||||
assert len(json.loads(user2.get(reverse(SHOPPING_LIST_URL)).content)) == sle_count[1]
|
||||
assert len(json.loads(
|
||||
user2.get(reverse(SHOPPING_LIST_URL)).content)) == sle_count[1]
|
||||
|
||||
|
||||
def test_shopping_recipe_mixed_authors(u1_s1, u2_s1,space_1):
|
||||
def test_shopping_recipe_mixed_authors(u1_s1, u2_s1, space_1):
|
||||
with scopes_disabled():
|
||||
user1 = auth.get_user(u1_s1)
|
||||
user2 = auth.get_user(u2_s1)
|
||||
@ -213,15 +229,19 @@ def test_shopping_recipe_mixed_authors(u1_s1, u2_s1,space_1):
|
||||
recipe1 = RecipeFactory(created_by=user1, space=space)
|
||||
recipe2 = RecipeFactory(created_by=user2, space=space)
|
||||
recipe3 = RecipeFactory(created_by=user3, space=space)
|
||||
food = Food.objects.get(id=recipe1.steps.first().ingredients.first().food.id)
|
||||
food = Food.objects.get(
|
||||
id=recipe1.steps.first().ingredients.first().food.id)
|
||||
food.recipe = recipe2
|
||||
food.save()
|
||||
recipe1.steps.add(StepFactory(step_recipe=recipe3, ingredients__count=0, space=space))
|
||||
recipe1.steps.add(StepFactory(step_recipe=recipe3,
|
||||
ingredients__count=0, space=space))
|
||||
recipe1.save()
|
||||
|
||||
u1_s1.put(reverse(SHOPPING_RECIPE_URL, args={recipe1.id}))
|
||||
assert len(json.loads(u1_s1.get(reverse(SHOPPING_LIST_URL)).content)) == 29
|
||||
assert len(json.loads(u2_s1.get(reverse(SHOPPING_LIST_URL)).content)) == 0
|
||||
assert len(json.loads(
|
||||
u1_s1.get(reverse(SHOPPING_LIST_URL)).content)) == 29
|
||||
assert len(json.loads(
|
||||
u2_s1.get(reverse(SHOPPING_LIST_URL)).content)) == 0
|
||||
|
||||
|
||||
@pytest.mark.parametrize("recipe", [{'steps__ingredients__header': 1}], indirect=['recipe'])
|
||||
@ -230,4 +250,5 @@ def test_shopping_with_header_ingredient(u1_s1, recipe):
|
||||
# recipe.step_set.first().ingredient_set.add(IngredientFactory(ingredients__header=1))
|
||||
u1_s1.put(reverse(SHOPPING_RECIPE_URL, args={recipe.id}))
|
||||
assert len(json.loads(u1_s1.get(reverse(SHOPPING_LIST_URL)).content)) == 10
|
||||
assert len(json.loads(u1_s1.get(reverse('api:ingredient-list')).content)['results']) == 11
|
||||
assert len(json.loads(
|
||||
u1_s1.get(reverse('api:ingredient-list')).content)['results']) == 11
|
||||
|
@ -5,12 +5,11 @@ import uuid
|
||||
|
||||
import pytest
|
||||
from django.contrib import auth
|
||||
from django.contrib.auth.models import Group, User
|
||||
from django_scopes import scopes_disabled
|
||||
from pytest_factoryboy import LazyFixture, register
|
||||
from pytest_factoryboy import register
|
||||
|
||||
from cookbook.models import Food, Ingredient, Recipe, Space, Step, Unit
|
||||
from cookbook.tests.factories import FoodFactory, SpaceFactory, UserFactory
|
||||
from cookbook.models import Food, Ingredient, Recipe, Step, Unit
|
||||
from cookbook.tests.factories import SpaceFactory, UserFactory
|
||||
|
||||
register(SpaceFactory, 'space_1')
|
||||
register(SpaceFactory, 'space_2')
|
||||
@ -60,8 +59,10 @@ def get_random_recipe(space_1, u1_s1):
|
||||
internal=True,
|
||||
)
|
||||
|
||||
s1 = Step.objects.create(name=str(uuid.uuid4()), instruction=str(uuid.uuid4()), space=space_1, )
|
||||
s2 = Step.objects.create(name=str(uuid.uuid4()), instruction=str(uuid.uuid4()), space=space_1, )
|
||||
s1 = Step.objects.create(name=str(uuid.uuid4()),
|
||||
instruction=str(uuid.uuid4()), space=space_1, )
|
||||
s2 = Step.objects.create(name=str(uuid.uuid4()),
|
||||
instruction=str(uuid.uuid4()), space=space_1, )
|
||||
|
||||
r.steps.add(s1)
|
||||
r.steps.add(s2)
|
||||
@ -70,8 +71,10 @@ def get_random_recipe(space_1, u1_s1):
|
||||
s1.ingredients.add(
|
||||
Ingredient.objects.create(
|
||||
amount=1,
|
||||
food=Food.objects.get_or_create(name=str(uuid.uuid4()), space=space_1)[0],
|
||||
unit=Unit.objects.create(name=str(uuid.uuid4()), space=space_1, ),
|
||||
food=Food.objects.get_or_create(
|
||||
name=str(uuid.uuid4()), space=space_1)[0],
|
||||
unit=Unit.objects.create(
|
||||
name=str(uuid.uuid4()), space=space_1, ),
|
||||
note=str(uuid.uuid4()),
|
||||
space=space_1,
|
||||
)
|
||||
@ -80,8 +83,10 @@ def get_random_recipe(space_1, u1_s1):
|
||||
s2.ingredients.add(
|
||||
Ingredient.objects.create(
|
||||
amount=1,
|
||||
food=Food.objects.get_or_create(name=str(uuid.uuid4()), space=space_1)[0],
|
||||
unit=Unit.objects.create(name=str(uuid.uuid4()), space=space_1, ),
|
||||
food=Food.objects.get_or_create(
|
||||
name=str(uuid.uuid4()), space=space_1)[0],
|
||||
unit=Unit.objects.create(
|
||||
name=str(uuid.uuid4()), space=space_1, ),
|
||||
note=str(uuid.uuid4()),
|
||||
space=space_1,
|
||||
)
|
||||
@ -99,8 +104,10 @@ def get_random_json_recipe():
|
||||
{
|
||||
"instruction": str(uuid.uuid4()),
|
||||
"ingredients": [
|
||||
{"food": {"name": str(uuid.uuid4())}, "unit": {"name": str(uuid.uuid4())}, "amount": random.randint(0, 10)},
|
||||
{"food": {"name": str(uuid.uuid4())}, "unit": {"name": str(uuid.uuid4())}, "amount": random.randint(0, 10)},
|
||||
{"food": {"name": str(uuid.uuid4())}, "unit": {"name": str(
|
||||
uuid.uuid4())}, "amount": random.randint(0, 10)},
|
||||
{"food": {"name": str(uuid.uuid4())}, "unit": {"name": str(
|
||||
uuid.uuid4())}, "amount": random.randint(0, 10)},
|
||||
],
|
||||
}
|
||||
],
|
||||
@ -133,7 +140,8 @@ def validate_recipe(expected, recipe):
|
||||
for key in expected_lists:
|
||||
for k in expected_lists[key]:
|
||||
try:
|
||||
print('comparing ', any([dict_compare(k, i) for i in target_lists[key]]))
|
||||
print('comparing ', any([dict_compare(k, i)
|
||||
for i in target_lists[key]]))
|
||||
assert any([dict_compare(k, i) for i in target_lists[key]])
|
||||
except AssertionError:
|
||||
for result in [dict_compare(k, i, details=True) for i in target_lists[key]]:
|
||||
@ -152,7 +160,8 @@ def dict_compare(d1, d2, details=False):
|
||||
added = d1_keys - d2_keys
|
||||
removed = d2_keys - d1_keys
|
||||
modified = {o: (d1[o], d2[o]) for o in not_dicts if d1[o] != d2[o]}
|
||||
modified_dicts = {o: (d1[o], d2[o]) for o in sub_dicts if not d1[o].items() <= d2[o].items()}
|
||||
modified_dicts = {o: (d1[o], d2[o])
|
||||
for o in sub_dicts if not d1[o].items() <= d2[o].items()}
|
||||
if details:
|
||||
return added, removed, modified, modified_dicts
|
||||
else:
|
||||
@ -173,12 +182,12 @@ def transpose(text, number=2):
|
||||
positions = random.sample(range(len(tokens[token_pos])), number)
|
||||
|
||||
# swap the positions
|
||||
l = list(tokens[token_pos])
|
||||
lt = list(tokens[token_pos])
|
||||
for first, second in zip(positions[::2], positions[1::2]):
|
||||
l[first], l[second] = l[second], l[first]
|
||||
lt[first], lt[second] = lt[second], lt[first]
|
||||
|
||||
# replace original tokens with swapped
|
||||
tokens[token_pos] = ''.join(l)
|
||||
tokens[token_pos] = ''.join(lt)
|
||||
|
||||
# return text with the swapped token
|
||||
return ' '.join(tokens)
|
||||
|
@ -4,13 +4,12 @@ from decimal import Decimal
|
||||
|
||||
import factory
|
||||
import pytest
|
||||
from django.contrib import auth
|
||||
from django.contrib.auth.models import Group, User
|
||||
from django_scopes import scopes_disabled
|
||||
from faker import Factory as FakerFactory
|
||||
from pytest_factoryboy import register
|
||||
|
||||
from cookbook.models import Recipe, Step, UserSpace
|
||||
from cookbook.models import UserSpace
|
||||
|
||||
# this code will run immediately prior to creating the model object useful when you want a reverse relationship
|
||||
# log = factory.RelatedFactory(
|
||||
@ -53,7 +52,8 @@ class SpaceFactory(factory.django.DjangoModelFactory):
|
||||
class UserFactory(factory.django.DjangoModelFactory):
|
||||
|
||||
"""User factory."""
|
||||
username = factory.LazyAttribute(lambda x: faker.simple_profile()['username'])
|
||||
username = factory.LazyAttribute(
|
||||
lambda x: faker.simple_profile()['username'])
|
||||
first_name = factory.LazyAttribute(lambda x: faker.first_name())
|
||||
last_name = factory.LazyAttribute(lambda x: faker.last_name())
|
||||
email = factory.LazyAttribute(lambda x: faker.email())
|
||||
@ -65,7 +65,8 @@ class UserFactory(factory.django.DjangoModelFactory):
|
||||
return
|
||||
|
||||
if extracted:
|
||||
us = UserSpace.objects.create(space=self.space, user=self, active=True)
|
||||
us = UserSpace.objects.create(
|
||||
space=self.space, user=self, active=True)
|
||||
us.groups.add(Group.objects.get(name=extracted))
|
||||
|
||||
@factory.post_generation
|
||||
@ -75,10 +76,12 @@ class UserFactory(factory.django.DjangoModelFactory):
|
||||
|
||||
if extracted:
|
||||
for prefs in extracted:
|
||||
self.userpreference[prefs] = extracted[prefs]/0 # intentionally break so it can be debugged later
|
||||
# intentionally break so it can be debugged later
|
||||
self.userpreference[prefs] = extracted[prefs] / 0
|
||||
|
||||
class Meta:
|
||||
model = User
|
||||
django_get_or_create = ('username', 'space',)
|
||||
|
||||
|
||||
@register
|
||||
@ -98,18 +101,22 @@ class SupermarketCategoryFactory(factory.django.DjangoModelFactory):
|
||||
class FoodFactory(factory.django.DjangoModelFactory):
|
||||
"""Food factory."""
|
||||
name = factory.LazyAttribute(lambda x: faker.sentence(nb_words=10)[:128])
|
||||
plural_name = factory.LazyAttribute(lambda x: faker.sentence(nb_words=3, variable_nb_words=False))
|
||||
plural_name = factory.LazyAttribute(
|
||||
lambda x: faker.sentence(nb_words=3, variable_nb_words=False))
|
||||
description = factory.LazyAttribute(lambda x: faker.sentence(nb_words=10))
|
||||
supermarket_category = factory.Maybe(
|
||||
factory.LazyAttribute(lambda x: x.has_category),
|
||||
yes_declaration=factory.SubFactory(SupermarketCategoryFactory, space=factory.SelfAttribute('..space')),
|
||||
yes_declaration=factory.SubFactory(
|
||||
SupermarketCategoryFactory, space=factory.SelfAttribute('..space')),
|
||||
no_declaration=None
|
||||
)
|
||||
recipe = factory.Maybe(
|
||||
factory.LazyAttribute(lambda x: x.has_recipe),
|
||||
yes_declaration=factory.SubFactory('cookbook.tests.factories.RecipeFactory', space=factory.SelfAttribute('..space')),
|
||||
yes_declaration=factory.SubFactory(
|
||||
'cookbook.tests.factories.RecipeFactory', space=factory.SelfAttribute('..space')),
|
||||
no_declaration=None
|
||||
)
|
||||
path = None
|
||||
space = factory.SubFactory(SpaceFactory)
|
||||
|
||||
@factory.post_generation
|
||||
@ -127,17 +134,19 @@ class FoodFactory(factory.django.DjangoModelFactory):
|
||||
|
||||
class Meta:
|
||||
model = 'cookbook.Food'
|
||||
django_get_or_create = ('name', 'plural_name', 'space',)
|
||||
django_get_or_create = ('name', 'plural_name', 'path', 'space',)
|
||||
|
||||
|
||||
@register
|
||||
class RecipeBookFactory(factory.django.DjangoModelFactory):
|
||||
"""RecipeBook factory."""
|
||||
name = factory.LazyAttribute(lambda x: faker.sentence(nb_words=3, variable_nb_words=False))
|
||||
name = factory.LazyAttribute(lambda x: faker.sentence(
|
||||
nb_words=3, variable_nb_words=False))
|
||||
description = factory.LazyAttribute(lambda x: faker.sentence(nb_words=10))
|
||||
icon = None
|
||||
# shared = factory.SubFactory(UserFactory, space=factory.SelfAttribute('..space'))
|
||||
created_by = factory.SubFactory(UserFactory, space=factory.SelfAttribute('..space'))
|
||||
created_by = factory.SubFactory(
|
||||
UserFactory, space=factory.SelfAttribute('..space'))
|
||||
filter = None
|
||||
space = factory.SubFactory(SpaceFactory)
|
||||
|
||||
@ -149,7 +158,8 @@ class RecipeBookFactory(factory.django.DjangoModelFactory):
|
||||
@register
|
||||
class RecipeBookEntryFactory(factory.django.DjangoModelFactory):
|
||||
"""RecipeBookEntry factory."""
|
||||
book = factory.SubFactory(RecipeBookFactory, space=factory.SelfAttribute('..recipe.space'))
|
||||
book = factory.SubFactory(
|
||||
RecipeBookFactory, space=factory.SelfAttribute('..recipe.space'))
|
||||
recipe = None
|
||||
|
||||
class Meta:
|
||||
@ -173,7 +183,8 @@ class UnitFactory(factory.django.DjangoModelFactory):
|
||||
@register
|
||||
class KeywordFactory(factory.django.DjangoModelFactory):
|
||||
"""Keyword factory."""
|
||||
name = factory.LazyAttribute(lambda x: faker.sentence(nb_words=2, variable_nb_words=False))
|
||||
name = factory.LazyAttribute(lambda x: faker.sentence(
|
||||
nb_words=2, variable_nb_words=False))
|
||||
# icon = models.CharField(max_length=16, blank=True, null=True)
|
||||
description = factory.LazyAttribute(lambda x: faker.sentence(nb_words=10))
|
||||
space = factory.SubFactory(SpaceFactory)
|
||||
@ -184,15 +195,17 @@ class KeywordFactory(factory.django.DjangoModelFactory):
|
||||
|
||||
class Meta:
|
||||
model = 'cookbook.Keyword'
|
||||
django_get_or_create = ('name', 'space',)
|
||||
django_get_or_create = ('name', 'space')
|
||||
exclude = ('num')
|
||||
|
||||
|
||||
@register
|
||||
class IngredientFactory(factory.django.DjangoModelFactory):
|
||||
"""Ingredient factory."""
|
||||
food = factory.SubFactory(FoodFactory, space=factory.SelfAttribute('..space'))
|
||||
unit = factory.SubFactory(UnitFactory, space=factory.SelfAttribute('..space'))
|
||||
food = factory.SubFactory(
|
||||
FoodFactory, space=factory.SelfAttribute('..space'))
|
||||
unit = factory.SubFactory(
|
||||
UnitFactory, space=factory.SelfAttribute('..space'))
|
||||
amount = factory.LazyAttribute(lambda x: faker.random_int(min=1, max=10))
|
||||
note = factory.LazyAttribute(lambda x: faker.sentence(nb_words=8))
|
||||
is_header = False
|
||||
@ -210,7 +223,8 @@ class MealTypeFactory(factory.django.DjangoModelFactory):
|
||||
# icon =
|
||||
color = factory.LazyAttribute(lambda x: faker.safe_hex_color())
|
||||
default = False
|
||||
created_by = factory.SubFactory(UserFactory, space=factory.SelfAttribute('..space'))
|
||||
created_by = factory.SubFactory(
|
||||
UserFactory, space=factory.SelfAttribute('..space'))
|
||||
space = factory.SubFactory(SpaceFactory)
|
||||
|
||||
class Meta:
|
||||
@ -221,13 +235,17 @@ class MealTypeFactory(factory.django.DjangoModelFactory):
|
||||
class MealPlanFactory(factory.django.DjangoModelFactory):
|
||||
recipe = factory.Maybe(
|
||||
factory.LazyAttribute(lambda x: x.has_recipe),
|
||||
yes_declaration=factory.SubFactory('cookbook.tests.factories.RecipeFactory', space=factory.SelfAttribute('..space')),
|
||||
yes_declaration=factory.SubFactory(
|
||||
'cookbook.tests.factories.RecipeFactory', space=factory.SelfAttribute('..space')),
|
||||
no_declaration=None
|
||||
)
|
||||
servings = factory.LazyAttribute(lambda x: Decimal(faker.random_int(min=1, max=1000)/100))
|
||||
servings = factory.LazyAttribute(
|
||||
lambda x: Decimal(faker.random_int(min=1, max=1000) / 100))
|
||||
title = factory.LazyAttribute(lambda x: faker.sentence(nb_words=5))
|
||||
created_by = factory.SubFactory(UserFactory, space=factory.SelfAttribute('..space'))
|
||||
meal_type = factory.SubFactory(MealTypeFactory, space=factory.SelfAttribute('..space'))
|
||||
created_by = factory.SubFactory(
|
||||
UserFactory, space=factory.SelfAttribute('..space'))
|
||||
meal_type = factory.SubFactory(
|
||||
MealTypeFactory, space=factory.SelfAttribute('..space'))
|
||||
note = factory.LazyAttribute(lambda x: faker.paragraph())
|
||||
date = factory.LazyAttribute(lambda x: faker.future_date())
|
||||
space = factory.SubFactory(SpaceFactory)
|
||||
@ -244,11 +262,13 @@ class ShoppingListRecipeFactory(factory.django.DjangoModelFactory):
|
||||
name = factory.LazyAttribute(lambda x: faker.sentence(nb_words=5))
|
||||
recipe = factory.Maybe(
|
||||
factory.LazyAttribute(lambda x: x.has_recipe),
|
||||
yes_declaration=factory.SubFactory('cookbook.tests.factories.RecipeFactory', space=factory.SelfAttribute('..space')),
|
||||
yes_declaration=factory.SubFactory(
|
||||
'cookbook.tests.factories.RecipeFactory', space=factory.SelfAttribute('..space')),
|
||||
no_declaration=None
|
||||
)
|
||||
servings = factory.LazyAttribute(lambda x: faker.random_int(min=1, max=10))
|
||||
mealplan = factory.SubFactory(MealPlanFactory, space=factory.SelfAttribute('..space'))
|
||||
mealplan = factory.SubFactory(
|
||||
MealPlanFactory, space=factory.SelfAttribute('..space'))
|
||||
space = factory.SubFactory(SpaceFactory)
|
||||
|
||||
class Params:
|
||||
@ -264,25 +284,32 @@ class ShoppingListEntryFactory(factory.django.DjangoModelFactory):
|
||||
|
||||
list_recipe = factory.Maybe(
|
||||
factory.LazyAttribute(lambda x: x.has_mealplan),
|
||||
yes_declaration=factory.SubFactory(ShoppingListRecipeFactory, space=factory.SelfAttribute('..space')),
|
||||
yes_declaration=factory.SubFactory(
|
||||
ShoppingListRecipeFactory, space=factory.SelfAttribute('..space')),
|
||||
no_declaration=None
|
||||
)
|
||||
food = factory.SubFactory(FoodFactory, space=factory.SelfAttribute('..space'))
|
||||
unit = factory.SubFactory(UnitFactory, space=factory.SelfAttribute('..space'))
|
||||
food = factory.SubFactory(
|
||||
FoodFactory, space=factory.SelfAttribute('..space'))
|
||||
unit = factory.SubFactory(
|
||||
UnitFactory, space=factory.SelfAttribute('..space'))
|
||||
# # ingredient = factory.SubFactory(IngredientFactory)
|
||||
amount = factory.LazyAttribute(lambda x: Decimal(faker.random_int(min=1, max=100))/10)
|
||||
amount = factory.LazyAttribute(
|
||||
lambda x: Decimal(faker.random_int(min=1, max=100)) / 10)
|
||||
order = factory.Sequence(int)
|
||||
checked = False
|
||||
created_by = factory.SubFactory(UserFactory, space=factory.SelfAttribute('..space'))
|
||||
created_by = factory.SubFactory(
|
||||
UserFactory, space=factory.SelfAttribute('..space'))
|
||||
created_at = factory.LazyAttribute(lambda x: faker.past_date())
|
||||
completed_at = None
|
||||
delay_until = None
|
||||
space = factory.SubFactory(SpaceFactory)
|
||||
|
||||
@classmethod
|
||||
def _create(cls, target_class, *args, **kwargs): # override create to prevent auto_add_now from changing the created_at date
|
||||
# override create to prevent auto_add_now from changing the created_at date
|
||||
def _create(cls, target_class, *args, **kwargs):
|
||||
created_at = kwargs.pop('created_at', None)
|
||||
obj = super(ShoppingListEntryFactory, cls)._create(target_class, *args, **kwargs)
|
||||
obj = super(ShoppingListEntryFactory, cls)._create(
|
||||
target_class, *args, **kwargs)
|
||||
if created_at is not None:
|
||||
obj.created_at = created_at
|
||||
obj.save()
|
||||
@ -298,7 +325,8 @@ class ShoppingListEntryFactory(factory.django.DjangoModelFactory):
|
||||
@register
|
||||
class StepFactory(factory.django.DjangoModelFactory):
|
||||
name = factory.LazyAttribute(lambda x: faker.sentence(nb_words=5))
|
||||
instruction = factory.LazyAttribute(lambda x: ''.join(faker.paragraphs(nb=5)))
|
||||
instruction = factory.LazyAttribute(
|
||||
lambda x: ''.join(faker.paragraphs(nb=5)))
|
||||
# TODO add optional recipe food, make dependent on recipe, make number of recipes a Params
|
||||
ingredients__count = 10 # default number of ingredients to add
|
||||
ingredients__header = 0
|
||||
@ -330,14 +358,16 @@ class StepFactory(factory.django.DjangoModelFactory):
|
||||
for i in range(num_ing):
|
||||
if num_food_recipe > 0:
|
||||
has_recipe = True
|
||||
num_food_recipe = num_food_recipe-1
|
||||
num_food_recipe = num_food_recipe - 1
|
||||
else:
|
||||
has_recipe = False
|
||||
self.ingredients.add(IngredientFactory(space=self.space, food__has_recipe=has_recipe))
|
||||
self.ingredients.add(IngredientFactory(
|
||||
space=self.space, food__has_recipe=has_recipe))
|
||||
num_header = kwargs.get('header', 0)
|
||||
if num_header > 0:
|
||||
for i in range(num_header):
|
||||
self.ingredients.add(IngredientFactory(food=None, unit=None, amount=0, is_header=True, space=self.space))
|
||||
self.ingredients.add(IngredientFactory(
|
||||
food=None, unit=None, amount=0, is_header=True, space=self.space))
|
||||
elif extracted:
|
||||
for ing in extracted:
|
||||
self.ingredients.add(ing)
|
||||
@ -351,20 +381,27 @@ class RecipeFactory(factory.django.DjangoModelFactory):
|
||||
name = factory.LazyAttribute(lambda x: faker.sentence(nb_words=7))
|
||||
description = factory.LazyAttribute(lambda x: faker.sentence(nb_words=10))
|
||||
servings = factory.LazyAttribute(lambda x: faker.random_int(min=1, max=20))
|
||||
servings_text = factory.LazyAttribute(lambda x: faker.sentence(nb_words=1)) # TODO generate list of expected servings text that can be iterated through
|
||||
# TODO generate list of expected servings text that can be iterated through
|
||||
servings_text = factory.LazyAttribute(lambda x: faker.sentence(nb_words=1))
|
||||
keywords__count = 5 # default number of keywords to generate
|
||||
steps__count = 1 # default number of steps to create
|
||||
steps__recipe_count = 0 # default number of step recipes to create
|
||||
steps__food_recipe_count = {} # by default, don't create food recipes, to override {'steps__food_recipe_count': {'step': 0, 'count': 1}}
|
||||
working_time = factory.LazyAttribute(lambda x: faker.random_int(min=0, max=360))
|
||||
waiting_time = factory.LazyAttribute(lambda x: faker.random_int(min=0, max=360))
|
||||
# by default, don't create food recipes, to override {'steps__food_recipe_count': {'step': 0, 'count': 1}}
|
||||
steps__food_recipe_count = {}
|
||||
working_time = factory.LazyAttribute(
|
||||
lambda x: faker.random_int(min=0, max=360))
|
||||
waiting_time = factory.LazyAttribute(
|
||||
lambda x: faker.random_int(min=0, max=360))
|
||||
internal = False
|
||||
created_by = factory.SubFactory(UserFactory, space=factory.SelfAttribute('..space'))
|
||||
created_at = factory.LazyAttribute(lambda x: faker.date_between_dates(date_start=date(2000, 1, 1), date_end=date(2020, 12, 31)))
|
||||
created_by = factory.SubFactory(
|
||||
UserFactory, space=factory.SelfAttribute('..space'))
|
||||
created_at = factory.LazyAttribute(lambda x: faker.date_between_dates(
|
||||
date_start=date(2000, 1, 1), date_end=date(2020, 12, 31)))
|
||||
space = factory.SubFactory(SpaceFactory)
|
||||
|
||||
@classmethod
|
||||
def _create(cls, target_class, *args, **kwargs): # override create to prevent auto_add_now from changing the created_at date
|
||||
# override create to prevent auto_add_now from changing the created_at date
|
||||
def _create(cls, target_class, *args, **kwargs):
|
||||
created_at = kwargs.pop('created_at', None)
|
||||
# updated_at = kwargs.pop('updated_at', None)
|
||||
obj = super(RecipeFactory, cls)._create(target_class, *args, **kwargs)
|
||||
@ -401,11 +438,13 @@ class RecipeFactory(factory.django.DjangoModelFactory):
|
||||
ing_recipe_count = 0
|
||||
if food_recipe_count.get('step', None) == i:
|
||||
ing_recipe_count = food_recipe_count.get('count', 0)
|
||||
self.steps.add(StepFactory(space=self.space, ingredients__food_recipe_count=ing_recipe_count, ingredients__header=num_ing_headers))
|
||||
num_ing_headers+-1
|
||||
self.steps.add(StepFactory(
|
||||
space=self.space, ingredients__food_recipe_count=ing_recipe_count, ingredients__header=num_ing_headers))
|
||||
num_ing_headers + - 1
|
||||
if num_recipe_steps > 0:
|
||||
for j in range(num_recipe_steps):
|
||||
self.steps.add(StepFactory(space=self.space, step_recipe__has_recipe=True, ingredients__count=0))
|
||||
self.steps.add(StepFactory(
|
||||
space=self.space, step_recipe__has_recipe=True, ingredients__count=0))
|
||||
if extracted and (num_steps + num_recipe_steps == 0):
|
||||
for step in extracted:
|
||||
self.steps.add(step)
|
||||
@ -428,15 +467,18 @@ class RecipeFactory(factory.django.DjangoModelFactory):
|
||||
@register
|
||||
class CookLogFactory(factory.django.DjangoModelFactory):
|
||||
"""CookLog factory."""
|
||||
recipe = factory.SubFactory(RecipeFactory, space=factory.SelfAttribute('..space'))
|
||||
created_by = factory.SubFactory(UserFactory, space=factory.SelfAttribute('..space'))
|
||||
recipe = factory.SubFactory(
|
||||
RecipeFactory, space=factory.SelfAttribute('..space'))
|
||||
created_by = factory.SubFactory(
|
||||
UserFactory, space=factory.SelfAttribute('..space'))
|
||||
created_at = factory.LazyAttribute(lambda x: faker.date_this_decade())
|
||||
rating = factory.LazyAttribute(lambda x: faker.random_int(min=1, max=5))
|
||||
servings = factory.LazyAttribute(lambda x: faker.random_int(min=1, max=32))
|
||||
space = factory.SubFactory(SpaceFactory)
|
||||
|
||||
@classmethod
|
||||
def _create(cls, target_class, *args, **kwargs): # override create to prevent auto_add_now from changing the created_at date
|
||||
# override create to prevent auto_add_now from changing the created_at date
|
||||
def _create(cls, target_class, *args, **kwargs):
|
||||
created_at = kwargs.pop('created_at', None)
|
||||
obj = super(CookLogFactory, cls)._create(target_class, *args, **kwargs)
|
||||
if created_at is not None:
|
||||
@ -451,13 +493,17 @@ class CookLogFactory(factory.django.DjangoModelFactory):
|
||||
@register
|
||||
class ViewLogFactory(factory.django.DjangoModelFactory):
|
||||
"""ViewLog factory."""
|
||||
recipe = factory.SubFactory(RecipeFactory, space=factory.SelfAttribute('..space'))
|
||||
created_by = factory.SubFactory(UserFactory, space=factory.SelfAttribute('..space'))
|
||||
created_at = factory.LazyAttribute(lambda x: faker.past_datetime(start_date='-365d'))
|
||||
recipe = factory.SubFactory(
|
||||
RecipeFactory, space=factory.SelfAttribute('..space'))
|
||||
created_by = factory.SubFactory(
|
||||
UserFactory, space=factory.SelfAttribute('..space'))
|
||||
created_at = factory.LazyAttribute(
|
||||
lambda x: faker.past_datetime(start_date='-365d'))
|
||||
space = factory.SubFactory(SpaceFactory)
|
||||
|
||||
@classmethod
|
||||
def _create(cls, target_class, *args, **kwargs): # override create to prevent auto_add_now from changing the created_at date
|
||||
# override create to prevent auto_add_now from changing the created_at date
|
||||
def _create(cls, target_class, *args, **kwargs):
|
||||
created_at = kwargs.pop('created_at', None)
|
||||
obj = super(ViewLogFactory, cls)._create(target_class, *args, **kwargs)
|
||||
if created_at is not None:
|
||||
|
@ -11,6 +11,11 @@ from cookbook.tests.factories import FoodFactory, RecipeFactory
|
||||
# TODO returns recipes with all ingredients via child substitute
|
||||
# TODO returns recipes with all ingredients via sibling substitute
|
||||
|
||||
if (Food.node_order_by):
|
||||
node_location = 'sorted-child'
|
||||
else:
|
||||
node_location = 'last-child'
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def recipes(space_1):
|
||||
@ -19,7 +24,8 @@ def recipes(space_1):
|
||||
|
||||
@pytest.fixture
|
||||
def makenow_recipe(request, space_1):
|
||||
onhand_user = auth.get_user(request.getfixturevalue(request.param.get('onhand_users', 'u1_s1')))
|
||||
onhand_user = auth.get_user(request.getfixturevalue(
|
||||
request.param.get('onhand_users', 'u1_s1')))
|
||||
|
||||
recipe = RecipeFactory.create(space=space_1)
|
||||
for food in Food.objects.filter(ingredient__step__recipe=recipe.id):
|
||||
@ -55,13 +61,16 @@ def test_makenow_ignoreshopping(recipes, makenow_recipe, user1, space_1):
|
||||
request = type('', (object,), {'space': space_1, 'user': user1})()
|
||||
search = RecipeSearch(request, makenow='true')
|
||||
with scope(space=space_1):
|
||||
food = Food.objects.filter(ingredient__step__recipe=makenow_recipe.id).first()
|
||||
food = Food.objects.filter(
|
||||
ingredient__step__recipe=makenow_recipe.id).first()
|
||||
food.onhand_users.clear()
|
||||
assert search.get_queryset(Recipe.objects.all()).count() == 0
|
||||
food.ignore_shopping = True
|
||||
food.save()
|
||||
assert Food.objects.filter(ingredient__step__recipe=makenow_recipe.id, onhand_users__isnull=False).count() == 9
|
||||
assert Food.objects.filter(ingredient__step__recipe=makenow_recipe.id, ignore_shopping=True).count() == 1
|
||||
assert Food.objects.filter(
|
||||
ingredient__step__recipe=makenow_recipe.id, onhand_users__isnull=False).count() == 9
|
||||
assert Food.objects.filter(
|
||||
ingredient__step__recipe=makenow_recipe.id, ignore_shopping=True).count() == 1
|
||||
search = search.get_queryset(Recipe.objects.all())
|
||||
assert search.count() == 1
|
||||
assert search.first().id == makenow_recipe.id
|
||||
@ -74,13 +83,17 @@ def test_makenow_substitute(recipes, makenow_recipe, user1, space_1):
|
||||
request = type('', (object,), {'space': space_1, 'user': user1})()
|
||||
search = RecipeSearch(request, makenow='true')
|
||||
with scope(space=space_1):
|
||||
food = Food.objects.filter(ingredient__step__recipe=makenow_recipe.id).first()
|
||||
food = Food.objects.filter(
|
||||
ingredient__step__recipe=makenow_recipe.id).first()
|
||||
onhand_user = food.onhand_users.first()
|
||||
food.onhand_users.clear()
|
||||
assert search.get_queryset(Recipe.objects.all()).count() == 0
|
||||
food.substitute.add(FoodFactory.create(space=space_1, onhand_users=[onhand_user]))
|
||||
assert Food.objects.filter(ingredient__step__recipe=makenow_recipe.id, onhand_users__isnull=False).count() == 9
|
||||
assert Food.objects.filter(ingredient__step__recipe=makenow_recipe.id, substitute__isnull=False).count() == 1
|
||||
food.substitute.add(FoodFactory.create(
|
||||
space=space_1, onhand_users=[onhand_user]))
|
||||
assert Food.objects.filter(
|
||||
ingredient__step__recipe=makenow_recipe.id, onhand_users__isnull=False).count() == 9
|
||||
assert Food.objects.filter(
|
||||
ingredient__step__recipe=makenow_recipe.id, substitute__isnull=False).count() == 1
|
||||
|
||||
search = search.get_queryset(Recipe.objects.all())
|
||||
assert search.count() == 1
|
||||
@ -94,16 +107,20 @@ def test_makenow_child_substitute(recipes, makenow_recipe, user1, space_1):
|
||||
request = type('', (object,), {'space': space_1, 'user': user1})()
|
||||
search = RecipeSearch(request, makenow='true')
|
||||
with scope(space=space_1):
|
||||
food = Food.objects.filter(ingredient__step__recipe=makenow_recipe.id).first()
|
||||
food = Food.objects.filter(
|
||||
ingredient__step__recipe=makenow_recipe.id).first()
|
||||
onhand_user = food.onhand_users.first()
|
||||
food.onhand_users.clear()
|
||||
food.substitute_children = True
|
||||
food.save()
|
||||
assert search.get_queryset(Recipe.objects.all()).count() == 0
|
||||
new_food = FoodFactory.create(space=space_1, onhand_users=[onhand_user])
|
||||
new_food.move(food, 'first-child')
|
||||
assert Food.objects.filter(ingredient__step__recipe=makenow_recipe.id, onhand_users__isnull=False).count() == 9
|
||||
assert Food.objects.filter(ingredient__step__recipe=makenow_recipe.id, numchild__gt=0).count() == 1
|
||||
new_food = FoodFactory.create(
|
||||
space=space_1, onhand_users=[onhand_user])
|
||||
new_food.move(food, node_location)
|
||||
assert Food.objects.filter(
|
||||
ingredient__step__recipe=makenow_recipe.id, onhand_users__isnull=False).count() == 9
|
||||
assert Food.objects.filter(
|
||||
ingredient__step__recipe=makenow_recipe.id, numchild__gt=0).count() == 1
|
||||
search = search.get_queryset(Recipe.objects.all())
|
||||
assert search.count() == 1
|
||||
assert search.first().id == makenow_recipe.id
|
||||
@ -116,18 +133,22 @@ def test_makenow_sibling_substitute(recipes, makenow_recipe, user1, space_1):
|
||||
request = type('', (object,), {'space': space_1, 'user': user1})()
|
||||
search = RecipeSearch(request, makenow='true')
|
||||
with scope(space=space_1):
|
||||
food = Food.objects.filter(ingredient__step__recipe=makenow_recipe.id).first()
|
||||
food = Food.objects.filter(
|
||||
ingredient__step__recipe=makenow_recipe.id).first()
|
||||
onhand_user = food.onhand_users.first()
|
||||
food.onhand_users.clear()
|
||||
food.substitute_siblings = True
|
||||
food.save()
|
||||
assert search.get_queryset(Recipe.objects.all()).count() == 0
|
||||
new_parent = FoodFactory.create(space=space_1)
|
||||
new_sibling = FoodFactory.create(space=space_1, onhand_users=[onhand_user])
|
||||
new_sibling.move(new_parent, 'first-child')
|
||||
food.move(new_parent, 'first-child')
|
||||
assert Food.objects.filter(ingredient__step__recipe=makenow_recipe.id, onhand_users__isnull=False).count() == 9
|
||||
assert Food.objects.filter(ingredient__step__recipe=makenow_recipe.id, depth=2).count() == 1
|
||||
new_sibling = FoodFactory.create(
|
||||
space=space_1, onhand_users=[onhand_user])
|
||||
new_sibling.move(new_parent, node_location)
|
||||
food.move(new_parent, node_location)
|
||||
assert Food.objects.filter(
|
||||
ingredient__step__recipe=makenow_recipe.id, onhand_users__isnull=False).count() == 9
|
||||
assert Food.objects.filter(
|
||||
ingredient__step__recipe=makenow_recipe.id, depth=2).count() == 1
|
||||
search = search.get_queryset(Recipe.objects.all())
|
||||
assert search.count() == 1
|
||||
assert search.first().id == makenow_recipe.id
|
||||
|
@ -7,9 +7,9 @@ from django.conf import settings
|
||||
from django.contrib import auth
|
||||
from django.urls import reverse
|
||||
from django.utils import timezone
|
||||
from django_scopes import scope, scopes_disabled
|
||||
from django_scopes import scope
|
||||
|
||||
from cookbook.models import Food, Recipe, SearchFields
|
||||
from cookbook.models import Recipe, SearchFields
|
||||
from cookbook.tests.conftest import transpose
|
||||
from cookbook.tests.factories import (CookLogFactory, FoodFactory, IngredientFactory,
|
||||
KeywordFactory, RecipeBookEntryFactory, RecipeFactory,
|
||||
@ -23,7 +23,8 @@ from cookbook.tests.factories import (CookLogFactory, FoodFactory, IngredientFac
|
||||
# TODO makenow with above filters
|
||||
# TODO test search food/keywords including/excluding children
|
||||
LIST_URL = 'api:recipe-list'
|
||||
sqlite = settings.DATABASES['default']['ENGINE'] not in ['django.db.backends.postgresql_psycopg2', 'django.db.backends.postgresql']
|
||||
sqlite = settings.DATABASES['default']['ENGINE'] not in [
|
||||
'django.db.backends.postgresql_psycopg2', 'django.db.backends.postgresql']
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
@ -50,26 +51,43 @@ def user1(request, space_1, u1_s1, unaccent):
|
||||
if params.get('fuzzy_lookups', False):
|
||||
user.searchpreference.lookup = True
|
||||
misspelled_result = 1
|
||||
else:
|
||||
user.searchpreference.lookup = False
|
||||
|
||||
if params.get('fuzzy_search', False):
|
||||
user.searchpreference.trigram.set(SearchFields.objects.all())
|
||||
misspelled_result = 1
|
||||
else:
|
||||
user.searchpreference.trigram.set([])
|
||||
|
||||
if params.get('icontains', False):
|
||||
user.searchpreference.icontains.set(SearchFields.objects.all())
|
||||
search_term = 'ghijklmn'
|
||||
else:
|
||||
user.searchpreference.icontains.set([])
|
||||
|
||||
if params.get('istartswith', False):
|
||||
user.searchpreference.istartswith.set(SearchFields.objects.all())
|
||||
search_term = 'abcdef'
|
||||
else:
|
||||
user.searchpreference.istartswith.set([])
|
||||
|
||||
if params.get('unaccent', False):
|
||||
user.searchpreference.unaccent.set(SearchFields.objects.all())
|
||||
misspelled_result *= 2
|
||||
result *= 2
|
||||
else:
|
||||
user.searchpreference.unaccent.set([])
|
||||
|
||||
# full text vectors are hard coded to use unaccent - put this after unaccent to override result
|
||||
if params.get('fulltext', False):
|
||||
user.searchpreference.fulltext.set(SearchFields.objects.all())
|
||||
# user.searchpreference.search = 'websearch'
|
||||
search_term = 'ghijklmn uvwxyz'
|
||||
result = 2
|
||||
else:
|
||||
user.searchpreference.fulltext.set([])
|
||||
|
||||
user.searchpreference.save()
|
||||
misspelled_term = transpose(search_term, number=3)
|
||||
return (u1_s1, result, misspelled_result, search_term, misspelled_term, params)
|
||||
@ -104,7 +122,8 @@ def found_recipe(request, space_1, accent, unaccent, u1_s1, u2_s1):
|
||||
obj2 = FoodFactory.create(name=accent, space=space_1)
|
||||
recipe1.steps.first().ingredients.add(IngredientFactory.create(food=obj1))
|
||||
recipe2.steps.first().ingredients.add(IngredientFactory.create(food=obj2))
|
||||
recipe3.steps.first().ingredients.add(IngredientFactory.create(food=obj1), IngredientFactory.create(food=obj2))
|
||||
recipe3.steps.first().ingredients.add(IngredientFactory.create(
|
||||
food=obj1), IngredientFactory.create(food=obj2))
|
||||
if request.param.get('keyword', None):
|
||||
obj1 = KeywordFactory.create(name=unaccent, space=space_1)
|
||||
obj2 = KeywordFactory.create(name=accent, space=space_1)
|
||||
@ -125,7 +144,8 @@ def found_recipe(request, space_1, accent, unaccent, u1_s1, u2_s1):
|
||||
obj2 = UnitFactory.create(name=accent, space=space_1)
|
||||
recipe1.steps.first().ingredients.add(IngredientFactory.create(unit=obj1))
|
||||
recipe2.steps.first().ingredients.add(IngredientFactory.create(unit=obj2))
|
||||
recipe3.steps.first().ingredients.add(IngredientFactory.create(unit=obj1), IngredientFactory.create(unit=obj2))
|
||||
recipe3.steps.first().ingredients.add(IngredientFactory.create(
|
||||
unit=obj1), IngredientFactory.create(unit=obj2))
|
||||
if request.param.get('name', None):
|
||||
recipe1.name = unaccent
|
||||
recipe2.name = accent
|
||||
@ -145,21 +165,32 @@ def found_recipe(request, space_1, accent, unaccent, u1_s1, u2_s1):
|
||||
i2.save()
|
||||
|
||||
if request.param.get('viewedon', None):
|
||||
ViewLogFactory.create(recipe=recipe1, created_by=user1, created_at=days_3, space=space_1)
|
||||
ViewLogFactory.create(recipe=recipe2, created_by=user1, created_at=days_30, space=space_1)
|
||||
ViewLogFactory.create(recipe=recipe3, created_by=user2, created_at=days_15, space=space_1)
|
||||
ViewLogFactory.create(recipe=recipe1, created_by=user1,
|
||||
created_at=days_3, space=space_1)
|
||||
ViewLogFactory.create(recipe=recipe2, created_by=user1,
|
||||
created_at=days_30, space=space_1)
|
||||
ViewLogFactory.create(recipe=recipe3, created_by=user2,
|
||||
created_at=days_15, space=space_1)
|
||||
if request.param.get('cookedon', None):
|
||||
CookLogFactory.create(recipe=recipe1, created_by=user1, created_at=days_3, space=space_1)
|
||||
CookLogFactory.create(recipe=recipe2, created_by=user1, created_at=days_30, space=space_1)
|
||||
CookLogFactory.create(recipe=recipe3, created_by=user2, created_at=days_15, space=space_1)
|
||||
CookLogFactory.create(recipe=recipe1, created_by=user1,
|
||||
created_at=days_3, space=space_1)
|
||||
CookLogFactory.create(recipe=recipe2, created_by=user1,
|
||||
created_at=days_30, space=space_1)
|
||||
CookLogFactory.create(recipe=recipe3, created_by=user2,
|
||||
created_at=days_15, space=space_1)
|
||||
if request.param.get('timescooked', None):
|
||||
CookLogFactory.create_batch(5, recipe=recipe1, created_by=user1, space=space_1)
|
||||
CookLogFactory.create_batch(
|
||||
5, recipe=recipe1, created_by=user1, space=space_1)
|
||||
CookLogFactory.create(recipe=recipe2, created_by=user1, space=space_1)
|
||||
CookLogFactory.create_batch(3, recipe=recipe3, created_by=user2, space=space_1)
|
||||
CookLogFactory.create_batch(
|
||||
3, recipe=recipe3, created_by=user2, space=space_1)
|
||||
if request.param.get('rating', None):
|
||||
CookLogFactory.create(recipe=recipe1, created_by=user1, rating=5.0, space=space_1)
|
||||
CookLogFactory.create(recipe=recipe2, created_by=user1, rating=1.0, space=space_1)
|
||||
CookLogFactory.create(recipe=recipe3, created_by=user2, rating=3.0, space=space_1)
|
||||
CookLogFactory.create(
|
||||
recipe=recipe1, created_by=user1, rating=5.0, space=space_1)
|
||||
CookLogFactory.create(
|
||||
recipe=recipe2, created_by=user1, rating=1.0, space=space_1)
|
||||
CookLogFactory.create(
|
||||
recipe=recipe3, created_by=user2, rating=3.0, space=space_1)
|
||||
|
||||
return (recipe1, recipe2, recipe3, obj1, obj2, request.param)
|
||||
|
||||
@ -188,7 +219,8 @@ def test_search_or_and_not(found_recipe, param_type, operator, recipes, u1_s1, s
|
||||
assert found_recipe[1].id in [x['id'] for x in r['results']]
|
||||
assert found_recipe[2].id in [x['id'] for x in r['results']]
|
||||
|
||||
r = json.loads(u1_s1.get(reverse(LIST_URL) + f'?{param1}&{param2}').content)
|
||||
r = json.loads(u1_s1.get(reverse(LIST_URL) +
|
||||
f'?{param1}&{param2}').content)
|
||||
assert r['count'] == operator[1]
|
||||
assert found_recipe[2].id in [x['id'] for x in r['results']]
|
||||
|
||||
@ -203,7 +235,8 @@ def test_search_or_and_not(found_recipe, param_type, operator, recipes, u1_s1, s
|
||||
assert found_recipe[1].id not in [x['id'] for x in r['results']]
|
||||
assert found_recipe[2].id not in [x['id'] for x in r['results']]
|
||||
|
||||
r = json.loads(u1_s1.get(reverse(LIST_URL) + f'?{param1_not}&{param2_not}').content)
|
||||
r = json.loads(u1_s1.get(reverse(LIST_URL) +
|
||||
f'?{param1_not}&{param2_not}').content)
|
||||
assert r['count'] == 10 + operator[2]
|
||||
assert found_recipe[2].id not in [x['id'] for x in r['results']]
|
||||
|
||||
@ -227,7 +260,8 @@ def test_search_units(found_recipe, recipes, u1_s1, space_1):
|
||||
assert found_recipe[1].id in [x['id'] for x in r['results']]
|
||||
assert found_recipe[2].id in [x['id'] for x in r['results']]
|
||||
|
||||
r = json.loads(u1_s1.get(reverse(LIST_URL) + f'?{param1}&{param2}').content)
|
||||
r = json.loads(u1_s1.get(reverse(LIST_URL) +
|
||||
f'?{param1}&{param2}').content)
|
||||
assert r['count'] == 3
|
||||
assert found_recipe[2].id in [x['id'] for x in r['results']]
|
||||
|
||||
@ -251,11 +285,15 @@ def test_fuzzy_lookup(found_recipe, recipes, param_type, user1, space_1):
|
||||
param1 = f"query={user1[3]}"
|
||||
param2 = f"query={user1[4]}"
|
||||
|
||||
r = json.loads(user1[0].get(reverse(list_url) + f'?{param1}&limit=2').content)
|
||||
assert len([x['id'] for x in r['results'] if x['id'] in [found_recipe[3].id, found_recipe[4].id]]) == user1[1]
|
||||
r = json.loads(user1[0].get(reverse(list_url) +
|
||||
f'?{param1}&limit=2').content)
|
||||
assert len([x['id'] for x in r['results'] if x['id'] in [
|
||||
found_recipe[3].id, found_recipe[4].id]]) == user1[1]
|
||||
|
||||
r = json.loads(user1[0].get(reverse(list_url) + f'?{param2}&limit=10').content)
|
||||
assert len([x['id'] for x in r['results'] if x['id'] in [found_recipe[3].id, found_recipe[4].id]]) == user1[2]
|
||||
r = json.loads(user1[0].get(reverse(list_url) +
|
||||
f'?{param2}&limit=10').content)
|
||||
assert len([x['id'] for x in r['results'] if x['id'] in [
|
||||
found_recipe[3].id, found_recipe[4].id]]) == user1[2]
|
||||
|
||||
# commenting this out for general use - it is really slow
|
||||
# it should be run on occasion to ensure everything still works
|
||||
@ -276,29 +314,35 @@ def test_fuzzy_lookup(found_recipe, recipes, param_type, user1, space_1):
|
||||
# ({'keyword': True}),
|
||||
# ({'food': True}),
|
||||
# ], indirect=['found_recipe'])
|
||||
# # user array contains: user client, expected count of search, expected count of mispelled search, search string, mispelled search string, user search preferences
|
||||
# def test_search_string(found_recipe, recipes, user1, space_1):
|
||||
# with scope(space=space_1):
|
||||
# param1 = f"query={user1[3]}"
|
||||
# param2 = f"query={user1[4]}"
|
||||
|
||||
# r = json.loads(user1[0].get(reverse(LIST_URL) + f'?{param1}').content)
|
||||
# assert len([x['id'] for x in r['results'] if x['id'] in [found_recipe[0].id, found_recipe[1].id]]) == user1[1]
|
||||
# assert len([x['id'] for x in r['results'] if x['id'] in [
|
||||
# found_recipe[0].id, found_recipe[1].id]]) == user1[1]
|
||||
|
||||
# r = json.loads(user1[0].get(reverse(LIST_URL) + f'?{param2}').content)
|
||||
# assert len([x['id'] for x in r['results'] if x['id'] in [found_recipe[0].id, found_recipe[1].id]]) == user1[2]
|
||||
# assert len([x['id'] for x in r['results'] if x['id'] in [
|
||||
# found_recipe[0].id, found_recipe[1].id]]) == user1[2]
|
||||
|
||||
|
||||
@pytest.mark.parametrize("found_recipe, param_type, result", [
|
||||
({'viewedon': True}, 'viewedon', (1, 1)),
|
||||
({'cookedon': True}, 'cookedon', (1, 1)),
|
||||
({'createdon': True}, 'createdon', (2, 12)), # created dates are not filtered by user
|
||||
({'createdon': True}, 'updatedon', (2, 12)), # updated dates are not filtered by user
|
||||
# created dates are not filtered by user
|
||||
({'createdon': True}, 'createdon', (2, 12)),
|
||||
# updated dates are not filtered by user
|
||||
({'createdon': True}, 'updatedon', (2, 12)),
|
||||
], indirect=['found_recipe'])
|
||||
def test_search_date(found_recipe, recipes, param_type, result, u1_s1, u2_s1, space_1):
|
||||
# force updated_at to equal created_at datetime
|
||||
with scope(space=space_1):
|
||||
for recipe in Recipe.objects.all():
|
||||
Recipe.objects.filter(id=recipe.id).update(updated_at=recipe.created_at)
|
||||
Recipe.objects.filter(id=recipe.id).update(
|
||||
updated_at=recipe.created_at)
|
||||
|
||||
date = (timezone.now() - timedelta(days=15)).strftime("%Y-%m-%d")
|
||||
param1 = f"?{param_type}={date}"
|
||||
@ -321,34 +365,34 @@ def test_search_date(found_recipe, recipes, param_type, result, u1_s1, u2_s1, sp
|
||||
assert found_recipe[2].id in [x['id'] for x in r['results']]
|
||||
|
||||
|
||||
# TODO this is somehow screwed, probably the search itself, dont want to fix it for now
|
||||
# @pytest.mark.parametrize("found_recipe, param_type", [
|
||||
# ({'rating': True}, 'rating'),
|
||||
# ({'timescooked': True}, 'timescooked'),
|
||||
# ], indirect=['found_recipe'])
|
||||
# def test_search_count(found_recipe, recipes, param_type, u1_s1, u2_s1, space_1):
|
||||
# param1 = f'?{param_type}=3'
|
||||
# param2 = f'?{param_type}=-3'
|
||||
# param3 = f'?{param_type}=0'
|
||||
#
|
||||
# r = json.loads(u1_s1.get(reverse(LIST_URL) + param1).content)
|
||||
# assert r['count'] == 1
|
||||
# assert found_recipe[0].id in [x['id'] for x in r['results']]
|
||||
#
|
||||
# r = json.loads(u1_s1.get(reverse(LIST_URL) + param2).content)
|
||||
# assert r['count'] == 1
|
||||
# assert found_recipe[1].id in [x['id'] for x in r['results']]
|
||||
#
|
||||
# # test search for not rated/cooked
|
||||
# r = json.loads(u1_s1.get(reverse(LIST_URL) + param3).content)
|
||||
# assert r['count'] == 11
|
||||
# assert (found_recipe[0].id or found_recipe[1].id) not in [x['id'] for x in r['results']]
|
||||
#
|
||||
# # test matched returns for lte and gte searches
|
||||
# r = json.loads(u2_s1.get(reverse(LIST_URL) + param1).content)
|
||||
# assert r['count'] == 1
|
||||
# assert found_recipe[2].id in [x['id'] for x in r['results']]
|
||||
#
|
||||
# r = json.loads(u2_s1.get(reverse(LIST_URL) + param2).content)
|
||||
# assert r['count'] == 1
|
||||
# assert found_recipe[2].id in [x['id'] for x in r['results']]
|
||||
@pytest.mark.parametrize("found_recipe, param_type", [
|
||||
({'rating': True}, 'rating'),
|
||||
({'timescooked': True}, 'timescooked'),
|
||||
], indirect=['found_recipe'])
|
||||
def test_search_count(found_recipe, recipes, param_type, u1_s1, u2_s1, space_1):
|
||||
param1 = f'?{param_type}=3'
|
||||
param2 = f'?{param_type}=-3'
|
||||
param3 = f'?{param_type}=0'
|
||||
|
||||
r = json.loads(u1_s1.get(reverse(LIST_URL) + param1).content)
|
||||
assert r['count'] == 1
|
||||
assert found_recipe[0].id in [x['id'] for x in r['results']]
|
||||
|
||||
r = json.loads(u1_s1.get(reverse(LIST_URL) + param2).content)
|
||||
assert r['count'] == 1
|
||||
assert found_recipe[1].id in [x['id'] for x in r['results']]
|
||||
|
||||
# test search for not rated/cooked
|
||||
r = json.loads(u1_s1.get(reverse(LIST_URL) + param3).content)
|
||||
assert r['count'] == 11
|
||||
assert (found_recipe[0].id or found_recipe[1].id) not in [
|
||||
x['id'] for x in r['results']]
|
||||
|
||||
# test matched returns for lte and gte searches
|
||||
r = json.loads(u2_s1.get(reverse(LIST_URL) + param1).content)
|
||||
assert r['count'] == 1
|
||||
assert found_recipe[2].id in [x['id'] for x in r['results']]
|
||||
|
||||
r = json.loads(u2_s1.get(reverse(LIST_URL) + param2).content)
|
||||
assert r['count'] == 1
|
||||
assert found_recipe[2].id in [x['id'] for x in r['results']]
|
||||
|
@ -96,6 +96,7 @@ AUTH_LDAP_USER_SEARCH_FILTER_STR=(uid=%(user)s)
|
||||
AUTH_LDAP_USER_ATTR_MAP={'first_name': 'givenName', 'last_name': 'sn', 'email': 'mail'}
|
||||
AUTH_LDAP_ALWAYS_UPDATE_USER=1
|
||||
AUTH_LDAP_CACHE_TIMEOUT=3600
|
||||
AUTH_LDAP_START_TLS=1
|
||||
AUTH_LDAP_TLS_CACERTFILE=/etc/ssl/certs/own-ca.pem
|
||||
```
|
||||
|
||||
|
@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-01-19 19:14+0100\n"
|
||||
"POT-Creation-Date: 2023-04-26 07:46+0200\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"
|
||||
@ -18,62 +18,66 @@ msgstr ""
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
#: .\recipes\settings.py:382
|
||||
#: .\recipes\settings.py:436
|
||||
msgid "Armenian "
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:383
|
||||
#: .\recipes\settings.py:437
|
||||
msgid "Bulgarian"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:384
|
||||
#: .\recipes\settings.py:438
|
||||
msgid "Catalan"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:385
|
||||
#: .\recipes\settings.py:439
|
||||
msgid "Czech"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:386
|
||||
#: .\recipes\settings.py:440
|
||||
msgid "Danish"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:387
|
||||
#: .\recipes\settings.py:441
|
||||
msgid "Dutch"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:388
|
||||
#: .\recipes\settings.py:442
|
||||
msgid "English"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:389
|
||||
#: .\recipes\settings.py:443
|
||||
msgid "French"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:390
|
||||
#: .\recipes\settings.py:444
|
||||
msgid "German"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:391
|
||||
#: .\recipes\settings.py:445
|
||||
msgid "Hungarian"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:446
|
||||
msgid "Italian"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:392
|
||||
#: .\recipes\settings.py:447
|
||||
msgid "Latvian"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:393
|
||||
#: .\recipes\settings.py:448
|
||||
msgid "Polish"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:394
|
||||
#: .\recipes\settings.py:449
|
||||
msgid "Russian"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:395
|
||||
#: .\recipes\settings.py:450
|
||||
msgid "Spanish"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:396
|
||||
#: .\recipes\settings.py:451
|
||||
msgid "Swedish"
|
||||
msgstr ""
|
||||
|
@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-01-19 19:14+0100\n"
|
||||
"POT-Creation-Date: 2023-04-26 07:46+0200\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"
|
||||
@ -18,64 +18,68 @@ msgstr ""
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
#: .\recipes\settings.py:382
|
||||
#: .\recipes\settings.py:436
|
||||
msgid "Armenian "
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:383
|
||||
#: .\recipes\settings.py:437
|
||||
msgid "Bulgarian"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:384
|
||||
#: .\recipes\settings.py:438
|
||||
msgid "Catalan"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:385
|
||||
#: .\recipes\settings.py:439
|
||||
msgid "Czech"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:386
|
||||
#: .\recipes\settings.py:440
|
||||
msgid "Danish"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:387
|
||||
#: .\recipes\settings.py:441
|
||||
msgid "Dutch"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:388
|
||||
#: .\recipes\settings.py:442
|
||||
msgid "English"
|
||||
msgstr "Englisch"
|
||||
|
||||
#: .\recipes\settings.py:389
|
||||
#: .\recipes\settings.py:443
|
||||
msgid "French"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:390
|
||||
#: .\recipes\settings.py:444
|
||||
msgid "German"
|
||||
msgstr "Deutsch"
|
||||
|
||||
#: .\recipes\settings.py:391
|
||||
#: .\recipes\settings.py:445
|
||||
msgid "Hungarian"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:446
|
||||
msgid "Italian"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:392
|
||||
#: .\recipes\settings.py:447
|
||||
msgid "Latvian"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:393
|
||||
#: .\recipes\settings.py:448
|
||||
#, fuzzy
|
||||
#| msgid "English"
|
||||
msgid "Polish"
|
||||
msgstr "Englisch"
|
||||
|
||||
#: .\recipes\settings.py:394
|
||||
#: .\recipes\settings.py:449
|
||||
msgid "Russian"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:395
|
||||
#: .\recipes\settings.py:450
|
||||
msgid "Spanish"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:396
|
||||
#: .\recipes\settings.py:451
|
||||
msgid "Swedish"
|
||||
msgstr ""
|
||||
|
@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-01-19 19:14+0100\n"
|
||||
"POT-Creation-Date: 2023-04-26 07:46+0200\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"
|
||||
@ -18,62 +18,66 @@ msgstr ""
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
#: .\recipes\settings.py:382
|
||||
#: .\recipes\settings.py:436
|
||||
msgid "Armenian "
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:383
|
||||
#: .\recipes\settings.py:437
|
||||
msgid "Bulgarian"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:384
|
||||
#: .\recipes\settings.py:438
|
||||
msgid "Catalan"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:385
|
||||
#: .\recipes\settings.py:439
|
||||
msgid "Czech"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:386
|
||||
#: .\recipes\settings.py:440
|
||||
msgid "Danish"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:387
|
||||
#: .\recipes\settings.py:441
|
||||
msgid "Dutch"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:388
|
||||
#: .\recipes\settings.py:442
|
||||
msgid "English"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:389
|
||||
#: .\recipes\settings.py:443
|
||||
msgid "French"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:390
|
||||
#: .\recipes\settings.py:444
|
||||
msgid "German"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:391
|
||||
#: .\recipes\settings.py:445
|
||||
msgid "Hungarian"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:446
|
||||
msgid "Italian"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:392
|
||||
#: .\recipes\settings.py:447
|
||||
msgid "Latvian"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:393
|
||||
#: .\recipes\settings.py:448
|
||||
msgid "Polish"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:394
|
||||
#: .\recipes\settings.py:449
|
||||
msgid "Russian"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:395
|
||||
#: .\recipes\settings.py:450
|
||||
msgid "Spanish"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:396
|
||||
#: .\recipes\settings.py:451
|
||||
msgid "Swedish"
|
||||
msgstr ""
|
||||
|
@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-01-19 19:14+0100\n"
|
||||
"POT-Creation-Date: 2023-04-26 07:46+0200\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"
|
||||
@ -18,62 +18,66 @@ msgstr ""
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
#: .\recipes\settings.py:382
|
||||
#: .\recipes\settings.py:436
|
||||
msgid "Armenian "
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:383
|
||||
#: .\recipes\settings.py:437
|
||||
msgid "Bulgarian"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:384
|
||||
#: .\recipes\settings.py:438
|
||||
msgid "Catalan"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:385
|
||||
#: .\recipes\settings.py:439
|
||||
msgid "Czech"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:386
|
||||
#: .\recipes\settings.py:440
|
||||
msgid "Danish"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:387
|
||||
#: .\recipes\settings.py:441
|
||||
msgid "Dutch"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:388
|
||||
#: .\recipes\settings.py:442
|
||||
msgid "English"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:389
|
||||
#: .\recipes\settings.py:443
|
||||
msgid "French"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:390
|
||||
#: .\recipes\settings.py:444
|
||||
msgid "German"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:391
|
||||
#: .\recipes\settings.py:445
|
||||
msgid "Hungarian"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:446
|
||||
msgid "Italian"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:392
|
||||
#: .\recipes\settings.py:447
|
||||
msgid "Latvian"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:393
|
||||
#: .\recipes\settings.py:448
|
||||
msgid "Polish"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:394
|
||||
#: .\recipes\settings.py:449
|
||||
msgid "Russian"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:395
|
||||
#: .\recipes\settings.py:450
|
||||
msgid "Spanish"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:396
|
||||
#: .\recipes\settings.py:451
|
||||
msgid "Swedish"
|
||||
msgstr ""
|
||||
|
@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-01-19 19:14+0100\n"
|
||||
"POT-Creation-Date: 2023-04-26 07:46+0200\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"
|
||||
@ -18,62 +18,66 @@ msgstr ""
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
|
||||
|
||||
#: .\recipes\settings.py:382
|
||||
#: .\recipes\settings.py:436
|
||||
msgid "Armenian "
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:383
|
||||
#: .\recipes\settings.py:437
|
||||
msgid "Bulgarian"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:384
|
||||
#: .\recipes\settings.py:438
|
||||
msgid "Catalan"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:385
|
||||
#: .\recipes\settings.py:439
|
||||
msgid "Czech"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:386
|
||||
#: .\recipes\settings.py:440
|
||||
msgid "Danish"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:387
|
||||
#: .\recipes\settings.py:441
|
||||
msgid "Dutch"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:388
|
||||
#: .\recipes\settings.py:442
|
||||
msgid "English"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:389
|
||||
#: .\recipes\settings.py:443
|
||||
msgid "French"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:390
|
||||
#: .\recipes\settings.py:444
|
||||
msgid "German"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:391
|
||||
#: .\recipes\settings.py:445
|
||||
msgid "Hungarian"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:446
|
||||
msgid "Italian"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:392
|
||||
#: .\recipes\settings.py:447
|
||||
msgid "Latvian"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:393
|
||||
#: .\recipes\settings.py:448
|
||||
msgid "Polish"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:394
|
||||
#: .\recipes\settings.py:449
|
||||
msgid "Russian"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:395
|
||||
#: .\recipes\settings.py:450
|
||||
msgid "Spanish"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:396
|
||||
#: .\recipes\settings.py:451
|
||||
msgid "Swedish"
|
||||
msgstr ""
|
||||
|
@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-01-19 19:14+0100\n"
|
||||
"POT-Creation-Date: 2023-04-26 07:46+0200\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"
|
||||
@ -17,62 +17,66 @@ msgstr ""
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
||||
#: .\recipes\settings.py:382
|
||||
#: .\recipes\settings.py:436
|
||||
msgid "Armenian "
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:383
|
||||
#: .\recipes\settings.py:437
|
||||
msgid "Bulgarian"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:384
|
||||
#: .\recipes\settings.py:438
|
||||
msgid "Catalan"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:385
|
||||
#: .\recipes\settings.py:439
|
||||
msgid "Czech"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:386
|
||||
#: .\recipes\settings.py:440
|
||||
msgid "Danish"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:387
|
||||
#: .\recipes\settings.py:441
|
||||
msgid "Dutch"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:388
|
||||
#: .\recipes\settings.py:442
|
||||
msgid "English"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:389
|
||||
#: .\recipes\settings.py:443
|
||||
msgid "French"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:390
|
||||
#: .\recipes\settings.py:444
|
||||
msgid "German"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:391
|
||||
#: .\recipes\settings.py:445
|
||||
msgid "Hungarian"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:446
|
||||
msgid "Italian"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:392
|
||||
#: .\recipes\settings.py:447
|
||||
msgid "Latvian"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:393
|
||||
#: .\recipes\settings.py:448
|
||||
msgid "Polish"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:394
|
||||
#: .\recipes\settings.py:449
|
||||
msgid "Russian"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:395
|
||||
#: .\recipes\settings.py:450
|
||||
msgid "Spanish"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:396
|
||||
#: .\recipes\settings.py:451
|
||||
msgid "Swedish"
|
||||
msgstr ""
|
||||
|
@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-01-19 19:14+0100\n"
|
||||
"POT-Creation-Date: 2023-04-26 07:46+0200\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"
|
||||
@ -18,62 +18,66 @@ msgstr ""
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
#: .\recipes\settings.py:382
|
||||
#: .\recipes\settings.py:436
|
||||
msgid "Armenian "
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:383
|
||||
#: .\recipes\settings.py:437
|
||||
msgid "Bulgarian"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:384
|
||||
#: .\recipes\settings.py:438
|
||||
msgid "Catalan"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:385
|
||||
#: .\recipes\settings.py:439
|
||||
msgid "Czech"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:386
|
||||
#: .\recipes\settings.py:440
|
||||
msgid "Danish"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:387
|
||||
#: .\recipes\settings.py:441
|
||||
msgid "Dutch"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:388
|
||||
#: .\recipes\settings.py:442
|
||||
msgid "English"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:389
|
||||
#: .\recipes\settings.py:443
|
||||
msgid "French"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:390
|
||||
#: .\recipes\settings.py:444
|
||||
msgid "German"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:391
|
||||
#: .\recipes\settings.py:445
|
||||
msgid "Hungarian"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:446
|
||||
msgid "Italian"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:392
|
||||
#: .\recipes\settings.py:447
|
||||
msgid "Latvian"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:393
|
||||
#: .\recipes\settings.py:448
|
||||
msgid "Polish"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:394
|
||||
#: .\recipes\settings.py:449
|
||||
msgid "Russian"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:395
|
||||
#: .\recipes\settings.py:450
|
||||
msgid "Spanish"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:396
|
||||
#: .\recipes\settings.py:451
|
||||
msgid "Swedish"
|
||||
msgstr ""
|
||||
|
@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-01-19 19:14+0100\n"
|
||||
"POT-Creation-Date: 2023-04-26 07:46+0200\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"
|
||||
@ -19,62 +19,66 @@ msgstr ""
|
||||
"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n != 0 ? 1 : "
|
||||
"2);\n"
|
||||
|
||||
#: .\recipes\settings.py:382
|
||||
#: .\recipes\settings.py:436
|
||||
msgid "Armenian "
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:383
|
||||
#: .\recipes\settings.py:437
|
||||
msgid "Bulgarian"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:384
|
||||
#: .\recipes\settings.py:438
|
||||
msgid "Catalan"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:385
|
||||
#: .\recipes\settings.py:439
|
||||
msgid "Czech"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:386
|
||||
#: .\recipes\settings.py:440
|
||||
msgid "Danish"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:387
|
||||
#: .\recipes\settings.py:441
|
||||
msgid "Dutch"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:388
|
||||
#: .\recipes\settings.py:442
|
||||
msgid "English"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:389
|
||||
#: .\recipes\settings.py:443
|
||||
msgid "French"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:390
|
||||
#: .\recipes\settings.py:444
|
||||
msgid "German"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:391
|
||||
#: .\recipes\settings.py:445
|
||||
msgid "Hungarian"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:446
|
||||
msgid "Italian"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:392
|
||||
#: .\recipes\settings.py:447
|
||||
msgid "Latvian"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:393
|
||||
#: .\recipes\settings.py:448
|
||||
msgid "Polish"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:394
|
||||
#: .\recipes\settings.py:449
|
||||
msgid "Russian"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:395
|
||||
#: .\recipes\settings.py:450
|
||||
msgid "Spanish"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:396
|
||||
#: .\recipes\settings.py:451
|
||||
msgid "Swedish"
|
||||
msgstr ""
|
||||
|
@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-01-19 19:14+0100\n"
|
||||
"POT-Creation-Date: 2023-04-26 07:46+0200\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"
|
||||
@ -18,62 +18,66 @@ msgstr ""
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
#: .\recipes\settings.py:382
|
||||
#: .\recipes\settings.py:436
|
||||
msgid "Armenian "
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:383
|
||||
#: .\recipes\settings.py:437
|
||||
msgid "Bulgarian"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:384
|
||||
#: .\recipes\settings.py:438
|
||||
msgid "Catalan"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:385
|
||||
#: .\recipes\settings.py:439
|
||||
msgid "Czech"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:386
|
||||
#: .\recipes\settings.py:440
|
||||
msgid "Danish"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:387
|
||||
#: .\recipes\settings.py:441
|
||||
msgid "Dutch"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:388
|
||||
#: .\recipes\settings.py:442
|
||||
msgid "English"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:389
|
||||
#: .\recipes\settings.py:443
|
||||
msgid "French"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:390
|
||||
#: .\recipes\settings.py:444
|
||||
msgid "German"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:391
|
||||
#: .\recipes\settings.py:445
|
||||
msgid "Hungarian"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:446
|
||||
msgid "Italian"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:392
|
||||
#: .\recipes\settings.py:447
|
||||
msgid "Latvian"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:393
|
||||
#: .\recipes\settings.py:448
|
||||
msgid "Polish"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:394
|
||||
#: .\recipes\settings.py:449
|
||||
msgid "Russian"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:395
|
||||
#: .\recipes\settings.py:450
|
||||
msgid "Spanish"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:396
|
||||
#: .\recipes\settings.py:451
|
||||
msgid "Swedish"
|
||||
msgstr ""
|
||||
|
@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-01-19 19:14+0100\n"
|
||||
"POT-Creation-Date: 2023-04-26 07:46+0200\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"
|
||||
@ -18,62 +18,66 @@ msgstr ""
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
#: .\recipes\settings.py:382
|
||||
#: .\recipes\settings.py:436
|
||||
msgid "Armenian "
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:383
|
||||
#: .\recipes\settings.py:437
|
||||
msgid "Bulgarian"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:384
|
||||
#: .\recipes\settings.py:438
|
||||
msgid "Catalan"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:385
|
||||
#: .\recipes\settings.py:439
|
||||
msgid "Czech"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:386
|
||||
#: .\recipes\settings.py:440
|
||||
msgid "Danish"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:387
|
||||
#: .\recipes\settings.py:441
|
||||
msgid "Dutch"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:388
|
||||
#: .\recipes\settings.py:442
|
||||
msgid "English"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:389
|
||||
#: .\recipes\settings.py:443
|
||||
msgid "French"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:390
|
||||
#: .\recipes\settings.py:444
|
||||
msgid "German"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:391
|
||||
#: .\recipes\settings.py:445
|
||||
msgid "Hungarian"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:446
|
||||
msgid "Italian"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:392
|
||||
#: .\recipes\settings.py:447
|
||||
msgid "Latvian"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:393
|
||||
#: .\recipes\settings.py:448
|
||||
msgid "Polish"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:394
|
||||
#: .\recipes\settings.py:449
|
||||
msgid "Russian"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:395
|
||||
#: .\recipes\settings.py:450
|
||||
msgid "Spanish"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:396
|
||||
#: .\recipes\settings.py:451
|
||||
msgid "Swedish"
|
||||
msgstr ""
|
||||
|
@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-01-19 19:14+0100\n"
|
||||
"POT-Creation-Date: 2023-04-26 07:46+0200\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"
|
||||
@ -17,62 +17,66 @@ msgstr ""
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
||||
#: .\recipes\settings.py:382
|
||||
#: .\recipes\settings.py:436
|
||||
msgid "Armenian "
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:383
|
||||
#: .\recipes\settings.py:437
|
||||
msgid "Bulgarian"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:384
|
||||
#: .\recipes\settings.py:438
|
||||
msgid "Catalan"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:385
|
||||
#: .\recipes\settings.py:439
|
||||
msgid "Czech"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:386
|
||||
#: .\recipes\settings.py:440
|
||||
msgid "Danish"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:387
|
||||
#: .\recipes\settings.py:441
|
||||
msgid "Dutch"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:388
|
||||
#: .\recipes\settings.py:442
|
||||
msgid "English"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:389
|
||||
#: .\recipes\settings.py:443
|
||||
msgid "French"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:390
|
||||
#: .\recipes\settings.py:444
|
||||
msgid "German"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:391
|
||||
#: .\recipes\settings.py:445
|
||||
msgid "Hungarian"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:446
|
||||
msgid "Italian"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:392
|
||||
#: .\recipes\settings.py:447
|
||||
msgid "Latvian"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:393
|
||||
#: .\recipes\settings.py:448
|
||||
msgid "Polish"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:394
|
||||
#: .\recipes\settings.py:449
|
||||
msgid "Russian"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:395
|
||||
#: .\recipes\settings.py:450
|
||||
msgid "Spanish"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:396
|
||||
#: .\recipes\settings.py:451
|
||||
msgid "Swedish"
|
||||
msgstr ""
|
||||
|
@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-01-19 19:14+0100\n"
|
||||
"POT-Creation-Date: 2023-04-26 07:46+0200\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"
|
||||
@ -18,62 +18,66 @@ msgstr ""
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
|
||||
|
||||
#: .\recipes\settings.py:382
|
||||
#: .\recipes\settings.py:436
|
||||
msgid "Armenian "
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:383
|
||||
#: .\recipes\settings.py:437
|
||||
msgid "Bulgarian"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:384
|
||||
#: .\recipes\settings.py:438
|
||||
msgid "Catalan"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:385
|
||||
#: .\recipes\settings.py:439
|
||||
msgid "Czech"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:386
|
||||
#: .\recipes\settings.py:440
|
||||
msgid "Danish"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:387
|
||||
#: .\recipes\settings.py:441
|
||||
msgid "Dutch"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:388
|
||||
#: .\recipes\settings.py:442
|
||||
msgid "English"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:389
|
||||
#: .\recipes\settings.py:443
|
||||
msgid "French"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:390
|
||||
#: .\recipes\settings.py:444
|
||||
msgid "German"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:391
|
||||
#: .\recipes\settings.py:445
|
||||
msgid "Hungarian"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:446
|
||||
msgid "Italian"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:392
|
||||
#: .\recipes\settings.py:447
|
||||
msgid "Latvian"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:393
|
||||
#: .\recipes\settings.py:448
|
||||
msgid "Polish"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:394
|
||||
#: .\recipes\settings.py:449
|
||||
msgid "Russian"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:395
|
||||
#: .\recipes\settings.py:450
|
||||
msgid "Spanish"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:396
|
||||
#: .\recipes\settings.py:451
|
||||
msgid "Swedish"
|
||||
msgstr ""
|
||||
|
@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2023-01-19 19:14+0100\n"
|
||||
"POT-Creation-Date: 2023-04-26 07:46+0200\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"
|
||||
@ -17,62 +17,66 @@ msgstr ""
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
||||
#: .\recipes\settings.py:382
|
||||
#: .\recipes\settings.py:436
|
||||
msgid "Armenian "
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:383
|
||||
#: .\recipes\settings.py:437
|
||||
msgid "Bulgarian"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:384
|
||||
#: .\recipes\settings.py:438
|
||||
msgid "Catalan"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:385
|
||||
#: .\recipes\settings.py:439
|
||||
msgid "Czech"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:386
|
||||
#: .\recipes\settings.py:440
|
||||
msgid "Danish"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:387
|
||||
#: .\recipes\settings.py:441
|
||||
msgid "Dutch"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:388
|
||||
#: .\recipes\settings.py:442
|
||||
msgid "English"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:389
|
||||
#: .\recipes\settings.py:443
|
||||
msgid "French"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:390
|
||||
#: .\recipes\settings.py:444
|
||||
msgid "German"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:391
|
||||
#: .\recipes\settings.py:445
|
||||
msgid "Hungarian"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:446
|
||||
msgid "Italian"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:392
|
||||
#: .\recipes\settings.py:447
|
||||
msgid "Latvian"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:393
|
||||
#: .\recipes\settings.py:448
|
||||
msgid "Polish"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:394
|
||||
#: .\recipes\settings.py:449
|
||||
msgid "Russian"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:395
|
||||
#: .\recipes\settings.py:450
|
||||
msgid "Spanish"
|
||||
msgstr ""
|
||||
|
||||
#: .\recipes\settings.py:396
|
||||
#: .\recipes\settings.py:451
|
||||
msgid "Swedish"
|
||||
msgstr ""
|
||||
|
@ -24,7 +24,8 @@ load_dotenv()
|
||||
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
|
||||
# Get vars from .env files
|
||||
SECRET_KEY = os.getenv('SECRET_KEY') if os.getenv('SECRET_KEY') else 'INSECURE_STANDARD_KEY_SET_IN_ENV'
|
||||
SECRET_KEY = os.getenv('SECRET_KEY') if os.getenv(
|
||||
'SECRET_KEY') else 'INSECURE_STANDARD_KEY_SET_IN_ENV'
|
||||
|
||||
DEBUG = bool(int(os.getenv('DEBUG', True)))
|
||||
DEBUG_TOOLBAR = bool(int(os.getenv('DEBUG_TOOLBAR', True)))
|
||||
@ -35,9 +36,11 @@ SOCIAL_DEFAULT_GROUP = os.getenv('SOCIAL_DEFAULT_GROUP', 'guest')
|
||||
SPACE_DEFAULT_MAX_RECIPES = int(os.getenv('SPACE_DEFAULT_MAX_RECIPES', 0))
|
||||
SPACE_DEFAULT_MAX_USERS = int(os.getenv('SPACE_DEFAULT_MAX_USERS', 0))
|
||||
SPACE_DEFAULT_MAX_FILES = int(os.getenv('SPACE_DEFAULT_MAX_FILES', 0))
|
||||
SPACE_DEFAULT_ALLOW_SHARING = bool(int(os.getenv('SPACE_DEFAULT_ALLOW_SHARING', True)))
|
||||
SPACE_DEFAULT_ALLOW_SHARING = bool(
|
||||
int(os.getenv('SPACE_DEFAULT_ALLOW_SHARING', True)))
|
||||
|
||||
INTERNAL_IPS = os.getenv('INTERNAL_IPS').split(',') if os.getenv('INTERNAL_IPS') else ['127.0.0.1']
|
||||
INTERNAL_IPS = os.getenv('INTERNAL_IPS').split(
|
||||
',') if os.getenv('INTERNAL_IPS') else ['127.0.0.1']
|
||||
|
||||
# allow djangos wsgi server to server mediafiles
|
||||
GUNICORN_MEDIA = bool(int(os.getenv('GUNICORN_MEDIA', True)))
|
||||
@ -51,9 +54,11 @@ KJ_PREF_DEFAULT = bool(int(os.getenv('KJ_PREF_DEFAULT', False)))
|
||||
STICKY_NAV_PREF_DEFAULT = bool(int(os.getenv('STICKY_NAV_PREF_DEFAULT', True)))
|
||||
|
||||
# minimum interval that users can set for automatic sync of shopping lists
|
||||
SHOPPING_MIN_AUTOSYNC_INTERVAL = int(os.getenv('SHOPPING_MIN_AUTOSYNC_INTERVAL', 5))
|
||||
SHOPPING_MIN_AUTOSYNC_INTERVAL = int(
|
||||
os.getenv('SHOPPING_MIN_AUTOSYNC_INTERVAL', 5))
|
||||
|
||||
ALLOWED_HOSTS = os.getenv('ALLOWED_HOSTS').split(',') if os.getenv('ALLOWED_HOSTS') else ['*']
|
||||
ALLOWED_HOSTS = os.getenv('ALLOWED_HOSTS').split(
|
||||
',') if os.getenv('ALLOWED_HOSTS') else ['*']
|
||||
|
||||
if os.getenv('CSRF_TRUSTED_ORIGINS'):
|
||||
CSRF_TRUSTED_ORIGINS = os.getenv('CSRF_TRUSTED_ORIGINS').split(',')
|
||||
@ -131,7 +136,8 @@ try:
|
||||
plugin_module = f'recipes.plugins.{d}.apps.{app_config_classname}'
|
||||
if plugin_module not in INSTALLED_APPS:
|
||||
INSTALLED_APPS.append(plugin_module)
|
||||
plugin_class = getattr(sys.modules[apps_path], app_config_classname)
|
||||
plugin_class = getattr(
|
||||
sys.modules[apps_path], app_config_classname)
|
||||
plugin_config = {
|
||||
'name': plugin_class.verbose_name if hasattr(plugin_class, 'verbose_name') else plugin_class.name,
|
||||
'module': f'recipes.plugins.{d}',
|
||||
@ -148,7 +154,8 @@ except Exception:
|
||||
if DEBUG:
|
||||
print('ERROR failed to initialize plugins')
|
||||
|
||||
SOCIAL_PROVIDERS = os.getenv('SOCIAL_PROVIDERS').split(',') if os.getenv('SOCIAL_PROVIDERS') else []
|
||||
SOCIAL_PROVIDERS = os.getenv('SOCIAL_PROVIDERS').split(
|
||||
',') if os.getenv('SOCIAL_PROVIDERS') else []
|
||||
SOCIALACCOUNT_EMAIL_VERIFICATION = 'none'
|
||||
INSTALLED_APPS = INSTALLED_APPS + SOCIAL_PROVIDERS
|
||||
|
||||
@ -194,7 +201,8 @@ if DEBUG_TOOLBAR:
|
||||
INSTALLED_APPS += ('debug_toolbar',)
|
||||
|
||||
SORT_TREE_BY_NAME = bool(int(os.getenv('SORT_TREE_BY_NAME', False)))
|
||||
DISABLE_TREE_FIX_STARTUP = bool(int(os.getenv('DISABLE_TREE_FIX_STARTUP', False)))
|
||||
DISABLE_TREE_FIX_STARTUP = bool(
|
||||
int(os.getenv('DISABLE_TREE_FIX_STARTUP', False)))
|
||||
|
||||
if bool(int(os.getenv('SQL_DEBUG', False))):
|
||||
MIDDLEWARE += ('recipes.middleware.SqlPrintingMiddleware',)
|
||||
@ -213,6 +221,7 @@ if LDAP_AUTH:
|
||||
|
||||
AUTHENTICATION_BACKENDS.append('django_auth_ldap.backend.LDAPBackend')
|
||||
AUTH_LDAP_SERVER_URI = os.getenv('AUTH_LDAP_SERVER_URI')
|
||||
AUTH_LDAP_START_TLS = bool(int(os.getenv('AUTH_LDAP_START_TLS', False)))
|
||||
AUTH_LDAP_BIND_DN = os.getenv('AUTH_LDAP_BIND_DN')
|
||||
AUTH_LDAP_BIND_PASSWORD = os.getenv('AUTH_LDAP_BIND_PASSWORD')
|
||||
AUTH_LDAP_USER_SEARCH = LDAPSearch(
|
||||
@ -225,10 +234,12 @@ if LDAP_AUTH:
|
||||
'last_name': 'sn',
|
||||
'email': 'mail',
|
||||
}
|
||||
AUTH_LDAP_ALWAYS_UPDATE_USER = bool(int(os.getenv('AUTH_LDAP_ALWAYS_UPDATE_USER', True)))
|
||||
AUTH_LDAP_ALWAYS_UPDATE_USER = bool(
|
||||
int(os.getenv('AUTH_LDAP_ALWAYS_UPDATE_USER', True)))
|
||||
AUTH_LDAP_CACHE_TIMEOUT = int(os.getenv('AUTH_LDAP_CACHE_TIMEOUT', 3600))
|
||||
if 'AUTH_LDAP_TLS_CACERTFILE' in os.environ:
|
||||
AUTH_LDAP_GLOBAL_OPTIONS = {ldap.OPT_X_TLS_CACERTFILE: os.getenv('AUTH_LDAP_TLS_CACERTFILE')}
|
||||
AUTH_LDAP_GLOBAL_OPTIONS = {
|
||||
ldap.OPT_X_TLS_CACERTFILE: os.getenv('AUTH_LDAP_TLS_CACERTFILE')}
|
||||
if DEBUG:
|
||||
LOGGING = {
|
||||
"version": 1,
|
||||
@ -249,7 +260,8 @@ ACCOUNT_ADAPTER = 'cookbook.helper.AllAuthCustomAdapter'
|
||||
|
||||
if REVERSE_PROXY_AUTH:
|
||||
MIDDLEWARE.insert(8, 'recipes.middleware.CustomRemoteUser')
|
||||
AUTHENTICATION_BACKENDS.append('django.contrib.auth.backends.RemoteUserBackend')
|
||||
AUTHENTICATION_BACKENDS.append(
|
||||
'django.contrib.auth.backends.RemoteUserBackend')
|
||||
|
||||
# Password validation
|
||||
# https://docs.djangoproject.com/en/2.0/ref/settings/#auth-password-validators
|
||||
@ -444,7 +456,8 @@ LANGUAGES = [
|
||||
|
||||
SCRIPT_NAME = os.getenv('SCRIPT_NAME', '')
|
||||
# path for django_js_reverse to generate the javascript file containing all urls. Only done because the default command (collectstatic_js_reverse) fails to update the manifest
|
||||
JS_REVERSE_OUTPUT_PATH = os.path.join(BASE_DIR, "cookbook/static/django_js_reverse")
|
||||
JS_REVERSE_OUTPUT_PATH = os.path.join(
|
||||
BASE_DIR, "cookbook/static/django_js_reverse")
|
||||
JS_REVERSE_SCRIPT_PREFIX = os.getenv('JS_REVERSE_SCRIPT_PREFIX', SCRIPT_NAME)
|
||||
|
||||
STATIC_URL = os.getenv('STATIC_URL', '/static/')
|
||||
@ -499,4 +512,5 @@ EMAIL_HOST_PASSWORD = os.getenv('EMAIL_HOST_PASSWORD', '')
|
||||
EMAIL_USE_TLS = bool(int(os.getenv('EMAIL_USE_TLS', False)))
|
||||
EMAIL_USE_SSL = bool(int(os.getenv('EMAIL_USE_SSL', False)))
|
||||
DEFAULT_FROM_EMAIL = os.getenv('DEFAULT_FROM_EMAIL', 'webmaster@localhost')
|
||||
ACCOUNT_EMAIL_SUBJECT_PREFIX = os.getenv('ACCOUNT_EMAIL_SUBJECT_PREFIX', '[Tandoor Recipes] ') # allauth sender prefix
|
||||
ACCOUNT_EMAIL_SUBJECT_PREFIX = os.getenv(
|
||||
'ACCOUNT_EMAIL_SUBJECT_PREFIX', '[Tandoor Recipes] ') # allauth sender prefix
|
||||
|
@ -29,12 +29,12 @@ microdata==0.8.0
|
||||
Jinja2==3.1.2
|
||||
django-webpack-loader==1.8.1
|
||||
git+https://github.com/BITSOLVER/django-js-reverse@071e304fd600107bc64bbde6f2491f1fe049ec82
|
||||
django-allauth==0.52.0
|
||||
recipe-scrapers==14.35.0
|
||||
django-allauth==0.54.0
|
||||
recipe-scrapers==14.36.1
|
||||
django-scopes==1.2.0.post1
|
||||
pytest==7.2.2
|
||||
pytest==7.3.1
|
||||
pytest-django==4.5.2
|
||||
django-treebeard==4.5.1
|
||||
django-treebeard==4.7
|
||||
django-cors-headers==3.13.0
|
||||
django-storages==1.13.2
|
||||
boto3==1.26.41
|
||||
@ -42,7 +42,7 @@ django-prometheus==2.2.0
|
||||
django-hCaptcha==0.2.0
|
||||
python-ldap==3.4.3
|
||||
django-auth-ldap==4.2.0
|
||||
pytest-factoryboy==2.5.0
|
||||
pytest-factoryboy==2.5.1
|
||||
pyppeteer==1.0.2
|
||||
validators==0.20.0
|
||||
pytube==12.1.0
|
||||
|
@ -12,18 +12,17 @@
|
||||
<i class="far fa-check-circle text-primary" v-if="!ingredient.checked"></i>
|
||||
</td>
|
||||
<td class="text-nowrap" @click="done">
|
||||
<span v-if="ingredient.amount !== 0 && !ingredient.no_amount"
|
||||
v-html="calculateAmount(ingredient.amount)"></span>
|
||||
<span v-if="ingredient.amount !== 0 && !ingredient.no_amount" v-html="calculateAmount(ingredient.amount)"></span>
|
||||
</td>
|
||||
<td @click="done">
|
||||
<template v-if="ingredient.unit !== null && !ingredient.no_amount">
|
||||
<template >
|
||||
<template>
|
||||
<template v-if="ingredient.unit.plural_name === '' || ingredient.unit.plural_name === null">
|
||||
<span>{{ ingredient.unit.name }}</span>
|
||||
</template>
|
||||
<template v-else>
|
||||
<span v-if="ingredient.always_use_plural_unit">{{ ingredient.unit.plural_name}}</span>
|
||||
<span v-else-if="(ingredient.amount * this.ingredient_factor) > 1">{{ ingredient.unit.plural_name }}</span>
|
||||
<span v-if="ingredient.always_use_plural_unit">{{ ingredient.unit.plural_name }}</span>
|
||||
<span v-else-if="ingredient.amount * this.ingredient_factor > 1">{{ ingredient.unit.plural_name }}</span>
|
||||
<span v-else>{{ ingredient.unit.name }}</span>
|
||||
</template>
|
||||
</template>
|
||||
@ -31,11 +30,10 @@
|
||||
</td>
|
||||
<td @click="done">
|
||||
<template v-if="ingredient.food !== null">
|
||||
<a :href="resolveDjangoUrl('view_recipe', ingredient.food.recipe.id)"
|
||||
v-if="ingredient.food.recipe !== null" target="_blank"
|
||||
rel="noopener noreferrer">{{ ingredient.food.name }}</a>
|
||||
<a :href="resolveDjangoUrl('view_recipe', ingredient.food.recipe.id)" v-if="ingredient.food.recipe !== null" target="_blank" rel="noopener noreferrer">{{
|
||||
ingredient.food.name
|
||||
}}</a>
|
||||
<template v-if="ingredient.food.recipe === null">
|
||||
|
||||
<template>
|
||||
<template v-if="ingredient.food.plural_name === '' || ingredient.food.plural_name === null">
|
||||
<span>{{ ingredient.food.name }}</span>
|
||||
@ -43,7 +41,7 @@
|
||||
<template v-else>
|
||||
<span v-if="ingredient.always_use_plural_food">{{ ingredient.food.plural_name }}</span>
|
||||
<span v-else-if="ingredient.no_amount">{{ ingredient.food.name }}</span>
|
||||
<span v-else-if="(ingredient.amount * this.ingredient_factor) > 1">{{ ingredient.food.plural_name }}</span>
|
||||
<span v-else-if="ingredient.amount * this.ingredient_factor > 1">{{ ingredient.food.plural_name }}</span>
|
||||
<span v-else>{{ ingredient.food.name }}</span>
|
||||
</template>
|
||||
</template>
|
||||
@ -51,35 +49,32 @@
|
||||
</template>
|
||||
</td>
|
||||
<td v-if="detailed">
|
||||
<div v-if="ingredient.note">
|
||||
<span v-b-popover.hover="ingredient.note" class="d-print-none touchable p-0 pl-md-2 pr-md-2">
|
||||
<template v-if="ingredient.note">
|
||||
<span v-b-popover.hover="ingredient.note" class="d-print-none touchable py-0 px-2">
|
||||
<i class="far fa-comment"></i>
|
||||
</span>
|
||||
|
||||
<div class="d-none d-print-block"><i class="far fa-comment-alt d-print-none"></i> {{
|
||||
ingredient.note
|
||||
}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-none d-print-block"><i class="far fa-comment-alt d-print-none"></i> {{ ingredient.note }}</div>
|
||||
</template>
|
||||
</td>
|
||||
</template>
|
||||
</tr>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {calculateAmount, ResolveUrlMixin} from "@/utils/utils"
|
||||
import { calculateAmount, ResolveUrlMixin } from "@/utils/utils"
|
||||
|
||||
import Vue from "vue"
|
||||
import VueSanitize from "vue-sanitize";
|
||||
import VueSanitize from "vue-sanitize"
|
||||
|
||||
Vue.use(VueSanitize);
|
||||
Vue.use(VueSanitize)
|
||||
|
||||
export default {
|
||||
name: "IngredientComponent",
|
||||
props: {
|
||||
ingredient: Object,
|
||||
ingredient_factor: {type: Number, default: 1},
|
||||
detailed: {type: Boolean, default: true},
|
||||
ingredient_factor: { type: Number, default: 1 },
|
||||
detailed: { type: Boolean, default: true },
|
||||
},
|
||||
mixins: [ResolveUrlMixin],
|
||||
data() {
|
||||
@ -88,9 +83,7 @@ export default {
|
||||
}
|
||||
},
|
||||
watch: {},
|
||||
mounted() {
|
||||
|
||||
},
|
||||
mounted() {},
|
||||
methods: {
|
||||
calculateAmount: function (x) {
|
||||
return this.$sanitize(calculateAmount(x, this.ingredient_factor))
|
||||
@ -106,9 +99,9 @@ export default {
|
||||
<style scoped>
|
||||
/* increase size of hover/touchable space without changing spacing */
|
||||
.touchable {
|
||||
padding-right: 2em;
|
||||
padding-left: 2em;
|
||||
margin-right: -2em;
|
||||
margin-left: -2em;
|
||||
/* padding-right: 2em;
|
||||
padding-left: 2em; */
|
||||
margin-right: -1em;
|
||||
margin-left: -1em;
|
||||
}
|
||||
</style>
|
||||
|
@ -72,7 +72,7 @@
|
||||
"Cancel": "Abbrechen",
|
||||
"success_deleting_resource": "Ressource erfolgreich gelöscht!",
|
||||
"Load_More": "Weitere laden",
|
||||
"Ok": "Öffnen",
|
||||
"Ok": "Ok",
|
||||
"Link": "Link",
|
||||
"Key_Ctrl": "Strg",
|
||||
"move_title": "{type} verschieben",
|
||||
@ -114,7 +114,7 @@
|
||||
"Create_New_Shopping Category": "Neue Einkaufskategorie erstellen",
|
||||
"Automate": "Automatisieren",
|
||||
"Type": "Typ",
|
||||
"and_up": "& Höher",
|
||||
"and_up": "& Hoch",
|
||||
"Unrated": "Unbewertet",
|
||||
"Shopping_list": "Einkaufsliste",
|
||||
"step_time_minutes": "Schritt Dauer in Minuten",
|
||||
@ -206,7 +206,7 @@
|
||||
"New_Cookbook": "Neues Kochbuch",
|
||||
"Coming_Soon": "Bald verfügbar",
|
||||
"Auto_Planner": "Smart Planen",
|
||||
"Hide_Keyword": "Keywords schließen",
|
||||
"Hide_Keyword": "Schlüsselwörter verbergen",
|
||||
"Clear": "Leeren",
|
||||
"GroupBy": "Gruppieren nach",
|
||||
"IgnoreThis": "Füge {food} nie automatisch zur Einkaufsliste hinzu",
|
||||
@ -222,11 +222,11 @@
|
||||
"NoCategory": "Keine Kategorie ausgewählt.",
|
||||
"ShowDelayed": "Zeige verschobene Elemente",
|
||||
"Completed": "Fertig",
|
||||
"OfflineAlert": "Du bist offline, deine Einkaufsliste wird nicht synchronisiert.",
|
||||
"OfflineAlert": "Du bist offline. Deine Einkaufsliste wird nicht synchronisiert.",
|
||||
"shopping_share": "Einkaufsliste teilen",
|
||||
"mealplan_autoadd_shopping": "Automatisches Hinzufügen zum Essensplan",
|
||||
"mealplan_autoexclude_onhand": "Ignoriere vorrätige Zutaten",
|
||||
"mealplan_autoinclude_related": "Füge verwandte Rezepte hinzu",
|
||||
"mealplan_autoinclude_related": "Ähnliche Rezepte hinzufügen",
|
||||
"default_delay": "Standard-Verzögerungszeit",
|
||||
"Added_by": "Hinzugefügt durch",
|
||||
"AddToShopping": "Zur Einkaufsliste hinzufügen",
|
||||
@ -241,7 +241,7 @@
|
||||
"IngredientInShopping": "Diese Zutat befindet sich auf Ihrer Einkaufsliste.",
|
||||
"NotInShopping": "{food} befindet sich nicht auf Ihrer Einkaufsliste.",
|
||||
"OnHand": "Aktuell vorrätig",
|
||||
"FoodNotOnHand": "Sie habe {food} nicht vorrätig.",
|
||||
"FoodNotOnHand": "Sie haben {food} nicht vorrätig.",
|
||||
"Undefined": "undefiniert",
|
||||
"AddFoodToShopping": "Fügen Sie {food} zur Einkaufsliste hinzu",
|
||||
"RemoveFoodFromShopping": "{food} von der Einkaufsliste löschen",
|
||||
@ -251,23 +251,23 @@
|
||||
"mealplan_autoadd_shopping_desc": "Zutaten aus dem Essensplan automatisch zur Einkaufsliste hinzufügen.",
|
||||
"Pin": "Anheften",
|
||||
"mark_complete": "Vollständig markieren",
|
||||
"shopping_add_onhand_desc": "Markiere Lebensmittel als \"Vorrätig\", wenn von der Einkaufsliste abgehakt wurden.",
|
||||
"shopping_add_onhand_desc": "Zutat beim Abhaken auf der Einkausfliste als \"vorrätig\" kennzeichnen.",
|
||||
"left_handed": "Linkshänder-Modus",
|
||||
"left_handed_help": "Optimiert die Benutzeroberfläche für die Bedienung mit der linken Hand.",
|
||||
"FoodInherit": "Lebensmittel vererbbare Felder",
|
||||
"SupermarketCategoriesOnly": "Nur Supermarktkategorien",
|
||||
"InheritWarning": "{food} ist auf Vererbung gesetzt ist, Änderungen werden möglicherweise nicht gespeichert.",
|
||||
"mealplan_autoexclude_onhand_desc": "Beim (manuellen oder automatischen) Hinzufügen eines Essensplans zur Einkaufsliste vorrätige Zutagen ausnehmen.",
|
||||
"InheritWarning": "{food} ist auf Vererbung gesetzt, Änderungen werden möglicherweise nicht gespeichert.",
|
||||
"mealplan_autoexclude_onhand_desc": "Wenn ein Speiseplan zur Einkaufsliste zugefügt wird (manuell oder automatisch), Zutaten ausschliessen, die gerade vorrätig sind.",
|
||||
"mealplan_autoinclude_related_desc": "Wenn Sie einen Essensplan zur Einkaufsliste hinzufügen (manuell oder automatisch), fügen Sie alle zugehörigen Rezepte hinzu.",
|
||||
"default_delay_desc": "Voreingestellte Anzahl von Stunden für die Verzögerung eines Einkaufslisteneintrags.",
|
||||
"filter_to_supermarket": "Nach Supermarkt filtern",
|
||||
"err_move_self": "Element kann nicht auf sich selbst verschoben werden",
|
||||
"nothing": "Nichts zu tun",
|
||||
"err_merge_self": "Element kann nicht mit sich selbst zusammengeführt werden",
|
||||
"show_sql": "SQL anzeigen",
|
||||
"show_sql": "Zeige SQL",
|
||||
"filter_to_supermarket_desc": "Standardmäßig wird die Einkaufsliste so gefiltert, dass sie nur Kategorien für den ausgewählten Supermarkt enthält.",
|
||||
"CategoryName": "Kategorie Name",
|
||||
"SupermarketName": "Supermarkt Name",
|
||||
"CategoryName": "Kategorienname",
|
||||
"SupermarketName": "Name Supermarkt",
|
||||
"CategoryInstruction": "Ziehen Sie Kategorien, um die Reihenfolge zu ändern, in der die Kategorien in der Einkaufsliste erscheinen.",
|
||||
"shopping_recent_days_desc": "Tage der letzten Einträge in der Einkaufsliste, die angezeigt werden sollen.",
|
||||
"shopping_recent_days": "Letzte Tage",
|
||||
@ -277,7 +277,7 @@
|
||||
"csv_delim_help": "Trennzeichen für CSV-Exporte.",
|
||||
"csv_delim_label": "CSV-Trennzeichen",
|
||||
"SuccessClipboard": "Einkaufsliste wurde in die Zwischenablage kopiert",
|
||||
"copy_to_clipboard": "In die Zwischenablage kopieren",
|
||||
"copy_to_clipboard": "In Zwischenablage kopieren",
|
||||
"csv_prefix_help": "Präfix, das beim Kopieren der Liste in die Zwischenablage hinzugefügt wird.",
|
||||
"csv_prefix_label": "Listenpräfix",
|
||||
"copy_markdown_table": "Als Markdown-Tabelle kopieren",
|
||||
@ -291,10 +291,10 @@
|
||||
"remember_search": "Suchbegriff merken",
|
||||
"remember_hours": "Stunden zu erinnern",
|
||||
"tree_select": "Baum-Auswahl verwenden",
|
||||
"CountMore": "...+{count} weitere",
|
||||
"ignore_shopping_help": "Füge Zutat nie zur Einkaufsliste hinzu (z.B. Wasser)",
|
||||
"CountMore": "...+{count} mehr",
|
||||
"ignore_shopping_help": "Zutat nie auf Einkaufsliste setzen (z.B. Wasser)",
|
||||
"OnHand_help": "Lebensmittel ist \"Vorrätig\" und wird nicht automatisch zur Einkaufsliste hinzugefügt. Der Status \"Vorrätig\" wird mit den Benutzern der Einkaufsliste geteilt.",
|
||||
"shopping_category_help": "Supermärkte können nach Einkaufskategorien geordnet und gefiltert werden, je nachdem, wie die Gänge angeordnet sind.",
|
||||
"shopping_category_help": "Einkaufsläden können nach Produktkategorie entsprechend der Anordnung der Regalreihen sortiert werden.",
|
||||
"Foods": "Lebensmittel",
|
||||
"food_recipe_help": "Wird ein Rezept hier verknüpft, wird diese Verknüpfung in allen anderen Rezepten übernommen, die dieses Lebensmittel beinhaltet",
|
||||
"review_shopping": "Überprüfe die Einkaufsliste vor dem Speichern",
|
||||
@ -356,10 +356,10 @@
|
||||
"search_rank": "Such-Rang",
|
||||
"paste_ingredients": "Zutaten einfügen",
|
||||
"Ingredient Editor": "Zutateneditor",
|
||||
"Protected": "Geschützt",
|
||||
"Protected": "Schützen",
|
||||
"not": "nicht",
|
||||
"warning_duplicate_filter": "Warnung: Wegen technischen Limitierungen können mehrere Filter der selben Kombination (und/oder/nicht) zu unerwarteten Ergebnissen führen.",
|
||||
"and_down": "& Niedriger",
|
||||
"and_down": "& Runter",
|
||||
"enable_expert": "Expertenmodus aktivieren",
|
||||
"filter_name": "Name des Filters",
|
||||
"shared_with": "Geteilt mit",
|
||||
@ -407,11 +407,11 @@
|
||||
"Warning_Delete_Supermarket_Category": "Die Löschung einer Supermarktkategorie werden auch alle Beziehungen zu Lebensmitteln gelöscht. Bist du dir sicher?",
|
||||
"New_Supermarket": "Erstelle einen neuen Supermarkt",
|
||||
"New_Supermarket_Category": "Erstelle eine neue Supermarktkategorie",
|
||||
"warning_space_delete": "Du kannst deinen Bereich inklusive all deiner Rezepte, Einkaufslisten, Essensplänen und allem anderen, die du erstellt hast löschen. Dieser Schritt kann nicht rückgängig gemacht werden! Bist du sicher, dass du das tun möchtest?",
|
||||
"Copy Link": "Link kopieren",
|
||||
"warning_space_delete": "Du kannst deinen Space inklusive all deiner Rezepte, Shoppinglisten, Essensplänen und allem anderen, das du erstellt hast löschen. Dieser Schritt kann nicht rückgängig gemacht werden! Bist du sicher, dass du das tun möchtest?",
|
||||
"Copy Link": "Kopiere den Link in die Zwischenablage",
|
||||
"Users": "Benutzer",
|
||||
"facet_count_info": "Zeige die Anzahl der Rezepte auf den Suchfiltern.",
|
||||
"Copy Token": "Token kopieren",
|
||||
"Copy Token": "Kopiere Token",
|
||||
"Invites": "Einladungen",
|
||||
"Message": "Nachricht",
|
||||
"Bookmarklet": "Lesezeichen",
|
||||
@ -473,11 +473,13 @@
|
||||
"UnpinnedConfirmation": "{recipe} wurde gelöst.",
|
||||
"Description_Replace": "Beschreibung ersetzen",
|
||||
"Instruction_Replace": "Anleitung ersetzen",
|
||||
"Split_All_Steps": "Teile alle Zeilen in seperate Schritte auf.",
|
||||
"Split_All_Steps": "Teile alle Zeilen in separate Schritte auf.",
|
||||
"Auto_Sort_Help": "Verschiebe alle Zutaten zu dem Schritt, der am Besten passt.",
|
||||
"Combine_All_Steps": "Fasse alle Schritte in einem einzelnem Feld zusammen.",
|
||||
"reset_children_help": "Überschreibe alle Kinder mit den Werten der vererbten Felder. Die vererbten Felder der Kinder werden als vererbte Felder gesetzt, es sei denn, das Kind-Vererben-Feld ist gesetzt.",
|
||||
"Unpin": "Lösen",
|
||||
"Amount": "Menge",
|
||||
"Original_Text": "Originaltext"
|
||||
"Original_Text": "Originaler Text",
|
||||
"Import Recipe": "Rezept importieren",
|
||||
"Create Recipe": "Rezept erstellen"
|
||||
}
|
||||
|
@ -469,7 +469,7 @@
|
||||
"New_Supermarket_Category": "Create new supermarket category",
|
||||
"Are_You_Sure": "Are you sure?",
|
||||
"Valid Until": "Valid Until",
|
||||
"Split_All_Steps": "Split all rows into seperate steps.",
|
||||
"Split_All_Steps": "Split all rows into separate steps.",
|
||||
"Combine_All_Steps": "Combine all steps into a single field.",
|
||||
"Plural": "Plural",
|
||||
"plural_short": "plural",
|
||||
|
482
vue/src/locales/nb_NO.json
Normal file
482
vue/src/locales/nb_NO.json
Normal file
@ -0,0 +1,482 @@
|
||||
{
|
||||
"warning_feature_beta": "Denne funksjonen er foreløpig i BETA-versjon (testing). Regn med feil og at det i fremtidige oppdateringer kan komme endringer som gjør funksjonen ubrukelig.",
|
||||
"err_fetching_resource": "Feil ved henting av ressurs!",
|
||||
"err_creating_resource": "Feil ved oppretting av ressurs!",
|
||||
"err_updating_resource": "Feil ved oppdatering av ressurs!",
|
||||
"err_deleting_resource": "Feil ved sletting av ressurs!",
|
||||
"err_deleting_protected_resource": "Objektet du prøver å slette er fortsatt i bruk, og kan ikke slettes.",
|
||||
"err_moving_resource": "Feil ved flytting av ressurs!",
|
||||
"err_merging_resource": "Feil ved sammenslåing av ressurs!",
|
||||
"success_fetching_resource": "Vellykket henting av ressurs!",
|
||||
"success_creating_resource": "Vellykket oppretting av ressurs!",
|
||||
"success_updating_resource": "Vellykket oppdatering av ressurs!",
|
||||
"success_deleting_resource": "Vellykket sletting av ressurs!",
|
||||
"success_moving_resource": "Vellykket flytting av ressurs!",
|
||||
"success_merging_resource": "Vellykket sammenslåing av ressurs!",
|
||||
"file_upload_disabled": "Opplasting av filer er ikke aktivert i området ditt.",
|
||||
"warning_space_delete": "Du kan slette området, inkludert alle oppskrifter, handlelister, måltidsplaner og alt annet du har opprettet. Dette kan ikke angres! Er du sikker på at du vil gjøre dette?",
|
||||
"food_inherit_info": "Felter på matvarer som skal arves som standard.",
|
||||
"facet_count_info": "Vis oppskriftsantall i søkefilter.",
|
||||
"step_time_minutes": "Tid for trinn, i minutter",
|
||||
"confirm_delete": "Er du sikker på at du vil slette dette {object}?",
|
||||
"import_running": "Importering pågår. Vennligst vent!",
|
||||
"all_fields_optional": "Alle felt er valgfri, og kan stå tomme.",
|
||||
"convert_internal": "Konverter til intern oppskrift",
|
||||
"show_only_internal": "Vis bare interne oppskrifter",
|
||||
"show_split_screen": "Delt visning",
|
||||
"Log_Recipe_Cooking": "Logg oppskriftsbruk",
|
||||
"External_Recipe_Image": "Bilde av ekstern oppskrift",
|
||||
"Add_to_Shopping": "Legg til i handleliste",
|
||||
"Add_to_Plan": "Legg til i Plan",
|
||||
"Step_start_time": "Trinn starttid",
|
||||
"Sort_by_new": "Sorter etter nyest",
|
||||
"Table_of_Contents": "Innholdsfortegnelse",
|
||||
"Recipes_per_page": "Oppskrifter per side",
|
||||
"Show_as_header": "Vis som overskrift",
|
||||
"Hide_as_header": "Skjul overskrift",
|
||||
"Add_nutrition_recipe": "Legg til næringsinnhold til oppskrift",
|
||||
"Remove_nutrition_recipe": "Fjern næringsinnhold fra oppskrift",
|
||||
"Copy_template_reference": "Kopier mal-referanse",
|
||||
"Save_and_View": "Lagre og vis",
|
||||
"Manage_Books": "Administrer bøker",
|
||||
"Meal_Plan": "Måltidsplan",
|
||||
"Select_Book": "Velg bok",
|
||||
"Select_File": "Velg fil",
|
||||
"Recipe_Image": "Oppskriftsbilde",
|
||||
"Import_finished": "Importering fullført",
|
||||
"View_Recipes": "Vis oppskrifter",
|
||||
"Log_Cooking": "Loggfør tilbereding",
|
||||
"New_Recipe": "Ny oppskrift",
|
||||
"Url_Import": "Importer lenke",
|
||||
"Reset_Search": "Nullstill søk",
|
||||
"Recently_Viewed": "Nylig vist",
|
||||
"Load_More": "Last inn flere",
|
||||
"New_Keyword": "Nytt nøkkelord",
|
||||
"Delete_Keyword": "Slett nøkkelord",
|
||||
"Edit_Keyword": "Rediger nøkkelord",
|
||||
"Edit_Recipe": "Rediger oppskrift",
|
||||
"Move_Keyword": "Flytt nøkkelord",
|
||||
"Merge_Keyword": "Slå sammen nøkkelord",
|
||||
"Hide_Keywords": "Skjul nøkkelord",
|
||||
"Hide_Recipes": "Skjul oppskrifter",
|
||||
"Move_Up": "Flytt opp",
|
||||
"Move_Down": "Flytt ned",
|
||||
"Step_Name": "Trinn navn",
|
||||
"Step_Type": "Trinn type",
|
||||
"Make_Header": "Bruk som overskrift",
|
||||
"Make_Ingredient": "Bruk som ingrediens",
|
||||
"Amount": "Mengde",
|
||||
"Enable_Amount": "Aktiver mengde",
|
||||
"Disable_Amount": "Deaktiver mengde",
|
||||
"Ingredient Editor": "",
|
||||
"Description_Replace": "",
|
||||
"Instruction_Replace": "",
|
||||
"Auto_Sort": "",
|
||||
"Auto_Sort_Help": "",
|
||||
"Private_Recipe": "",
|
||||
"Private_Recipe_Help": "",
|
||||
"reusable_help_text": "",
|
||||
"Add_Step": "",
|
||||
"Keywords": "",
|
||||
"Books": "Bøker",
|
||||
"Proteins": "",
|
||||
"Fats": "",
|
||||
"Carbohydrates": "Karbohydrater",
|
||||
"Calories": "",
|
||||
"Energy": "",
|
||||
"Nutrition": "",
|
||||
"Date": "",
|
||||
"Share": "",
|
||||
"Automation": "",
|
||||
"Parameter": "",
|
||||
"Export": "",
|
||||
"Copy": "",
|
||||
"Rating": "Karakter",
|
||||
"Close": "Lukk",
|
||||
"Cancel": "",
|
||||
"Link": "Lenke",
|
||||
"Add": "",
|
||||
"New": "",
|
||||
"Note": "",
|
||||
"Success": "",
|
||||
"Failure": "",
|
||||
"Protected": "",
|
||||
"Ingredients": "Ingredienser",
|
||||
"Supermarket": "Butikk",
|
||||
"Categories": "",
|
||||
"Category": "",
|
||||
"Selected": "",
|
||||
"min": "",
|
||||
"Servings": "",
|
||||
"Waiting": "",
|
||||
"Preparation": "",
|
||||
"External": "",
|
||||
"Size": "",
|
||||
"Files": "",
|
||||
"File": "",
|
||||
"Edit": "",
|
||||
"Image": "",
|
||||
"Delete": "",
|
||||
"Open": "",
|
||||
"Ok": "",
|
||||
"Save": "",
|
||||
"Step": "",
|
||||
"Search": "",
|
||||
"Import": "",
|
||||
"Print": "",
|
||||
"Settings": "Innstillinger",
|
||||
"or": "",
|
||||
"and": "",
|
||||
"Information": "",
|
||||
"Download": "",
|
||||
"Create": "Opprett",
|
||||
"Search Settings": "",
|
||||
"View": "",
|
||||
"Recipes": "",
|
||||
"Move": "",
|
||||
"Merge": "",
|
||||
"Parent": "",
|
||||
"Copy Link": "",
|
||||
"Copy Token": "",
|
||||
"delete_confirmation": "",
|
||||
"move_confirmation": "",
|
||||
"merge_confirmation": "",
|
||||
"create_rule": "",
|
||||
"move_selection": "",
|
||||
"merge_selection": "",
|
||||
"Root": "",
|
||||
"Ignore_Shopping": "",
|
||||
"Shopping_Category": "",
|
||||
"Shopping_Categories": "",
|
||||
"Edit_Food": "",
|
||||
"Move_Food": "",
|
||||
"New_Food": "",
|
||||
"Hide_Food": "",
|
||||
"Food_Alias": "",
|
||||
"Unit_Alias": "",
|
||||
"Keyword_Alias": "",
|
||||
"Delete_Food": "",
|
||||
"No_ID": "",
|
||||
"Meal_Plan_Days": "",
|
||||
"merge_title": "",
|
||||
"move_title": "",
|
||||
"Food": "",
|
||||
"Original_Text": "",
|
||||
"Recipe_Book": "",
|
||||
"del_confirmation_tree": "",
|
||||
"delete_title": "",
|
||||
"create_title": "",
|
||||
"edit_title": "",
|
||||
"Name": "",
|
||||
"Type": "",
|
||||
"Description": "",
|
||||
"Recipe": "",
|
||||
"tree_root": "",
|
||||
"Icon": "",
|
||||
"Unit": "",
|
||||
"Decimals": "",
|
||||
"Default_Unit": "",
|
||||
"No_Results": "",
|
||||
"New_Unit": "",
|
||||
"Create_New_Shopping Category": "",
|
||||
"Create_New_Food": "",
|
||||
"Create_New_Keyword": "",
|
||||
"Create_New_Unit": "",
|
||||
"Create_New_Meal_Type": "",
|
||||
"Create_New_Shopping_Category": "",
|
||||
"and_up": "",
|
||||
"and_down": "",
|
||||
"Instructions": "",
|
||||
"Unrated": "",
|
||||
"Automate": "",
|
||||
"Empty": "",
|
||||
"Key_Ctrl": "",
|
||||
"Key_Shift": "",
|
||||
"Time": "",
|
||||
"Text": "",
|
||||
"Shopping_list": "",
|
||||
"Added_by": "",
|
||||
"Added_on": "",
|
||||
"AddToShopping": "",
|
||||
"IngredientInShopping": "",
|
||||
"NotInShopping": "",
|
||||
"OnHand": "",
|
||||
"FoodOnHand": "",
|
||||
"FoodNotOnHand": "",
|
||||
"Undefined": "",
|
||||
"Create_Meal_Plan_Entry": "",
|
||||
"Edit_Meal_Plan_Entry": "",
|
||||
"Title": "",
|
||||
"Week": "",
|
||||
"Month": "",
|
||||
"Year": "",
|
||||
"Planner": "",
|
||||
"Planner_Settings": "",
|
||||
"Period": "",
|
||||
"Plan_Period_To_Show": "",
|
||||
"Periods": "",
|
||||
"Plan_Show_How_Many_Periods": "",
|
||||
"Starting_Day": "",
|
||||
"Meal_Types": "",
|
||||
"Meal_Type": "",
|
||||
"New_Entry": "",
|
||||
"Clone": "",
|
||||
"Drag_Here_To_Delete": "",
|
||||
"Meal_Type_Required": "",
|
||||
"Title_or_Recipe_Required": "",
|
||||
"Color": "",
|
||||
"New_Meal_Type": "",
|
||||
"Use_Fractions": "",
|
||||
"Use_Fractions_Help": "",
|
||||
"AddFoodToShopping": "",
|
||||
"RemoveFoodFromShopping": "",
|
||||
"DeleteShoppingConfirm": "",
|
||||
"IgnoredFood": "",
|
||||
"Add_Servings_to_Shopping": "",
|
||||
"Week_Numbers": "",
|
||||
"Show_Week_Numbers": "",
|
||||
"Export_As_ICal": "",
|
||||
"Export_To_ICal": "",
|
||||
"Cannot_Add_Notes_To_Shopping": "",
|
||||
"Added_To_Shopping_List": "",
|
||||
"Shopping_List_Empty": "",
|
||||
"Next_Period": "",
|
||||
"Previous_Period": "",
|
||||
"Current_Period": "",
|
||||
"Next_Day": "",
|
||||
"Previous_Day": "",
|
||||
"Inherit": "",
|
||||
"InheritFields": "",
|
||||
"FoodInherit": "",
|
||||
"ShowUncategorizedFood": "",
|
||||
"GroupBy": "",
|
||||
"Language": "",
|
||||
"Theme": "",
|
||||
"SupermarketCategoriesOnly": "",
|
||||
"MoveCategory": "",
|
||||
"CountMore": "",
|
||||
"IgnoreThis": "",
|
||||
"DelayFor": "",
|
||||
"Warning": "",
|
||||
"NoCategory": "",
|
||||
"InheritWarning": "",
|
||||
"ShowDelayed": "",
|
||||
"Completed": "",
|
||||
"OfflineAlert": "",
|
||||
"shopping_share": "",
|
||||
"shopping_auto_sync": "",
|
||||
"one_url_per_line": "",
|
||||
"mealplan_autoadd_shopping": "",
|
||||
"mealplan_autoexclude_onhand": "",
|
||||
"mealplan_autoinclude_related": "",
|
||||
"default_delay": "",
|
||||
"plan_share_desc": "",
|
||||
"shopping_share_desc": "",
|
||||
"shopping_auto_sync_desc": "",
|
||||
"mealplan_autoadd_shopping_desc": "",
|
||||
"mealplan_autoexclude_onhand_desc": "",
|
||||
"mealplan_autoinclude_related_desc": "",
|
||||
"default_delay_desc": "",
|
||||
"filter_to_supermarket": "",
|
||||
"Coming_Soon": "",
|
||||
"Auto_Planner": "",
|
||||
"New_Cookbook": "",
|
||||
"Hide_Keyword": "",
|
||||
"Hour": "",
|
||||
"Hours": "",
|
||||
"Day": "",
|
||||
"Days": "",
|
||||
"Second": "",
|
||||
"Seconds": "",
|
||||
"Clear": "",
|
||||
"Users": "",
|
||||
"Invites": "",
|
||||
"err_move_self": "",
|
||||
"nothing": "",
|
||||
"err_merge_self": "",
|
||||
"show_sql": "",
|
||||
"filter_to_supermarket_desc": "",
|
||||
"CategoryName": "",
|
||||
"SupermarketName": "",
|
||||
"CategoryInstruction": "",
|
||||
"shopping_recent_days_desc": "",
|
||||
"shopping_recent_days": "",
|
||||
"download_pdf": "",
|
||||
"download_csv": "",
|
||||
"csv_delim_help": "",
|
||||
"csv_delim_label": "",
|
||||
"SuccessClipboard": "",
|
||||
"copy_to_clipboard": "",
|
||||
"csv_prefix_help": "",
|
||||
"csv_prefix_label": "",
|
||||
"copy_markdown_table": "",
|
||||
"in_shopping": "",
|
||||
"DelayUntil": "",
|
||||
"Pin": "",
|
||||
"Unpin": "",
|
||||
"PinnedConfirmation": "",
|
||||
"UnpinnedConfirmation": "",
|
||||
"mark_complete": "",
|
||||
"QuickEntry": "",
|
||||
"shopping_add_onhand_desc": "",
|
||||
"shopping_add_onhand": "",
|
||||
"related_recipes": "",
|
||||
"today_recipes": "",
|
||||
"sql_debug": "",
|
||||
"remember_search": "",
|
||||
"remember_hours": "",
|
||||
"tree_select": "",
|
||||
"OnHand_help": "",
|
||||
"ignore_shopping_help": "",
|
||||
"shopping_category_help": "",
|
||||
"food_recipe_help": "",
|
||||
"Foods": "",
|
||||
"Account": "",
|
||||
"Cosmetic": "",
|
||||
"API": "",
|
||||
"enable_expert": "",
|
||||
"expert_mode": "",
|
||||
"simple_mode": "",
|
||||
"advanced": "",
|
||||
"fields": "",
|
||||
"show_keywords": "",
|
||||
"show_foods": "",
|
||||
"show_books": "",
|
||||
"show_rating": "",
|
||||
"show_units": "",
|
||||
"show_filters": "",
|
||||
"not": "",
|
||||
"save_filter": "",
|
||||
"filter_name": "",
|
||||
"left_handed": "",
|
||||
"left_handed_help": "",
|
||||
"Custom Filter": "",
|
||||
"shared_with": "",
|
||||
"sort_by": "",
|
||||
"asc": "",
|
||||
"desc": "",
|
||||
"date_viewed": "",
|
||||
"last_cooked": "",
|
||||
"times_cooked": "",
|
||||
"date_created": "",
|
||||
"show_sortby": "",
|
||||
"search_rank": "",
|
||||
"make_now": "",
|
||||
"recipe_filter": "",
|
||||
"book_filter_help": "",
|
||||
"review_shopping": "",
|
||||
"view_recipe": "",
|
||||
"copy_to_new": "",
|
||||
"recipe_name": "",
|
||||
"paste_ingredients_placeholder": "",
|
||||
"paste_ingredients": "",
|
||||
"ingredient_list": "",
|
||||
"explain": "",
|
||||
"filter": "",
|
||||
"Website": "",
|
||||
"App": "",
|
||||
"Message": "",
|
||||
"Bookmarklet": "",
|
||||
"Sticky_Nav": "",
|
||||
"Sticky_Nav_Help": "",
|
||||
"Nav_Color": "",
|
||||
"Nav_Color_Help": "",
|
||||
"Use_Kj": "",
|
||||
"Comments_setting": "",
|
||||
"click_image_import": "",
|
||||
"no_more_images_found": "",
|
||||
"import_duplicates": "",
|
||||
"paste_json": "",
|
||||
"Click_To_Edit": "",
|
||||
"search_no_recipes": "",
|
||||
"search_import_help_text": "",
|
||||
"search_create_help_text": "",
|
||||
"warning_duplicate_filter": "",
|
||||
"reset_children": "",
|
||||
"reset_children_help": "",
|
||||
"reset_food_inheritance": "",
|
||||
"reset_food_inheritance_info": "",
|
||||
"substitute_help": "",
|
||||
"substitute_siblings_help": "",
|
||||
"substitute_children_help": "",
|
||||
"substitute_siblings": "",
|
||||
"substitute_children": "",
|
||||
"SubstituteOnHand": "",
|
||||
"ChildInheritFields": "",
|
||||
"ChildInheritFields_help": "",
|
||||
"InheritFields_help": "",
|
||||
"show_ingredient_overview": "",
|
||||
"Ingredient Overview": "",
|
||||
"last_viewed": "",
|
||||
"created_on": "",
|
||||
"updatedon": "",
|
||||
"Imported_From": "",
|
||||
"advanced_search_settings": "",
|
||||
"nothing_planned_today": "",
|
||||
"no_pinned_recipes": "",
|
||||
"Planned": "",
|
||||
"Pinned": "",
|
||||
"Imported": "",
|
||||
"Quick actions": "",
|
||||
"Ratings": "",
|
||||
"Internal": "",
|
||||
"Units": "",
|
||||
"Manage_Emails": "",
|
||||
"Change_Password": "",
|
||||
"Social_Authentication": "",
|
||||
"Random Recipes": "",
|
||||
"parameter_count": "",
|
||||
"select_keyword": "",
|
||||
"add_keyword": "",
|
||||
"select_file": "",
|
||||
"select_recipe": "",
|
||||
"select_unit": "",
|
||||
"select_food": "",
|
||||
"remove_selection": "",
|
||||
"empty_list": "",
|
||||
"Select": "Velg",
|
||||
"Supermarkets": "",
|
||||
"User": "",
|
||||
"Username": "",
|
||||
"First_name": "",
|
||||
"Last_name": "",
|
||||
"Keyword": "Nøkkelord",
|
||||
"Advanced": "",
|
||||
"Page": "",
|
||||
"Single": "",
|
||||
"Multiple": "",
|
||||
"Reset": "",
|
||||
"Disabled": "",
|
||||
"Disable": "",
|
||||
"Options": "",
|
||||
"Create Food": "",
|
||||
"create_food_desc": "",
|
||||
"additional_options": "",
|
||||
"Importer_Help": "",
|
||||
"Documentation": "",
|
||||
"Select_App_To_Import": "",
|
||||
"Import_Supported": "",
|
||||
"Export_Supported": "",
|
||||
"Import_Not_Yet_Supported": "",
|
||||
"Export_Not_Yet_Supported": "",
|
||||
"Import_Result_Info": "",
|
||||
"Recipes_In_Import": "",
|
||||
"Toggle": "",
|
||||
"Import_Error": "",
|
||||
"Warning_Delete_Supermarket_Category": "",
|
||||
"New_Supermarket": "",
|
||||
"New_Supermarket_Category": "",
|
||||
"Are_You_Sure": "",
|
||||
"Valid Until": "",
|
||||
"Split_All_Steps": "",
|
||||
"Combine_All_Steps": "",
|
||||
"Plural": "",
|
||||
"plural_short": "",
|
||||
"Use_Plural_Unit_Always": "",
|
||||
"Use_Plural_Unit_Simple": "",
|
||||
"Use_Plural_Food_Always": "",
|
||||
"Use_Plural_Food_Simple": "",
|
||||
"plural_usage_info": "",
|
||||
"Create Recipe": "",
|
||||
"Import Recipe": ""
|
||||
}
|
@ -16,202 +16,468 @@
|
||||
"convert_internal": "Transformați în rețetă internă",
|
||||
"show_only_internal": "Arătați doar rețetele interne",
|
||||
"show_split_screen": "Vedere divizată",
|
||||
"Log_Recipe_Cooking": "",
|
||||
"External_Recipe_Image": "",
|
||||
"Add_to_Shopping": "",
|
||||
"Add_to_Plan": "",
|
||||
"Step_start_time": "",
|
||||
"Sort_by_new": "",
|
||||
"Table_of_Contents": "",
|
||||
"Recipes_per_page": "",
|
||||
"Show_as_header": "",
|
||||
"Hide_as_header": "",
|
||||
"Add_nutrition_recipe": "",
|
||||
"Remove_nutrition_recipe": "",
|
||||
"Copy_template_reference": "",
|
||||
"Save_and_View": "",
|
||||
"Manage_Books": "",
|
||||
"Meal_Plan": "",
|
||||
"Select_Book": "",
|
||||
"Select_File": "",
|
||||
"Recipe_Image": "",
|
||||
"Import_finished": "",
|
||||
"View_Recipes": "",
|
||||
"Log_Cooking": "",
|
||||
"New_Recipe": "",
|
||||
"Url_Import": "",
|
||||
"Reset_Search": "",
|
||||
"Recently_Viewed": "",
|
||||
"Load_More": "",
|
||||
"New_Keyword": "",
|
||||
"Delete_Keyword": "",
|
||||
"Edit_Keyword": "",
|
||||
"Edit_Recipe": "",
|
||||
"Move_Keyword": "",
|
||||
"Merge_Keyword": "",
|
||||
"Hide_Keywords": "",
|
||||
"Hide_Recipes": "",
|
||||
"Move_Up": "",
|
||||
"Move_Down": "",
|
||||
"Step_Name": "",
|
||||
"Step_Type": "",
|
||||
"Make_Header": "",
|
||||
"Make_Ingredient": "",
|
||||
"Enable_Amount": "",
|
||||
"Disable_Amount": "",
|
||||
"Add_Step": "",
|
||||
"Keywords": "",
|
||||
"Books": "",
|
||||
"Proteins": "",
|
||||
"Fats": "",
|
||||
"Carbohydrates": "",
|
||||
"Calories": "",
|
||||
"Energy": "",
|
||||
"Nutrition": "",
|
||||
"Date": "",
|
||||
"Share": "",
|
||||
"Automation": "",
|
||||
"Parameter": "",
|
||||
"Export": "",
|
||||
"Copy": "",
|
||||
"Rating": "",
|
||||
"Close": "",
|
||||
"Cancel": "",
|
||||
"Link": "",
|
||||
"Add": "",
|
||||
"New": "",
|
||||
"Note": "",
|
||||
"Success": "",
|
||||
"Failure": "",
|
||||
"Ingredients": "",
|
||||
"Supermarket": "",
|
||||
"Categories": "",
|
||||
"Category": "",
|
||||
"Selected": "",
|
||||
"min": "",
|
||||
"Servings": "",
|
||||
"Waiting": "",
|
||||
"Preparation": "",
|
||||
"External": "",
|
||||
"Size": "",
|
||||
"Files": "",
|
||||
"File": "",
|
||||
"Edit": "",
|
||||
"Image": "",
|
||||
"Delete": "",
|
||||
"Open": "",
|
||||
"Ok": "",
|
||||
"Save": "",
|
||||
"Step": "",
|
||||
"Search": "",
|
||||
"Import": "",
|
||||
"Print": "",
|
||||
"Settings": "",
|
||||
"or": "",
|
||||
"and": "",
|
||||
"Information": "",
|
||||
"Download": "",
|
||||
"Create": "",
|
||||
"Log_Recipe_Cooking": "Jurnalul rețetelor de pregătire",
|
||||
"External_Recipe_Image": "Imagine rețetă externă",
|
||||
"Add_to_Shopping": "Adaugare la cumpărături",
|
||||
"Add_to_Plan": "Adăugare la plan",
|
||||
"Step_start_time": "Pasule de începere a orei",
|
||||
"Sort_by_new": "Sortare după nou",
|
||||
"Table_of_Contents": "Cuprins",
|
||||
"Recipes_per_page": "Rețete pe pagină",
|
||||
"Show_as_header": "Afișare ca antet",
|
||||
"Hide_as_header": "Ascunderea ca antet",
|
||||
"Add_nutrition_recipe": "Adăugare a nutriției la rețetă",
|
||||
"Remove_nutrition_recipe": "Ștergere a nutriției din rețetă",
|
||||
"Copy_template_reference": "Copie referința șablonului",
|
||||
"Save_and_View": "Salvare și vizionare",
|
||||
"Manage_Books": "Gestionarea cărților",
|
||||
"Meal_Plan": "Plan de alimentare",
|
||||
"Select_Book": "Selectare carte",
|
||||
"Select_File": "Selectare fișier",
|
||||
"Recipe_Image": "Imagine a rețetei",
|
||||
"Import_finished": "Importare finalizată",
|
||||
"View_Recipes": "Vizionare rețete",
|
||||
"Log_Cooking": "Jurnal de pregătire",
|
||||
"New_Recipe": "Rețetă nouă",
|
||||
"Url_Import": "Importă URL",
|
||||
"Reset_Search": "Resetarea căutării",
|
||||
"Recently_Viewed": "Vizualizate recent",
|
||||
"Load_More": "Încărcați mai mult",
|
||||
"New_Keyword": "Cuvânt cheie nou",
|
||||
"Delete_Keyword": "Ștergere cuvânt cheie",
|
||||
"Edit_Keyword": "Editează cuvânt cheie",
|
||||
"Edit_Recipe": "Editează rețeta",
|
||||
"Move_Keyword": "Mută cuvânt cheie",
|
||||
"Merge_Keyword": "Unește cuvânt cheie",
|
||||
"Hide_Keywords": "Ascunde cuvânt cheie",
|
||||
"Hide_Recipes": "Ascunde rețetele",
|
||||
"Move_Up": "Deplasați-vă în sus",
|
||||
"Move_Down": "Deplasați-vă în jos",
|
||||
"Step_Name": "Nume pas",
|
||||
"Step_Type": "Tip pas",
|
||||
"Make_Header": "Creare antet",
|
||||
"Make_Ingredient": "Create ingredient",
|
||||
"Enable_Amount": "Activare cantitate",
|
||||
"Disable_Amount": "Dezactivare cantitate",
|
||||
"Add_Step": "Adaugă pas",
|
||||
"Keywords": "Cuvinte cheie",
|
||||
"Books": "Cărți",
|
||||
"Proteins": "Proteine",
|
||||
"Fats": "Grăsimi",
|
||||
"Carbohydrates": "Carbohidrați",
|
||||
"Calories": "Calorii",
|
||||
"Energy": "Energie",
|
||||
"Nutrition": "Nutriție",
|
||||
"Date": "Dată",
|
||||
"Share": "Împărtășire",
|
||||
"Automation": "Automatizare",
|
||||
"Parameter": "Parametru",
|
||||
"Export": "Exportă",
|
||||
"Copy": "Copie",
|
||||
"Rating": "Evaluare",
|
||||
"Close": "Închide",
|
||||
"Cancel": "Anulează",
|
||||
"Link": "Link",
|
||||
"Add": "Adaugă",
|
||||
"New": "Nou",
|
||||
"Note": "Notă",
|
||||
"Success": "Succes",
|
||||
"Failure": "Eșec",
|
||||
"Ingredients": "Ingrediente",
|
||||
"Supermarket": "Supermarket",
|
||||
"Categories": "Categorii",
|
||||
"Category": "Categorie",
|
||||
"Selected": "Selectat",
|
||||
"min": "min",
|
||||
"Servings": "Porții",
|
||||
"Waiting": "Așteptare",
|
||||
"Preparation": "Pregătire",
|
||||
"External": "Extern",
|
||||
"Size": "Marime",
|
||||
"Files": "Fișiere",
|
||||
"File": "Fișier",
|
||||
"Edit": "Editează",
|
||||
"Image": "Imagine",
|
||||
"Delete": "Șterge",
|
||||
"Open": "Deschide",
|
||||
"Ok": "Ok",
|
||||
"Save": "Salvare",
|
||||
"Step": "Pas",
|
||||
"Search": "Căutare",
|
||||
"Import": "Importă",
|
||||
"Print": "Tipărește",
|
||||
"Settings": "Setări",
|
||||
"or": "sau",
|
||||
"and": "și",
|
||||
"Information": "Informație",
|
||||
"Download": "Descarcă",
|
||||
"Create": "Creează",
|
||||
"Advanced Search Settings": "",
|
||||
"View": "",
|
||||
"Recipes": "",
|
||||
"Move": "",
|
||||
"Merge": "",
|
||||
"Parent": "",
|
||||
"delete_confirmation": "",
|
||||
"move_confirmation": "",
|
||||
"merge_confirmation": "",
|
||||
"create_rule": "",
|
||||
"move_selection": "",
|
||||
"merge_selection": "",
|
||||
"Root": "",
|
||||
"Ignore_Shopping": "",
|
||||
"Shopping_Category": "",
|
||||
"Edit_Food": "",
|
||||
"Move_Food": "",
|
||||
"New_Food": "",
|
||||
"Hide_Food": "",
|
||||
"Food_Alias": "",
|
||||
"Unit_Alias": "",
|
||||
"Keyword_Alias": "",
|
||||
"Delete_Food": "",
|
||||
"No_ID": "",
|
||||
"Meal_Plan_Days": "",
|
||||
"merge_title": "",
|
||||
"move_title": "",
|
||||
"Food": "",
|
||||
"Recipe_Book": "",
|
||||
"del_confirmation_tree": "",
|
||||
"delete_title": "",
|
||||
"create_title": "",
|
||||
"edit_title": "",
|
||||
"Name": "",
|
||||
"Type": "",
|
||||
"Description": "",
|
||||
"Recipe": "",
|
||||
"tree_root": "",
|
||||
"Icon": "",
|
||||
"Unit": "",
|
||||
"No_Results": "",
|
||||
"New_Unit": "",
|
||||
"Create_New_Shopping Category": "",
|
||||
"Create_New_Food": "",
|
||||
"Create_New_Keyword": "",
|
||||
"Create_New_Unit": "",
|
||||
"Create_New_Meal_Type": "",
|
||||
"and_up": "",
|
||||
"Instructions": "",
|
||||
"Unrated": "",
|
||||
"Automate": "",
|
||||
"Empty": "",
|
||||
"Key_Ctrl": "",
|
||||
"Key_Shift": "",
|
||||
"Time": "",
|
||||
"Text": "",
|
||||
"Shopping_list": "",
|
||||
"Create_Meal_Plan_Entry": "",
|
||||
"Edit_Meal_Plan_Entry": "",
|
||||
"Title": "",
|
||||
"Week": "",
|
||||
"Month": "",
|
||||
"Year": "",
|
||||
"Planner": "",
|
||||
"Planner_Settings": "",
|
||||
"Period": "",
|
||||
"Plan_Period_To_Show": "",
|
||||
"Periods": "",
|
||||
"Plan_Show_How_Many_Periods": "",
|
||||
"Starting_Day": "",
|
||||
"Meal_Types": "",
|
||||
"Meal_Type": "",
|
||||
"Clone": "",
|
||||
"Drag_Here_To_Delete": "",
|
||||
"Meal_Type_Required": "",
|
||||
"Title_or_Recipe_Required": "",
|
||||
"Color": "",
|
||||
"New_Meal_Type": "",
|
||||
"Week_Numbers": "",
|
||||
"Show_Week_Numbers": "",
|
||||
"Export_As_ICal": "",
|
||||
"Export_To_ICal": "",
|
||||
"Cannot_Add_Notes_To_Shopping": "",
|
||||
"Added_To_Shopping_List": "",
|
||||
"Shopping_List_Empty": "",
|
||||
"Next_Period": "",
|
||||
"Previous_Period": "",
|
||||
"Current_Period": "",
|
||||
"Next_Day": "",
|
||||
"Previous_Day": "",
|
||||
"Coming_Soon": "",
|
||||
"Auto_Planner": "",
|
||||
"New_Cookbook": "",
|
||||
"Hide_Keyword": "",
|
||||
"Clear": "",
|
||||
"Plural": "",
|
||||
"plural_short": "",
|
||||
"Use_Plural_Unit_Always": "",
|
||||
"Use_Plural_Unit_Simple": "",
|
||||
"Use_Plural_Food_Always": "",
|
||||
"Use_Plural_Food_Simple": "",
|
||||
"plural_usage_info": ""
|
||||
"View": "Vizualizare",
|
||||
"Recipes": "Rețete",
|
||||
"Move": "Mută",
|
||||
"Merge": "Unire",
|
||||
"Parent": "Părinte",
|
||||
"delete_confirmation": "Sunteți sigur că doriți să ștergeți {source}?",
|
||||
"move_confirmation": "Mutare <i>{copil}</i> la părinte <i>{părinte}</i>",
|
||||
"merge_confirmation": "Înlocuiți <i>{source}</i> cu <i>{target}</i>",
|
||||
"create_rule": "și crearea automatizării",
|
||||
"move_selection": "Selectați un părinte {type} pentru a muta {source} în.",
|
||||
"merge_selection": "Înlocuiți toate aparițiile {source} cu {type} selectat.",
|
||||
"Root": "Rădăcină",
|
||||
"Ignore_Shopping": "Ignoră cumpărăturile",
|
||||
"Shopping_Category": "Categorie de cumpărături",
|
||||
"Edit_Food": "Editare mâncare",
|
||||
"Move_Food": "Mutare mâncare",
|
||||
"New_Food": "Mâncare nouă",
|
||||
"Hide_Food": "Ascunde mâncare",
|
||||
"Food_Alias": "Pseudonim mâncare",
|
||||
"Unit_Alias": "Pseudonim unitate",
|
||||
"Keyword_Alias": "Pseudonim cuvânt cheie",
|
||||
"Delete_Food": "Ștergere mâncare",
|
||||
"No_ID": "ID-ul nu a fost găsit, nu se poate șterge.",
|
||||
"Meal_Plan_Days": "Planuri de alimentație pe viitor",
|
||||
"merge_title": "Unire {type}",
|
||||
"move_title": "Mutare {type}",
|
||||
"Food": "Mâncare",
|
||||
"Recipe_Book": "Carte de rețete",
|
||||
"del_confirmation_tree": "Sunteți sigur că doriți să ștergeți {sursa} și toți copiii săi?",
|
||||
"delete_title": "Ștergere {type}",
|
||||
"create_title": "{type} nou",
|
||||
"edit_title": "Editare {type}",
|
||||
"Name": "Nume",
|
||||
"Type": "Tip",
|
||||
"Description": "Descriere",
|
||||
"Recipe": "Rețetă",
|
||||
"tree_root": "Rădăcina copacului",
|
||||
"Icon": "Iconiță",
|
||||
"Unit": "Unitate",
|
||||
"No_Results": "Fără rezultate",
|
||||
"New_Unit": "Unitate nouă",
|
||||
"Create_New_Shopping Category": "Creați o nouă categorie de cumpărături",
|
||||
"Create_New_Food": "Adaugă mâncare nouă",
|
||||
"Create_New_Keyword": "Adaugă cuvânt cheie nou",
|
||||
"Create_New_Unit": "Adaugă unitate nouă",
|
||||
"Create_New_Meal_Type": "Adaugă tip mâncare nou",
|
||||
"and_up": "& Sus",
|
||||
"Instructions": "Instrucțiuni",
|
||||
"Unrated": "Neevaluat",
|
||||
"Automate": "Automatizat",
|
||||
"Empty": "Gol",
|
||||
"Key_Ctrl": "Ctrl",
|
||||
"Key_Shift": "Shift",
|
||||
"Time": "Timp",
|
||||
"Text": "Text",
|
||||
"Shopping_list": "Lisă de cumpărături",
|
||||
"Create_Meal_Plan_Entry": "Crearea înregistrării în planul de alimentare",
|
||||
"Edit_Meal_Plan_Entry": "Editarea înregistrării în planul de alimentare",
|
||||
"Title": "Titlu",
|
||||
"Week": "Săptămână",
|
||||
"Month": "Lună",
|
||||
"Year": "An",
|
||||
"Planner": "Planificator",
|
||||
"Planner_Settings": "Setări planificator",
|
||||
"Period": "Perioadă",
|
||||
"Plan_Period_To_Show": "Afișați săptămâni, luni sau ani",
|
||||
"Periods": "Perioade",
|
||||
"Plan_Show_How_Many_Periods": "Câte perioade să afișezi",
|
||||
"Starting_Day": "Ziua de început a săptămânii",
|
||||
"Meal_Types": "Tipuri de mese",
|
||||
"Meal_Type": "Tipul mesei",
|
||||
"Clone": "Clonă",
|
||||
"Drag_Here_To_Delete": "Mută aici pentru a șterge",
|
||||
"Meal_Type_Required": "Tipul mesei este necesar",
|
||||
"Title_or_Recipe_Required": "Titlul sau selecția rețetei necesare",
|
||||
"Color": "Culoare",
|
||||
"New_Meal_Type": "Tip de masă nou",
|
||||
"Week_Numbers": "Numerele săptămânii",
|
||||
"Show_Week_Numbers": "Afișați numerele săptămânii?",
|
||||
"Export_As_ICal": "Exportul perioadei curente în format iCal",
|
||||
"Export_To_ICal": "Exportă .ics",
|
||||
"Cannot_Add_Notes_To_Shopping": "Notele nu pot fi adăugate la lista de cumpărături",
|
||||
"Added_To_Shopping_List": "Adăugat la lista de cumpărături",
|
||||
"Shopping_List_Empty": "Lista de cumpărături este în prezent goală, puteți adăuga articole prin meniul contextual al unei intrări în planul de alimentație (faceți click dreapta pe card sau faceți click stânga pe iconița meniului)",
|
||||
"Next_Period": "Perioada următoare",
|
||||
"Previous_Period": "Perioada precedentă",
|
||||
"Current_Period": "Perioada curentă",
|
||||
"Next_Day": "Ziua următoare",
|
||||
"Previous_Day": "Ziua precedentă",
|
||||
"Coming_Soon": "În curând",
|
||||
"Auto_Planner": "Planificator automat",
|
||||
"New_Cookbook": "Nouă carte de bucate",
|
||||
"Hide_Keyword": "Ascunde cuvintele cheie",
|
||||
"Clear": "Curățare",
|
||||
"Plural": "Plural",
|
||||
"plural_short": "plural",
|
||||
"Use_Plural_Unit_Always": "Utilizarea formei plurale pentru unitate întotdeauna",
|
||||
"Use_Plural_Unit_Simple": "Utilizarea dinamică a formei plurale pentru unitate",
|
||||
"Use_Plural_Food_Always": "Utilizarea formei plurale pentru alimente întotdeauna",
|
||||
"Use_Plural_Food_Simple": "Utilizarea dinamica a formei plurale pentru alimente",
|
||||
"plural_usage_info": "Utilizarea formei plurale pentru unități și alimente în interiorul acestui spațiu.",
|
||||
"last_viewed": "Ultima vizualizare",
|
||||
"created_on": "Creat la data de",
|
||||
"updatedon": "Actualizat la data de",
|
||||
"Imported_From": "Importat din",
|
||||
"and_down": "& Jos",
|
||||
"Warning": "Atenționare",
|
||||
"ShowDelayed": "Afișarea elementelor întârziate",
|
||||
"shopping_share_desc": "Utilizatorii vor vedea toate articolele pe care le adăugați în lista de cumpărături. Ei trebuie să vă adauge pentru a vedea elementele din lista lor.",
|
||||
"mealplan_autoinclude_related_desc": "Atunci când adăugați un plan de alimentare în lista de cumpărături (manual sau automat), includeți toate rețetele asociate.",
|
||||
"SuccessClipboard": "Lista de cumpărături copiată în clipboard",
|
||||
"in_shopping": "În lista de cumpărături",
|
||||
"not": "nu",
|
||||
"Pin": "Fixează",
|
||||
"Create Recipe": "Crearea rețetei",
|
||||
"Import Recipe": "Importă rețeta",
|
||||
"csv_prefix_label": "Prefix a listei",
|
||||
"Click_To_Edit": "Faceți click pentru a edita",
|
||||
"Ingredient Editor": "Editor de ingrediente",
|
||||
"FoodOnHand": "Aveți {food} la îndemână.",
|
||||
"AddFoodToShopping": "Adăugă {food} în lista de cumpărături",
|
||||
"New_Entry": "Înregistrare nouă",
|
||||
"GroupBy": "Grupat de",
|
||||
"CountMore": "...+{count} mai mult",
|
||||
"IgnoreThis": "Nu adăugați niciodată automat {food} la cumpărături",
|
||||
"InheritWarning": "{food} este setat să moștenească, este posibil ca modificările să nu persiste.",
|
||||
"err_move_self": "Nu se poate muta elementul în sine",
|
||||
"CategoryName": "Nume categorie",
|
||||
"Foods": "Alimente",
|
||||
"copy_to_new": "Copiere in rețetă nouă",
|
||||
"reset_children": "Resetarea moștenirii copilului",
|
||||
"err_moving_resource": "A existat o eroare în mutarea unei resurse!",
|
||||
"err_merging_resource": "A existat o eroare la fuzionarea unei resurse!",
|
||||
"success_moving_resource": "Resursă mutată cu succes!",
|
||||
"success_merging_resource": "A fuzionat cu succes o resursă!",
|
||||
"Decimals": "Zecimale",
|
||||
"Default_Unit": "Unitate standard",
|
||||
"Use_Fractions": "Folosire fracțiuni",
|
||||
"Use_Fractions_Help": "Convertiți automat zecimalele în fracții atunci când vizualizați o rețetă.",
|
||||
"RemoveFoodFromShopping": "Șterge {food} din lista de cumpărături",
|
||||
"IgnoredFood": "{food} este setat să ignore cumpărăturile.",
|
||||
"Add_Servings_to_Shopping": "Adăugă {servings} porții la cumpărături",
|
||||
"InheritFields": "Moștenirea valorilor câmpurilor",
|
||||
"Language": "Limba",
|
||||
"Theme": "Tema",
|
||||
"NoCategory": "Nicio categorie selectată.",
|
||||
"OfflineAlert": "Sunteți offline, este posibil ca lista de cumpărături să nu se sincronizeze.",
|
||||
"mealplan_autoinclude_related": "Adăugați rețete asociate",
|
||||
"shopping_auto_sync": "Sincronizare automată",
|
||||
"mealplan_autoadd_shopping": "Adăugare automată a planului de alimentare",
|
||||
"default_delay": "Ore de întârziere implicite",
|
||||
"plan_share_desc": "Noile intrări din Planul de alimentare vor fi partajate automat cu utilizatorii selectați.",
|
||||
"shopping_auto_sync_desc": "Setarea la 0 va dezactiva sincronizarea automată. Atunci când vizualizați o listă de cumpărături, lista este actualizată la fiecare câteva secunde setate pentru a sincroniza modificările pe care altcineva le-ar fi putut face. Util atunci când faceți cumpărături cu mai multe persoane, dar va folosi mai multe date mobile.",
|
||||
"mealplan_autoexclude_onhand_desc": "Atunci când adăugați un plan de alimentare în lista de cumpărături (manual sau automat), excludeți ingredientele care sunt în prezent la îndemână.",
|
||||
"default_delay_desc": "Numărul implicit de ore pentru a întârzia o intrare în lista de cumpărături.",
|
||||
"Hour": "Oră",
|
||||
"Hours": "Ore",
|
||||
"Day": "Zi",
|
||||
"Days": "Zile",
|
||||
"Second": "Secundă",
|
||||
"Seconds": "Secunde",
|
||||
"Users": "Utilizatori",
|
||||
"Invites": "Invită",
|
||||
"nothing": "Nimic de făcut",
|
||||
"err_merge_self": "Nu se poate uni elementul cu el însuși",
|
||||
"download_csv": "Descarcă CSV",
|
||||
"Account": "Cont",
|
||||
"Cosmetic": "Cosmetice",
|
||||
"API": "API",
|
||||
"left_handed_help": "Va optimiza interfața de utilizare pentru utilizare cu mâna stângă.",
|
||||
"Custom Filter": "Filtru personalizat",
|
||||
"recipe_name": "Nume rețetă",
|
||||
"paste_ingredients": "Inserați ingredientele",
|
||||
"Website": "Site web",
|
||||
"Nav_Color_Help": "Modificare culoare navigare.",
|
||||
"Use_Kj": "Utilizare kJ în loc de kcal",
|
||||
"Username": "Nume utilizator",
|
||||
"First_name": "Prenume",
|
||||
"Last_name": "Nume de familie",
|
||||
"Keyword": "Cuvânt cheie",
|
||||
"Advanced": "Avansat",
|
||||
"Page": "Pagină",
|
||||
"User": "Utilizator",
|
||||
"Shopping_Categories": "Categorii de cumpărături",
|
||||
"Single": "Singur",
|
||||
"Multiple": "Multiplu",
|
||||
"Reset": "Resetare",
|
||||
"Disabled": "Dezactivat",
|
||||
"Disable": "Dezactivare",
|
||||
"Importer_Help": "Mai multe informații și ajutor cu privire la acest importator:",
|
||||
"Documentation": "Documentație",
|
||||
"Import_Error": "A apărut o eroare în timpul importului. Vă rugăm să extindeți detaliile din partea de jos a paginii pentru a le vizualiza.",
|
||||
"Warning_Delete_Supermarket_Category": "Ștergerea unei categorii de supermarketuri va șterge, de asemenea, toate relațiile cu alimentele. Sunteți sigur?",
|
||||
"one_url_per_line": "O adresă URL pe linie",
|
||||
"mealplan_autoexclude_onhand": "Excludeți alimentele la îndemână",
|
||||
"shopping_recent_days": "Zilele recente",
|
||||
"download_pdf": "Descarcă PDF",
|
||||
"filter": "Filtru",
|
||||
"Search Settings": "Setări de căutare",
|
||||
"err_deleting_protected_resource": "Obiectul pe care încercați să îl ștergeți este încă utilizat și nu poate fi șters.",
|
||||
"csv_delim_help": "Delimitatorul utilizat pentru exporturile CSV.",
|
||||
"csv_delim_label": "Delimitatorul CSV",
|
||||
"SupermarketCategoriesOnly": "Numai categorii de supermarket-uri",
|
||||
"shopping_category_help": "Supermarket-urile pot fi ordonate și filtrate în funcție de categoria de cumpărături în conformitate cu aspectul culoarului.",
|
||||
"food_recipe_help": "Legarea unei rețete aici va include rețeta legată în orice altă rețetă care utilizează acest aliment",
|
||||
"Private_Recipe": "Rețetă privată",
|
||||
"DelayUntil": "Amână până la",
|
||||
"shared_with": "Împărtășit cu",
|
||||
"asc": "Crescător",
|
||||
"desc": "Descrescător",
|
||||
"date_viewed": "Ultimul vizionat",
|
||||
"show_sortby": "Afișează sortat de",
|
||||
"Quick actions": "Acțiuni rapide",
|
||||
"Internal": "Intern",
|
||||
"parameter_count": "Parametru {count}",
|
||||
"Ratings": "Evaluări",
|
||||
"warning_space_delete": "Puteți șterge spațiul, inclusiv toate rețetele, listele de cumpărături, planurile de alimentare și orice altceva ați creat. Acest lucru nu poate fi anulat! Sunteți sigur că doriți să faceți acest lucru?",
|
||||
"remember_hours": "Ore de reținut",
|
||||
"tree_select": "Utilizarea selecției arborilor",
|
||||
"last_cooked": "Ultimul pregătit",
|
||||
"Auto_Sort": "Sortare automatizată",
|
||||
"Private_Recipe_Help": "Rețeta este arătată doar ție și oamenilor cu care este împărtășită.",
|
||||
"save_filter": "Salvare filtru",
|
||||
"Nav_Color": "Culoare navigare",
|
||||
"Comments_setting": "Afișează comentarii",
|
||||
"search_no_recipes": "Nu a putut găsi nici o rețetă!",
|
||||
"Supermarkets": "Supermarket-uri",
|
||||
"Undefined": "Nedefinit",
|
||||
"Select": "Selectare",
|
||||
"food_inherit_info": "Câmpuri pe alimente care ar trebui să fie moștenite în mod implicit.",
|
||||
"facet_count_info": "Afișarea numărului de rețete pe filtrele de căutare.",
|
||||
"Amount": "Cantitate",
|
||||
"Auto_Sort_Help": "Mutați toate ingredientele la cel mai potrivit pas.",
|
||||
"search_create_help_text": "Creați o rețetă nouă direct în Tandoor.",
|
||||
"reusable_help_text": "Ar trebui link-ul de invitație să poată fi utilizat de mai mulți utilizatori.",
|
||||
"Copy Link": "Copiere link",
|
||||
"AddToShopping": "Adaugă la lista de cumpărături",
|
||||
"FoodNotOnHand": "Nu aveți {food} la îndemână.",
|
||||
"DeleteShoppingConfirm": "Sunteți sigur că doriți să eliminați toate {food} din lista de cumpărături?",
|
||||
"mealplan_autoadd_shopping_desc": "Adăugați automat ingredientele planului de alimentare în lista de cumpărături.",
|
||||
"filter_to_supermarket_desc": "În mod implicit, filtrați lista de cumpărături pentru a include numai categoriile pentru supermarketul selectat.",
|
||||
"CategoryInstruction": "Trageți categoriile pentru a schimba categoriile de comenzi care apar în lista de cumpărături.",
|
||||
"copy_markdown_table": "Copiere ca tabel Markdown",
|
||||
"sql_debug": "Depanare SQL",
|
||||
"remember_search": "Rețineți căutarea",
|
||||
"OnHand_help": "Alimentele sunt în inventar și nu vor fi adăugate automat la o listă de cumpărături. Starea la îndemână este partajată cu utilizatorii de cumpărături.",
|
||||
"show_rating": "Afișează evaluarea",
|
||||
"search_rank": "Rang de căutare",
|
||||
"book_filter_help": "Includeți rețete din filtrul de rețete în plus față de cele atribuite manual.",
|
||||
"Sticky_Nav_Help": "Afișați întotdeauna meniul de navigare din partea de sus a ecranului.",
|
||||
"import_duplicates": "Pentru a preveni duplicatele, rețetele cu același nume ca și cele existente sunt ignorate. Bifați această casetă pentru a importa totul.",
|
||||
"warning_duplicate_filter": "Atenționare: Din cauza limitărilor tehnice care au mai multe filtre de aceeași combinație (și/sau/nu) ar putea da rezultate neașteptate.",
|
||||
"substitute_help": "Înlocuitorii sunt luați în considerare atunci când căutați rețete care pot fi făcute cu ingrediente la îndemână.",
|
||||
"substitute_children": "Înlocuire copii",
|
||||
"SubstituteOnHand": "Ai un înlocuitor la îndemână.",
|
||||
"InheritFields_help": "Valorile acestor câmpuri vor fi moștenite de la părinte (Excepție: categoriile de cumpărături necompletate nu sunt moștenite)",
|
||||
"Social_Authentication": "Autentificare socială",
|
||||
"empty_list": "Lista este goală.",
|
||||
"Select_App_To_Import": "Selectați o aplicație din care să importați",
|
||||
"Recipes_In_Import": "Rețete în fișierul de import",
|
||||
"Split_All_Steps": "Împărțiți toate rândurile în pași separați.",
|
||||
"Description_Replace": "Înlocuire descripție",
|
||||
"Instruction_Replace": "Înlocuire instrucții",
|
||||
"Copy Token": "Copiere token",
|
||||
"ShowUncategorizedFood": "Afișează nedefinit",
|
||||
"MoveCategory": "Mută la: ",
|
||||
"DelayFor": "Întârziere pentru {hours} ore",
|
||||
"Completed": "Completat",
|
||||
"shopping_share": "Partajați lista de cumpărături",
|
||||
"filter_to_supermarket": "Filtrați la supermarket",
|
||||
"show_sql": "Afișează SQL",
|
||||
"SupermarketName": "Numele supermarketului",
|
||||
"FoodInherit": "Câmpuri moștenite de alimente",
|
||||
"mark_complete": "Marcare completată",
|
||||
"shopping_add_onhand_desc": "Marcați mâncarea 'La îndemână' atunci când este bifată de pe lista de cumpărături.",
|
||||
"shopping_add_onhand": "La îndemână automat",
|
||||
"related_recipes": "Rețete înrudite",
|
||||
"ignore_shopping_help": "Nu adăugați niciodată alimente pe lista de cumpărături (ex. apă)",
|
||||
"today_recipes": "Rețete de astăzi",
|
||||
"enable_expert": "Activarea modului Expert",
|
||||
"expert_mode": "Modul Expert",
|
||||
"simple_mode": "Modul Simplu",
|
||||
"advanced": "Avansat",
|
||||
"Unpin": "Anularea fixării",
|
||||
"Protected": "Protejat",
|
||||
"Original_Text": "Text original",
|
||||
"Create_New_Shopping_Category": "Adaugă categorie de cumpărături nouă",
|
||||
"Added_by": "Adăugat de",
|
||||
"Added_on": "Adăugat la",
|
||||
"IngredientInShopping": "Acest ingredient se află în lista de cumpărături.",
|
||||
"NotInShopping": "{food} nu se află în lista de cumpărături.",
|
||||
"OnHand": "În prezent, la îndemână",
|
||||
"Inherit": "Moștenire",
|
||||
"shopping_recent_days_desc": "Zile de intrări recente lista de cumpărături pentru a afișa.",
|
||||
"copy_to_clipboard": "Copierea în Clipboard",
|
||||
"csv_prefix_help": "Prefix de adăugat la copierea listei în clipboard.",
|
||||
"PinnedConfirmation": "{recipe} a fost fixată.",
|
||||
"UnpinnedConfirmation": "Fixarea {recipe} a fost anulată.",
|
||||
"QuickEntry": "Înscriere rapidă",
|
||||
"fields": "Câmpuri",
|
||||
"show_keywords": "Afișează cuvinte cheie",
|
||||
"show_foods": "Afișează mâncări",
|
||||
"show_books": "Afișează cărți",
|
||||
"show_units": "Afișează unitățile",
|
||||
"show_filters": "Afișează filtrele",
|
||||
"filter_name": "Nume filtru",
|
||||
"left_handed": "Modul stângaci",
|
||||
"sort_by": "Sortat de",
|
||||
"times_cooked": "Ori pregătite",
|
||||
"date_created": "Data creării",
|
||||
"make_now": "Creează acum",
|
||||
"recipe_filter": "Filtru rețete",
|
||||
"review_shopping": "Examinați intrările de cumpărături înainte de a salva",
|
||||
"view_recipe": "Vizionează rețeta",
|
||||
"paste_ingredients_placeholder": "Inserați lista de ingrediente aici...",
|
||||
"ingredient_list": "Lista de ingrediente",
|
||||
"explain": "Explicație",
|
||||
"App": "Aplicație",
|
||||
"Message": "Mesaj",
|
||||
"Sticky_Nav": "Navigare lipicioasă",
|
||||
"click_image_import": "Faceți click pe imaginea pe care doriți să o importați pentru această rețetă",
|
||||
"no_more_images_found": "Nu există imagini suplimentare găsite pe site-ul web.",
|
||||
"paste_json": "Inserați sursă JSON sau HTML aici pentru a încărca rețetă.",
|
||||
"search_import_help_text": "Importați o rețetă de pe un site web sau o aplicație externă.",
|
||||
"reset_children_help": "Suprascrieți toți copiii cu valori din câmpurile moștenite. Câmpurile moștenite ale copiilor vor fi setate la câmpuri standard, cu excepția cazului în care sunt setate câmpurile moștenite de copii.",
|
||||
"reset_food_inheritance": "Resetați moștenirea",
|
||||
"reset_food_inheritance_info": "Resetați toate alimentele la câmpurile moștenite implicit și la valorile părinte ale acestora.",
|
||||
"substitute_siblings_help": "Toate alimentele care împărtășesc un părinte al acestui aliment sunt considerate înlocuitori.",
|
||||
"substitute_children_help": "Toate alimentele care sunt copii ai acestui aliment sunt considerate înlocuitori.",
|
||||
"substitute_siblings": "Înlocuire frați",
|
||||
"Bookmarklet": "Marcaj",
|
||||
"ChildInheritFields": "Copiii moștenesc câmpurile",
|
||||
"ChildInheritFields_help": "Copiii vor moșteni aceste câmpuri în mod implicit.",
|
||||
"show_ingredient_overview": "Afișați o listă cu toate ingredientele la începutul rețetei.",
|
||||
"Ingredient Overview": "Prezentare generală a ingredientelor",
|
||||
"advanced_search_settings": "Setări avansate de căutare",
|
||||
"nothing_planned_today": "Nu ai nimic planificat pentru ziua de azi!",
|
||||
"no_pinned_recipes": "Nu ai rețete fixate!",
|
||||
"Planned": "Planificate",
|
||||
"Pinned": "Fixate",
|
||||
"Imported": "Importate",
|
||||
"Units": "Unități",
|
||||
"Manage_Emails": "Gestionarea e-mailurilor",
|
||||
"Change_Password": "Schimbați parola",
|
||||
"Random Recipes": "Rețete aleatoare",
|
||||
"select_keyword": "Selectați cuvânt cheie",
|
||||
"add_keyword": "Adăugare cuvânt cheie",
|
||||
"select_file": "Selectare fișier",
|
||||
"select_recipe": "Selectare rețetă",
|
||||
"select_unit": "Selectare unitate",
|
||||
"select_food": "Selectare mâncare",
|
||||
"remove_selection": "Deselectare",
|
||||
"Options": "Opțiuni",
|
||||
"Create Food": "Creare mâncare",
|
||||
"create_food_desc": "Creați un aliment și conectați-l la această rețetă.",
|
||||
"additional_options": "Opțiuni suplimentare",
|
||||
"Import_Supported": "Import compatibil",
|
||||
"Export_Supported": "Export compatibil",
|
||||
"Import_Not_Yet_Supported": "Importul încă nu este compatibil",
|
||||
"Export_Not_Yet_Supported": "Exportul încă nu este compatibil",
|
||||
"Import_Result_Info": "{imported} din {total} rețete au fost importate",
|
||||
"Toggle": "Comutare",
|
||||
"New_Supermarket": "Creați un supermarket nou",
|
||||
"New_Supermarket_Category": "Creați o nouă categorie de supermarket-uri",
|
||||
"Are_You_Sure": "Sunteți sigur?",
|
||||
"Valid Until": "Valabil până la",
|
||||
"Combine_All_Steps": "Combinați toți pașii într-un singur câmp."
|
||||
}
|
||||
|
@ -286,7 +286,7 @@
|
||||
"expert_mode": "Экспертный режим",
|
||||
"enable_expert": "Включить экспертный режим",
|
||||
"review_shopping": "Просмотрите записи о покупках перед сохранением",
|
||||
"empty_list": "Список пуст",
|
||||
"empty_list": "Список пуст.",
|
||||
"default_delay_desc": "Число часов по умолчанию для отсрочки записи в списке покупок.",
|
||||
"one_url_per_line": "Один URL в строке",
|
||||
"mealplan_autoinclude_related": "Добавить сопутствующие рецепты",
|
||||
@ -343,5 +343,8 @@
|
||||
"DelayFor": "Отложить на {hours} часов",
|
||||
"New_Entry": "Новая запись",
|
||||
"GroupBy": "Сгруппировать по",
|
||||
"facet_count_info": "Показывать количество рецептов в фильтрах поиска."
|
||||
"facet_count_info": "Показывать количество рецептов в фильтрах поиска.",
|
||||
"food_inherit_info": "Поля для продуктов питания, которые должны наследоваться по умолчанию.",
|
||||
"warning_space_delete": "Вы можете удалить свое пространство, включая все рецепты, списки покупок, планы питания и все остальное, что вы создали. Этого нельзя отменить! Ты уверен, что хочешь это сделать?",
|
||||
"Description_Replace": "Изменить описание"
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user