mirror of
https://github.com/TandoorRecipes/recipes.git
synced 2025-12-26 03:43:34 -05:00
Compare commits
88 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6faabe3759 | ||
|
|
69acca7de1 | ||
|
|
9d8c08341f | ||
|
|
d488559e42 | ||
|
|
85f7740e9b | ||
|
|
72e831afcf | ||
|
|
cb59f046c0 | ||
|
|
25d505161f | ||
|
|
62aa62b90f | ||
|
|
3fe5340592 | ||
|
|
9233cb9cf9 | ||
|
|
fd9f6f6dca | ||
|
|
ecd4ce603c | ||
|
|
695cab29a1 | ||
|
|
7b6ca94d49 | ||
|
|
35e04f94c6 | ||
|
|
7c4cd02dfa | ||
|
|
5ae440d5c9 | ||
|
|
e88010310c | ||
|
|
b6eba9c5e7 | ||
|
|
9d827ac174 | ||
|
|
27679ae8a5 | ||
|
|
6cb9a7068e | ||
|
|
f41c2ee7bb | ||
|
|
af581bb27c | ||
|
|
885c8982c1 | ||
|
|
64a9f67802 | ||
|
|
df45e1d523 | ||
|
|
03d7aa37da | ||
|
|
dd3d28ec75 | ||
|
|
ea377c2f3b | ||
|
|
6f0bf886f6 | ||
|
|
16fbd9fe48 | ||
|
|
79cdb56f9a | ||
|
|
1e6ba924ab | ||
|
|
9ae076e426 | ||
|
|
f346022d8b | ||
|
|
c9cd5325c4 | ||
|
|
ba6c80e04a | ||
|
|
be3f860ba1 | ||
|
|
1ac4020b3d | ||
|
|
5da535b8ac | ||
|
|
b8ed99a59a | ||
|
|
c199536fca | ||
|
|
d60a9f0379 | ||
|
|
c5da006f4a | ||
|
|
b72919dd42 | ||
|
|
020b102c5f | ||
|
|
c02ea744ac | ||
|
|
df76791e1e | ||
|
|
086f8b4d62 | ||
|
|
409594a73a | ||
|
|
5af687364f | ||
|
|
1431be5cad | ||
|
|
769b53a309 | ||
|
|
fd8752b298 | ||
|
|
301f1cede4 | ||
|
|
3f7aed995a | ||
|
|
44d535301d | ||
|
|
15e36cc03f | ||
|
|
f6cb5128f5 | ||
|
|
83567c25aa | ||
|
|
4288d76afd | ||
|
|
195a13f825 | ||
|
|
f0335ebe40 | ||
|
|
7fd2817014 | ||
|
|
0ab21f9941 | ||
|
|
699edb6579 | ||
|
|
372a2e480e | ||
|
|
06e54aed4b | ||
|
|
e426cae091 | ||
|
|
56e44ee3ff | ||
|
|
e18737d254 | ||
|
|
dda2529f6f | ||
|
|
5fdbedc924 | ||
|
|
a2e06a3099 | ||
|
|
6fadad1a5f | ||
|
|
cbc517b5da | ||
|
|
fb018ef9e2 | ||
|
|
1eac80942c | ||
|
|
e611c095bb | ||
|
|
c418b7bbff | ||
|
|
c0417f0b5d | ||
|
|
aef73bc104 | ||
|
|
ca1ce40048 | ||
|
|
b2eef1ee30 | ||
|
|
b4f754e7d3 | ||
|
|
d681e3ced3 |
2
.github/workflows/docker-publish-release.yml
vendored
2
.github/workflows/docker-publish-release.yml
vendored
@@ -49,4 +49,4 @@ jobs:
|
||||
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 🥳 \nCheck it out *https://github.com/vabene1111/recipes/releases/tag/{{ EVENT_PAYLOAD.release.tag_name }}*'
|
||||
args: '🚀 Version {{ EVENT_PAYLOAD.release.tag_name }} of tandoor has been released 🥳 \nCheck it out https://github.com/vabene1111/recipes/releases/tag/{{ EVENT_PAYLOAD.release.tag_name }}'
|
||||
|
||||
2
.github/workflows/docs.yml
vendored
2
.github/workflows/docs.yml
vendored
@@ -14,5 +14,5 @@ jobs:
|
||||
- uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: 3.x
|
||||
- run: pip install mkdocs-material
|
||||
- run: pip install mkdocs-material mkdocs-include-markdown-plugin
|
||||
- run: mkdocs gh-deploy --force
|
||||
@@ -1,26 +1,40 @@
|
||||
import traceback
|
||||
|
||||
from django.apps import AppConfig
|
||||
from django.conf import settings
|
||||
from django.db import OperationalError, ProgrammingError
|
||||
from django_scopes import scopes_disabled
|
||||
|
||||
from recipes.settings import DEBUG
|
||||
|
||||
|
||||
class CookbookConfig(AppConfig):
|
||||
name = 'cookbook'
|
||||
|
||||
def ready(self):
|
||||
# post_save signal is only necessary if using full-text search on postgres
|
||||
if settings.DATABASES['default']['ENGINE'] in ['django.db.backends.postgresql_psycopg2', 'django.db.backends.postgresql']:
|
||||
if settings.DATABASES['default']['ENGINE'] in ['django.db.backends.postgresql_psycopg2',
|
||||
'django.db.backends.postgresql']:
|
||||
import cookbook.signals # noqa
|
||||
|
||||
# when starting up run fix_tree to:
|
||||
# a) make sure that nodes are sorted when switching between sort modes
|
||||
# b) fix problems, if any, with tree consistency
|
||||
with scopes_disabled():
|
||||
try:
|
||||
from cookbook.models import Keyword, Food
|
||||
Keyword.fix_tree(fix_paths=True)
|
||||
Food.fix_tree(fix_paths=True)
|
||||
except OperationalError:
|
||||
pass # if model does not exist there is no need to fix it
|
||||
except ProgrammingError:
|
||||
pass # if migration has not been run database cannot be fixed yet
|
||||
if not settings.DISABLE_TREE_FIX_STARTUP:
|
||||
# when starting up run fix_tree to:
|
||||
# a) make sure that nodes are sorted when switching between sort modes
|
||||
# b) fix problems, if any, with tree consistency
|
||||
with scopes_disabled():
|
||||
try:
|
||||
from cookbook.models import Keyword, Food
|
||||
Keyword.fix_tree(fix_paths=True)
|
||||
Food.fix_tree(fix_paths=True)
|
||||
except OperationalError:
|
||||
if DEBUG:
|
||||
traceback.print_exc()
|
||||
pass # if model does not exist there is no need to fix it
|
||||
except ProgrammingError:
|
||||
if DEBUG:
|
||||
traceback.print_exc()
|
||||
pass # if migration has not been run database cannot be fixed yet
|
||||
except Exception:
|
||||
if DEBUG:
|
||||
traceback.print_exc()
|
||||
pass # dont break startup just because fix could not run, need to investigate cases when this happens
|
||||
|
||||
@@ -36,6 +36,11 @@ class DateWidget(forms.DateInput):
|
||||
class UserPreferenceForm(forms.ModelForm):
|
||||
prefix = 'preference'
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
space = kwargs.pop('space')
|
||||
super().__init__(*args, **kwargs)
|
||||
self.fields['plan_share'].queryset = User.objects.filter(userpreference__space=space).all()
|
||||
|
||||
class Meta:
|
||||
model = UserPreference
|
||||
fields = (
|
||||
@@ -46,7 +51,19 @@ class UserPreferenceForm(forms.ModelForm):
|
||||
)
|
||||
|
||||
labels = {
|
||||
'use_kj': 'Use KJ'
|
||||
'default_unit': _('Default unit'),
|
||||
'use_fractions': _('Use fractions'),
|
||||
'use_kj': _('Use KJ'),
|
||||
'theme': _('Theme'),
|
||||
'nav_color': _('Navbar color'),
|
||||
'sticky_navbar': _('Sticky navbar'),
|
||||
'default_page': _('Default page'),
|
||||
'show_recent': _('Show recent recipes'),
|
||||
'search_style': _('Search style'),
|
||||
'plan_share': _('Plan sharing'),
|
||||
'ingredient_decimals': _('Ingredient decimal places'),
|
||||
'shopping_auto_sync': _('Shopping list auto sync period'),
|
||||
'comments': _('Comments')
|
||||
}
|
||||
|
||||
help_texts = {
|
||||
@@ -56,7 +73,7 @@ class UserPreferenceForm(forms.ModelForm):
|
||||
'use_fractions': _(
|
||||
'Enables support for fractions in ingredient amounts (e.g. convert decimals to fractions automatically)'),
|
||||
# noqa: E501
|
||||
'use_kj': _('Display nutritional energy amounts in joules instead of calories'), # noqa: E501
|
||||
'use_kj': _('Display nutritional energy amounts in joules instead of calories'), # noqa: E501
|
||||
'plan_share': _(
|
||||
'Users with whom newly created meal plan/shopping list entries should be shared by default.'),
|
||||
# noqa: E501
|
||||
@@ -237,6 +254,12 @@ class SyncForm(forms.ModelForm):
|
||||
'storage': SafeModelChoiceField,
|
||||
}
|
||||
|
||||
labels = {
|
||||
'storage': _('Storage'),
|
||||
'path': _('Path'),
|
||||
'active': _('Active')
|
||||
}
|
||||
|
||||
|
||||
class BatchEditForm(forms.Form):
|
||||
search = forms.CharField(label=_('Search String'))
|
||||
@@ -325,7 +348,8 @@ class InviteLinkForm(forms.ModelForm):
|
||||
|
||||
def clean(self):
|
||||
space = self.cleaned_data['space']
|
||||
if space.max_users != 0 and (UserPreference.objects.filter(space=space).count() + InviteLink.objects.filter(space=space).count()) >= space.max_users:
|
||||
if space.max_users != 0 and (UserPreference.objects.filter(space=space).count() + InviteLink.objects.filter(
|
||||
space=space).count()) >= space.max_users:
|
||||
raise ValidationError(_('Maximum number of users for this space reached.'))
|
||||
|
||||
def clean_email(self):
|
||||
@@ -340,7 +364,7 @@ class InviteLinkForm(forms.ModelForm):
|
||||
model = InviteLink
|
||||
fields = ('email', 'group', 'valid_until', 'space')
|
||||
help_texts = {
|
||||
'email': _('An email address is not required but if present the invite link will be send to the user.'),
|
||||
'email': _('An email address is not required but if present the invite link will be sent to the user.'),
|
||||
}
|
||||
field_classes = {
|
||||
'space': SafeModelChoiceField,
|
||||
@@ -395,22 +419,31 @@ class UserCreateForm(forms.Form):
|
||||
|
||||
class SearchPreferenceForm(forms.ModelForm):
|
||||
prefix = 'search'
|
||||
trigram_threshold = forms.DecimalField(min_value=0.01, max_value=1, decimal_places=2, widget=NumberInput(attrs={'class': "form-control-range", 'type': 'range'}),
|
||||
help_text=_('Determines how fuzzy a search is if it uses trigram similarity matching (e.g. low values mean more typos are ignored).'))
|
||||
preset = forms.CharField(widget=forms.HiddenInput(),required=False)
|
||||
trigram_threshold = forms.DecimalField(min_value=0.01, max_value=1, decimal_places=2,
|
||||
widget=NumberInput(attrs={'class': "form-control-range", 'type': 'range'}),
|
||||
help_text=_(
|
||||
'Determines how fuzzy a search is if it uses trigram similarity matching (e.g. low values mean more typos are ignored).'))
|
||||
preset = forms.CharField(widget=forms.HiddenInput(), required=False)
|
||||
|
||||
class Meta:
|
||||
model = SearchPreference
|
||||
fields = ('search', 'lookup', 'unaccent', 'icontains', 'istartswith', 'trigram', 'fulltext', 'trigram_threshold')
|
||||
fields = (
|
||||
'search', 'lookup', 'unaccent', 'icontains', 'istartswith', 'trigram', 'fulltext', 'trigram_threshold')
|
||||
|
||||
help_texts = {
|
||||
'search': _('Select type method of search. Click <a href="/docs/search/">here</a> for full desciption of choices.'),
|
||||
'search': _(
|
||||
'Select type method of search. Click <a href="/docs/search/">here</a> for full desciption of choices.'),
|
||||
'lookup': _('Use fuzzy matching on units, keywords and ingredients when editing and importing recipes.'),
|
||||
'unaccent': _('Fields to search ignoring accents. Selecting this option can improve or degrade search quality depending on language'),
|
||||
'icontains': _("Fields to search for partial matches. (e.g. searching for 'Pie' will return 'pie' and 'piece' and 'soapie')"),
|
||||
'istartswith': _("Fields to search for beginning of word matches. (e.g. searching for 'sa' will return 'salad' and 'sandwich')"),
|
||||
'trigram': _("Fields to 'fuzzy' search. (e.g. searching for 'recpie' will find 'recipe'.) Note: this option will conflict with 'web' and 'raw' methods of search."),
|
||||
'fulltext': _("Fields to full text search. Note: 'web', 'phrase', and 'raw' search methods only function with fulltext fields."),
|
||||
'unaccent': _(
|
||||
'Fields to search ignoring accents. Selecting this option can improve or degrade search quality depending on language'),
|
||||
'icontains': _(
|
||||
"Fields to search for partial matches. (e.g. searching for 'Pie' will return 'pie' and 'piece' and 'soapie')"),
|
||||
'istartswith': _(
|
||||
"Fields to search for beginning of word matches. (e.g. searching for 'sa' will return 'salad' and 'sandwich')"),
|
||||
'trigram': _(
|
||||
"Fields to 'fuzzy' search. (e.g. searching for 'recpie' will find 'recipe'.) Note: this option will conflict with 'web' and 'raw' methods of search."),
|
||||
'fulltext': _(
|
||||
"Fields to full text search. Note: 'web', 'phrase', and 'raw' search methods only function with fulltext fields."),
|
||||
}
|
||||
|
||||
labels = {
|
||||
|
||||
@@ -5,7 +5,7 @@ from PIL import Image
|
||||
from io import BytesIO
|
||||
|
||||
|
||||
def rescale_image_jpeg(image_object, base_width=720):
|
||||
def rescale_image_jpeg(image_object, base_width=1020):
|
||||
img = Image.open(image_object)
|
||||
icc_profile = img.info.get('icc_profile') # remember color profile to not mess up colors
|
||||
width_percent = (base_width / float(img.size[0]))
|
||||
@@ -13,20 +13,20 @@ def rescale_image_jpeg(image_object, base_width=720):
|
||||
|
||||
img = img.resize((base_width, height), Image.ANTIALIAS)
|
||||
img_bytes = BytesIO()
|
||||
img.save(img_bytes, 'JPEG', quality=75, optimize=True, icc_profile=icc_profile)
|
||||
img.save(img_bytes, 'JPEG', quality=90, optimize=True, icc_profile=icc_profile)
|
||||
|
||||
return img_bytes
|
||||
|
||||
|
||||
def rescale_image_png(image_object, base_width=720):
|
||||
basewidth = 720
|
||||
wpercent = (basewidth / float(image_object.size[0]))
|
||||
def rescale_image_png(image_object, base_width=1020):
|
||||
image_object = Image.open(image_object)
|
||||
wpercent = (base_width / float(image_object.size[0]))
|
||||
hsize = int((float(image_object.size[1]) * float(wpercent)))
|
||||
img = image_object.resize((basewidth, hsize), Image.ANTIALIAS)
|
||||
img = image_object.resize((base_width, hsize), Image.ANTIALIAS)
|
||||
|
||||
im_io = BytesIO()
|
||||
img.save(im_io, 'PNG', quality=70)
|
||||
return img
|
||||
img.save(im_io, 'PNG', quality=90)
|
||||
return im_io
|
||||
|
||||
|
||||
def get_filetype(name):
|
||||
@@ -36,9 +36,11 @@ def get_filetype(name):
|
||||
return '.jpeg'
|
||||
|
||||
|
||||
# TODO this whole file needs proper documentation, refactoring, and testing
|
||||
# TODO also add env variable to define which images sizes should be compressed
|
||||
def handle_image(request, image_object, filetype='.jpeg'):
|
||||
if sys.getsizeof(image_object) / 8 > 500:
|
||||
if filetype == '.jpeg':
|
||||
if (image_object.size / 1000) > 500: # if larger than 500 kb compress
|
||||
if filetype == '.jpeg' or filetype == '.jpg':
|
||||
return rescale_image_jpeg(image_object), filetype
|
||||
if filetype == '.png':
|
||||
return rescale_image_png(image_object), filetype
|
||||
|
||||
@@ -38,6 +38,7 @@ def search_recipes(request, queryset, params):
|
||||
search_keywords = params.getlist('keywords', [])
|
||||
search_foods = params.getlist('foods', [])
|
||||
search_books = params.getlist('books', [])
|
||||
search_steps = params.getlist('steps', [])
|
||||
search_units = params.get('units', None)
|
||||
|
||||
# TODO I think default behavior should be 'AND' which is how most sites operate with facet/filters based on results
|
||||
@@ -191,6 +192,10 @@ def search_recipes(request, queryset, params):
|
||||
if search_units:
|
||||
queryset = queryset.filter(steps__ingredients__unit__id=search_units)
|
||||
|
||||
# probably only useful in Unit list view, so keeping it simple
|
||||
if search_steps:
|
||||
queryset = queryset.filter(steps__id__in=search_steps)
|
||||
|
||||
if search_internal:
|
||||
queryset = queryset.filter(internal=True)
|
||||
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
from django.urls import reverse
|
||||
from django_scopes import scope, scopes_disabled
|
||||
from rest_framework.authentication import TokenAuthentication
|
||||
from rest_framework.authtoken.models import Token
|
||||
from rest_framework.exceptions import AuthenticationFailed
|
||||
|
||||
from cookbook.views import views
|
||||
|
||||
@@ -33,6 +36,15 @@ class ScopeMiddleware:
|
||||
with scope(space=request.space):
|
||||
return self.get_response(request)
|
||||
else:
|
||||
if request.path.startswith('/api/'):
|
||||
try:
|
||||
if auth := TokenAuthentication().authenticate(request):
|
||||
request.space = auth[0].userpreference.space
|
||||
with scope(space=request.space):
|
||||
return self.get_response(request)
|
||||
except AuthenticationFailed:
|
||||
pass
|
||||
|
||||
with scopes_disabled():
|
||||
request.space = None
|
||||
return self.get_response(request)
|
||||
|
||||
@@ -14,7 +14,7 @@ from django.utils.translation import gettext as _
|
||||
from django_scopes import scope
|
||||
|
||||
from cookbook.forms import ImportExportBase
|
||||
from cookbook.helper.image_processing import get_filetype
|
||||
from cookbook.helper.image_processing import get_filetype, handle_image
|
||||
from cookbook.models import Keyword, Recipe
|
||||
from recipes.settings import DATABASES, DEBUG
|
||||
|
||||
@@ -52,7 +52,7 @@ class Integration:
|
||||
icon=icon,
|
||||
space=request.space
|
||||
)
|
||||
except IntegrityError: # in case, for whatever reason, the name does exist append UUID to it. Not nice but works for now.
|
||||
except IntegrityError: # in case, for whatever reason, the name does exist append UUID to it. Not nice but works for now.
|
||||
self.keyword = parent.add_child(
|
||||
name=f'{name} {str(uuid.uuid4())[0:8]}',
|
||||
description=description,
|
||||
@@ -123,8 +123,6 @@ class Integration:
|
||||
:return: HttpResponseRedirect to the recipe search showing all imported recipes
|
||||
"""
|
||||
with scope(space=self.request.space):
|
||||
self.keyword.name = _('Import') + ' ' + str(il.pk)
|
||||
self.keyword.save()
|
||||
|
||||
try:
|
||||
self.files = files
|
||||
@@ -231,15 +229,14 @@ class Integration:
|
||||
self.ignored_recipes.append(recipe.name)
|
||||
recipe.delete()
|
||||
|
||||
@staticmethod
|
||||
def import_recipe_image(recipe, image_file, filetype='.jpeg'):
|
||||
def import_recipe_image(self, recipe, image_file, filetype='.jpeg'):
|
||||
"""
|
||||
Adds an image to a recipe naming it correctly
|
||||
:param recipe: Recipe object
|
||||
:param image_file: ByteIO stream containing the image
|
||||
:param filetype: type of file to write bytes to, default to .jpeg if unknown
|
||||
"""
|
||||
recipe.image = File(image_file, name=f'{uuid.uuid4()}_{recipe.pk}{filetype}')
|
||||
recipe.image = File(handle_image(self.request, File(image_file, name='image'), filetype=filetype)[0], name=f'{uuid.uuid4()}_{recipe.pk}{filetype}')
|
||||
recipe.save()
|
||||
|
||||
def get_recipe_from_file(self, file):
|
||||
|
||||
@@ -63,7 +63,7 @@ class MealMaster(Integration):
|
||||
current_recipe = ''
|
||||
|
||||
for fl in file.readlines():
|
||||
line = fl.decode("ANSI")
|
||||
line = fl.decode("windows-1250")
|
||||
if (line.startswith('MMMMM') or line.startswith('-----')) and 'meal-master' in line.lower():
|
||||
if current_recipe != '':
|
||||
recipe_list.append(current_recipe)
|
||||
|
||||
@@ -5,8 +5,9 @@ from zipfile import ZipFile
|
||||
|
||||
from cookbook.helper.image_processing import get_filetype
|
||||
from cookbook.helper.ingredient_parser import IngredientParser
|
||||
from cookbook.helper.recipe_url_import import iso_duration_to_minutes
|
||||
from cookbook.integration.integration import Integration
|
||||
from cookbook.models import Recipe, Step, Ingredient
|
||||
from cookbook.models import Recipe, Step, Ingredient, Keyword
|
||||
|
||||
|
||||
class NextcloudCookbook(Integration):
|
||||
@@ -24,9 +25,24 @@ class NextcloudCookbook(Integration):
|
||||
created_by=self.request.user, internal=True,
|
||||
servings=recipe_json['recipeYield'], space=self.request.space)
|
||||
|
||||
# TODO parse times (given in PT2H3M )
|
||||
# @vabene check recipe_url_import.iso_duration_to_minutes I think it does what you are looking for
|
||||
# TODO parse keywords
|
||||
try:
|
||||
recipe.working_time = iso_duration_to_minutes(recipe_json['prepTime'])
|
||||
recipe.waiting_time = iso_duration_to_minutes(recipe_json['cookTime'])
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
if 'recipeCategory' in recipe_json:
|
||||
try:
|
||||
recipe.keywords.add(Keyword.objects.get_or_create(space=self.request.space, name=recipe_json['recipeCategory'])[0])
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
if 'keywords' in recipe_json:
|
||||
try:
|
||||
for x in recipe_json['keywords'].split(','):
|
||||
recipe.keywords.add(Keyword.objects.get_or_create(space=self.request.space, name=x)[0])
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
ingredients_added = False
|
||||
for s in recipe_json['recipeInstructions']:
|
||||
@@ -41,19 +57,28 @@ class NextcloudCookbook(Integration):
|
||||
|
||||
ingredient_parser = IngredientParser(self.request, True)
|
||||
for ingredient in recipe_json['recipeIngredient']:
|
||||
amount, unit, ingredient, note = parse(ingredient)
|
||||
f = get_food(ingredient, self.request.space)
|
||||
u = get_unit(unit, self.request.space)
|
||||
amount, unit, ingredient, note = ingredient_parser.parse(ingredient)
|
||||
f = ingredient_parser.get_food(ingredient)
|
||||
u = ingredient_parser.get_unit(unit)
|
||||
step.ingredients.add(Ingredient.objects.create(
|
||||
food=f, unit=u, amount=amount, note=note, space=self.request.space,
|
||||
))
|
||||
recipe.steps.add(step)
|
||||
|
||||
if 'nutrition' in recipe_json:
|
||||
try:
|
||||
recipe.nutrition.calories = recipe_json['nutrition']['calories'].replace(' kcal', '').replace(' ', '')
|
||||
recipe.nutrition.proteins = recipe_json['nutrition']['calories'].replace(' g', '').replace(',', '.').replace(' ', '')
|
||||
recipe.nutrition.fats = recipe_json['nutrition']['calories'].replace(' g', '').replace(',', '.').replace(' ', '')
|
||||
recipe.nutrition.carbohydrates = recipe_json['nutrition']['calories'].replace(' g', '').replace(',', '.').replace(' ', '')
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
for f in self.files:
|
||||
if '.zip' in f['name']:
|
||||
import_zip = ZipFile(f['file'])
|
||||
for z in import_zip.filelist:
|
||||
if re.match(f'^Recipes/{recipe.name}/full.jpg$', z.filename):
|
||||
if re.match(f'^(.)+{recipe.name}/full.jpg$', z.filename):
|
||||
self.import_recipe_image(recipe, BytesIO(import_zip.read(z.filename)), filetype=get_filetype(z.filename))
|
||||
|
||||
return recipe
|
||||
|
||||
@@ -78,7 +78,7 @@ class Plantoeat(Integration):
|
||||
current_recipe = ''
|
||||
|
||||
for fl in file.readlines():
|
||||
line = fl.decode("ANSI")
|
||||
line = fl.decode("windows-1250")
|
||||
if line.startswith('--------------'):
|
||||
if current_recipe != '':
|
||||
recipe_list.append(current_recipe)
|
||||
|
||||
@@ -62,7 +62,7 @@ class RezKonv(Integration):
|
||||
current_recipe = ''
|
||||
|
||||
for fl in file.readlines():
|
||||
line = fl.decode("ANSI")
|
||||
line = fl.decode("windows-1250")
|
||||
if line.startswith('=====') and 'rezkonv' in line.lower():
|
||||
if current_recipe != '':
|
||||
recipe_list.append(current_recipe)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
BIN
cookbook/locale/fi/LC_MESSAGES/django.mo
Normal file
BIN
cookbook/locale/fi/LC_MESSAGES/django.mo
Normal file
Binary file not shown.
2510
cookbook/locale/fi/LC_MESSAGES/django.po
Normal file
2510
cookbook/locale/fi/LC_MESSAGES/django.po
Normal file
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
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.
@@ -11,8 +11,8 @@ msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2021-02-09 18:01+0100\n"
|
||||
"PO-Revision-Date: 2021-10-23 09:06+0000\n"
|
||||
"Last-Translator: Tomasz Klimczak <klemensble@gmail.com>\n"
|
||||
"PO-Revision-Date: 2021-11-06 14:06+0000\n"
|
||||
"Last-Translator: retmas gh <tandoor@oppai.ovh>\n"
|
||||
"Language-Team: Polish <http://translate.tandoor.dev/projects/tandoor/"
|
||||
"recipes-backend/pl/>\n"
|
||||
"Language: pl\n"
|
||||
@@ -385,7 +385,7 @@ msgstr "Zabierz mnie na stronę główną"
|
||||
|
||||
#: .\cookbook\templates\404.html:35
|
||||
msgid "Report a Bug"
|
||||
msgstr "Raprtuj błąd"
|
||||
msgstr "Raportuj błąd"
|
||||
|
||||
#: .\cookbook\templates\account\login.html:7
|
||||
#: .\cookbook\templates\base.html:166
|
||||
|
||||
Binary file not shown.
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
BIN
cookbook/locale/ro/LC_MESSAGES/django.mo
Normal file
BIN
cookbook/locale/ro/LC_MESSAGES/django.mo
Normal file
Binary file not shown.
2572
cookbook/locale/ro/LC_MESSAGES/django.po
Normal file
2572
cookbook/locale/ro/LC_MESSAGES/django.po
Normal file
File diff suppressed because it is too large
Load Diff
BIN
cookbook/locale/sl/LC_MESSAGES/django.mo
Normal file
BIN
cookbook/locale/sl/LC_MESSAGES/django.mo
Normal file
Binary file not shown.
2575
cookbook/locale/sl/LC_MESSAGES/django.po
Normal file
2575
cookbook/locale/sl/LC_MESSAGES/django.po
Normal file
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
@@ -39,6 +39,11 @@ class RecipeSchema(AutoSchema):
|
||||
"description": 'Id of book a recipe should have. For multiple repeat parameter.',
|
||||
'schema': {'type': 'string', },
|
||||
})
|
||||
parameters.append({
|
||||
"name": 'steps', "in": "query", "required": False,
|
||||
"description": 'Id of a step a recipe should have. For multiple repeat parameter.',
|
||||
'schema': {'type': 'string', },
|
||||
})
|
||||
parameters.append({
|
||||
"name": 'keywords_or', "in": "query", "required": False,
|
||||
"description": 'If recipe should have all (AND) or any (OR) of the provided keywords.',
|
||||
@@ -86,7 +91,8 @@ class TreeSchema(AutoSchema):
|
||||
})
|
||||
parameters.append({
|
||||
"name": 'root', "in": "query", "required": False,
|
||||
"description": 'Return first level children of {obj} with ID [int]. Integer 0 will return root {obj}s.'.format(obj=api_name),
|
||||
"description": 'Return first level children of {obj} with ID [int]. Integer 0 will return root {obj}s.'.format(
|
||||
obj=api_name),
|
||||
'schema': {'type': 'int', },
|
||||
})
|
||||
parameters.append({
|
||||
@@ -110,3 +116,17 @@ class FilterSchema(AutoSchema):
|
||||
'schema': {'type': 'string', },
|
||||
})
|
||||
return parameters
|
||||
|
||||
|
||||
class QueryOnlySchema(AutoSchema):
|
||||
def get_path_parameters(self, path, method):
|
||||
if not is_list_view(path, method, self.view):
|
||||
return super(QueryOnlySchema, self).get_path_parameters(path, method)
|
||||
|
||||
parameters = super().get_path_parameters(path, method)
|
||||
parameters.append({
|
||||
"name": 'query', "in": "query", "required": False,
|
||||
"description": 'Query string matched (fuzzy) against object name.',
|
||||
'schema': {'type': 'string', },
|
||||
})
|
||||
return parameters
|
||||
|
||||
@@ -34,7 +34,8 @@ class ExtendedRecipeMixin(serializers.ModelSerializer):
|
||||
api_serializer = None
|
||||
# extended values are computationally expensive and not needed in normal circumstances
|
||||
try:
|
||||
if bool(int(self.context['request'].query_params.get('extended', False))) and self.__class__ == api_serializer:
|
||||
if bool(int(
|
||||
self.context['request'].query_params.get('extended', False))) and self.__class__ == api_serializer:
|
||||
return fields
|
||||
except AttributeError:
|
||||
pass
|
||||
@@ -49,11 +50,13 @@ class ExtendedRecipeMixin(serializers.ModelSerializer):
|
||||
|
||||
def get_image(self, obj):
|
||||
# TODO add caching
|
||||
recipes = Recipe.objects.filter(**{self.recipe_filter: obj}, space=obj.space).exclude(image__isnull=True).exclude(image__exact='')
|
||||
recipes = Recipe.objects.filter(**{self.recipe_filter: obj}, space=obj.space).exclude(
|
||||
image__isnull=True).exclude(image__exact='')
|
||||
try:
|
||||
if recipes.count() == 0 and obj.has_children():
|
||||
obj__in = self.recipe_filter + '__in'
|
||||
recipes = Recipe.objects.filter(**{obj__in: obj.get_descendants()}, space=obj.space).exclude(image__isnull=True).exclude(image__exact='') # if no recipes found - check whole tree
|
||||
recipes = Recipe.objects.filter(**{obj__in: obj.get_descendants()}, space=obj.space).exclude(
|
||||
image__isnull=True).exclude(image__exact='') # if no recipes found - check whole tree
|
||||
except AttributeError:
|
||||
# probably not a tree
|
||||
pass
|
||||
@@ -404,7 +407,10 @@ class FoodSerializer(UniqueFieldsMixin, WritableNestedModelSerializer, ExtendedR
|
||||
|
||||
class Meta:
|
||||
model = Food
|
||||
fields = ('id', 'name', 'description', 'recipe', 'ignore_shopping', 'supermarket_category', 'image', 'parent', 'numchild', 'numrecipe')
|
||||
fields = (
|
||||
'id', 'name', 'description', 'recipe', 'ignore_shopping', 'supermarket_category', 'image', 'parent',
|
||||
'numchild',
|
||||
'numrecipe')
|
||||
read_only_fields = ('id', 'numchild', 'parent', 'image')
|
||||
|
||||
|
||||
@@ -425,12 +431,13 @@ class IngredientSerializer(WritableNestedModelSerializer):
|
||||
)
|
||||
|
||||
|
||||
class StepSerializer(WritableNestedModelSerializer):
|
||||
class StepSerializer(WritableNestedModelSerializer, ExtendedRecipeMixin):
|
||||
ingredients = IngredientSerializer(many=True)
|
||||
ingredients_markdown = serializers.SerializerMethodField('get_ingredients_markdown')
|
||||
ingredients_vue = serializers.SerializerMethodField('get_ingredients_vue')
|
||||
file = UserFileViewSerializer(allow_null=True, required=False)
|
||||
step_recipe_data = serializers.SerializerMethodField('get_step_recipe_data')
|
||||
recipe_filter = 'steps'
|
||||
|
||||
def create(self, validated_data):
|
||||
validated_data['space'] = self.context['request'].space
|
||||
@@ -442,6 +449,9 @@ class StepSerializer(WritableNestedModelSerializer):
|
||||
def get_ingredients_markdown(self, obj):
|
||||
return obj.get_instruction_render()
|
||||
|
||||
def get_step_recipes(self, obj):
|
||||
return list(obj.recipe_set.values_list('id', flat=True).all())
|
||||
|
||||
def get_step_recipe_data(self, obj):
|
||||
# check if root type is recipe to prevent infinite recursion
|
||||
# can be improved later to allow multi level embedding
|
||||
@@ -452,7 +462,7 @@ class StepSerializer(WritableNestedModelSerializer):
|
||||
model = Step
|
||||
fields = (
|
||||
'id', 'name', 'type', 'instruction', 'ingredients', 'ingredients_markdown',
|
||||
'ingredients_vue', 'time', 'order', 'show_as_header', 'file', 'step_recipe', 'step_recipe_data'
|
||||
'ingredients_vue', 'time', 'order', 'show_as_header', 'file', 'step_recipe', 'step_recipe_data', 'numrecipe'
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
</p>
|
||||
<form method="POST" class="post-form">{% csrf_token %}
|
||||
{{ form|crispy }}
|
||||
<input type="submit" value="Submit" class="btn btn-success">
|
||||
<input type="submit" value="{% trans 'Save' %}" class="btn btn-success">
|
||||
<a href="{% url 'list_storage' %}"><button type="button" class="btn btn-primary">{% trans 'Manage External Storage' %}</button></a>
|
||||
</form>
|
||||
|
||||
|
||||
@@ -29,26 +29,25 @@
|
||||
style="height:50%"
|
||||
href="{% bookmarklet request %}"
|
||||
title="{% trans 'Drag me to your bookmarks to import recipes from anywhere' %}">
|
||||
<img src="{% static 'assets/favicon-16x16.png' %}">{% trans 'Bookmark Me!' %} </a>
|
||||
<img src="{% static 'assets/favicon-16x16.png' %}">{% trans 'Bookmark Me!' %} </a>
|
||||
</div>
|
||||
<nav class="nav nav-pills flex-sm-row" style="margin-bottom:10px">
|
||||
<nav class="nav nav-pills flex-sm-row mb-2">
|
||||
<a class="nav-link active" href="#nav-url" data-toggle="tab" role="tab" aria-controls="nav-url"
|
||||
aria-selected="true" @click="mode='url'">URL</a>
|
||||
aria-selected="true" @click="mode='url'">{% trans 'URL' %}</a>
|
||||
<a class="nav-link" href="#nav-app" data-toggle="tab" role="tab" aria-controls="nav-app"
|
||||
@click="mode='app'">App</a>
|
||||
@click="mode='app'">{% trans 'App' %}</a>
|
||||
<a class="nav-link" href="#nav-source" data-toggle="tab" role="tab" aria-controls="nav-source"
|
||||
@click="mode='source'">Source</a>
|
||||
@click="mode='source'">{% trans 'Source' %}</a>
|
||||
<a class="nav-link disabled" href="#nav-text" data-toggle="tab" role="tab" aria-controls="nav-text"
|
||||
@click="mode='text'">Text</a>
|
||||
@click="mode='text'">{% trans 'Text' %}</a>
|
||||
<a class="nav-link disabled" href="#nav-file" data-toggle="tab" role="tab" aria-controls="nav-file"
|
||||
@click="mode='file'">File</a>
|
||||
@click="mode='file'">{% trans 'File' %}</a>
|
||||
</nav>
|
||||
|
||||
|
||||
<div class="tab-content" id="nav-tabContent">
|
||||
<!-- Import URL -->
|
||||
<div class="tab-pane fade show active" id="nav-url" role="tabpanel">
|
||||
<div class="btn-group btn-group-toggle" data-toggle="buttons">
|
||||
<div class="btn-group btn-group-toggle mt-2" data-toggle="buttons">
|
||||
<label class="btn btn-outline-info btn-sm active" @click="automatic=true">
|
||||
<input type="radio" autocomplete="off" checked> Automatic
|
||||
</label>
|
||||
@@ -57,10 +56,13 @@
|
||||
<input type="radio" autocomplete="off"> Manual
|
||||
</label>
|
||||
</div>
|
||||
<div class="input-group my-2">
|
||||
<input class="form-control" v-model="remote_url" placeholder="{% trans 'Enter website URL' %}">
|
||||
<div role="group" class="input-group mt-4">
|
||||
<input type="text" v-model="remote_url"
|
||||
class="form-control form-control-lg form-control-borderless form-control-search form-control"
|
||||
placeholder="{% trans 'Enter website URL' %}">
|
||||
<div class="input-group-append">
|
||||
<button @click="loadRecipe()" class="btn btn-primary shadow-none" type="button"
|
||||
<button @click="loadRecipe()" class="btn btn-primary shadow-none"
|
||||
type="button"
|
||||
id="id_btn_search"><i class="fas fa-search"></i>
|
||||
</button>
|
||||
</div>
|
||||
@@ -106,7 +108,7 @@
|
||||
|
||||
<!-- Import JSON or HTML -->
|
||||
<div class=" tab-pane fade show" id="nav-source" role="tabpanel">
|
||||
<div class="btn-group btn-group-toggle" data-toggle="buttons">
|
||||
<div class="btn-group btn-group-toggle mt-2" data-toggle="buttons">
|
||||
<label class="btn btn-outline-info btn-sm active" @click="automatic=true">
|
||||
<input type="radio" autocomplete="off" checked> Automatic
|
||||
</label>
|
||||
@@ -115,7 +117,7 @@
|
||||
<input type="radio" autocomplete="off"> Manual
|
||||
</label>
|
||||
</div>
|
||||
<div class="input-group my-2">
|
||||
<div class="input-group mt-4">
|
||||
<textarea class="form-control input-group-append" v-model="source_data" rows=10
|
||||
placeholder="{% trans 'Paste json or html source here to load recipe.' %}"
|
||||
style="font-size: 12px">
|
||||
@@ -455,7 +457,8 @@
|
||||
|
||||
<div class="form-group">
|
||||
<label for="id_servings">{% trans 'Servings' %}</label>
|
||||
<b-form-input id="id_servings" class="form-control" v-model="recipe_data.servings" @change="recipe_data.servings = Math.round($event.replace(',','.'))"></b-form-input>
|
||||
<b-form-input id="id_servings" class="form-control" v-model="recipe_data.servings"
|
||||
@change="recipe_data.servings = Math.round($event.replace(',','.'))"></b-form-input>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -92,10 +92,10 @@ def recipe_last(recipe, user):
|
||||
@register.simple_tag
|
||||
def page_help(page_name):
|
||||
help_pages = {
|
||||
'edit_storage': 'https://vabene1111.github.io/recipes/features/external_recipes/',
|
||||
'view_shopping': 'https://vabene1111.github.io/recipes/features/shopping/',
|
||||
'view_import': 'https://vabene1111.github.io/recipes/features/import_export/',
|
||||
'view_export': 'https://vabene1111.github.io/recipes/features/import_export/',
|
||||
'edit_storage': 'https://docs.tandoor.dev/features/external_recipes/',
|
||||
'view_shopping': 'https://docs.tandoor.dev/features/shopping/',
|
||||
'view_import': 'https://docs.tandoor.dev/features/import_export/',
|
||||
'view_export': 'https://docs.tandoor.dev/features/import_export/',
|
||||
}
|
||||
|
||||
link = help_pages.get(page_name, '')
|
||||
|
||||
@@ -10,7 +10,8 @@ from cookbook.helper import dal
|
||||
|
||||
from .models import (Comment, Food, InviteLink, Keyword, MealPlan, Recipe,
|
||||
RecipeBook, RecipeBookEntry, RecipeImport, ShoppingList,
|
||||
Storage, Supermarket, SupermarketCategory, Sync, SyncLog, Unit, get_model_name, Automation, UserFile)
|
||||
Storage, Supermarket, SupermarketCategory, Sync, SyncLog, Unit, get_model_name, Automation,
|
||||
UserFile, Step)
|
||||
from .views import api, data, delete, edit, import_export, lists, new, views, telegram
|
||||
|
||||
router = routers.DefaultRouter()
|
||||
@@ -177,7 +178,7 @@ for m in generic_models:
|
||||
)
|
||||
)
|
||||
|
||||
vue_models = [Food, Keyword, Unit, Supermarket, SupermarketCategory, Automation, UserFile]
|
||||
vue_models = [Food, Keyword, Unit, Supermarket, SupermarketCategory, Automation, UserFile, Step]
|
||||
for m in vue_models:
|
||||
py_name = get_model_name(m)
|
||||
url_name = py_name.replace('_', '-')
|
||||
|
||||
@@ -48,7 +48,7 @@ from cookbook.models import (CookLog, Food, Ingredient, Keyword, MealPlan,
|
||||
from cookbook.provider.dropbox import Dropbox
|
||||
from cookbook.provider.local import Local
|
||||
from cookbook.provider.nextcloud import Nextcloud
|
||||
from cookbook.schemas import FilterSchema, RecipeSchema, TreeSchema
|
||||
from cookbook.schemas import FilterSchema, RecipeSchema, TreeSchema, QueryOnlySchema
|
||||
from cookbook.serializer import (FoodSerializer, IngredientSerializer,
|
||||
KeywordSerializer, MealPlanSerializer,
|
||||
MealTypeSerializer, RecipeBookSerializer,
|
||||
@@ -63,6 +63,7 @@ from cookbook.serializer import (FoodSerializer, IngredientSerializer,
|
||||
ViewLogSerializer, CookLogSerializer, RecipeBookEntrySerializer,
|
||||
RecipeOverviewSerializer, SupermarketSerializer, ImportLogSerializer,
|
||||
BookmarkletImportSerializer, SupermarketCategorySerializer, UserFileSerializer, SupermarketCategoryRelationSerializer, AutomationSerializer)
|
||||
from recipes import settings
|
||||
|
||||
|
||||
class StandardFilterMixin(ViewSetMixin):
|
||||
@@ -409,7 +410,7 @@ class RecipeBookViewSet(viewsets.ModelViewSet, StandardFilterMixin):
|
||||
permission_classes = [CustomIsOwner]
|
||||
|
||||
def get_queryset(self):
|
||||
self.queryset = self.queryset.filter(created_by=self.request.user).filter(space=self.request.space)
|
||||
self.queryset = self.queryset.filter(Q(created_by=self.request.user) | Q(shared=self.request.user)).filter(space=self.request.space)
|
||||
return super().get_queryset()
|
||||
|
||||
|
||||
@@ -497,9 +498,16 @@ class StepViewSet(viewsets.ModelViewSet):
|
||||
queryset = Step.objects
|
||||
serializer_class = StepSerializer
|
||||
permission_classes = [CustomIsUser]
|
||||
pagination_class = DefaultPagination
|
||||
schema = QueryOnlySchema()
|
||||
|
||||
def get_queryset(self):
|
||||
return self.queryset.filter(recipe__space=self.request.space)
|
||||
queryset = self.queryset.filter(recipe__space=self.request.space)
|
||||
|
||||
query = self.request.query_params.get('query', None)
|
||||
if query is not None:
|
||||
queryset = queryset.filter(Q(name__icontains=query) | Q(recipe__name__icontains=query))
|
||||
return queryset
|
||||
|
||||
|
||||
class RecipePagination(PageNumberPagination):
|
||||
@@ -718,10 +726,8 @@ def get_recipe_file(request, recipe_id):
|
||||
|
||||
@group_required('user')
|
||||
def sync_all(request):
|
||||
if request.space.demo:
|
||||
messages.add_message(
|
||||
request, messages.ERROR, _('This feature is not available in the demo version!')
|
||||
)
|
||||
if request.space.demo or settings.HOSTED:
|
||||
messages.add_message(request, messages.ERROR, _('This feature is not yet available in the hosted version of tandoor!'))
|
||||
return redirect('index')
|
||||
|
||||
monitors = Sync.objects.filter(active=True).filter(space=request.user.userpreference.space)
|
||||
|
||||
@@ -25,6 +25,7 @@ from cookbook.helper.recipe_url_import import parse_cooktime
|
||||
from cookbook.models import (Comment, Food, Ingredient, Keyword, Recipe,
|
||||
RecipeImport, Step, Sync, Unit, UserPreference)
|
||||
from cookbook.tables import SyncTable
|
||||
from recipes import settings
|
||||
|
||||
|
||||
@group_required('user')
|
||||
@@ -37,6 +38,10 @@ def sync(request):
|
||||
messages.add_message(request, messages.WARNING, _('You have more users than allowed in your space.'))
|
||||
return HttpResponseRedirect(reverse('index'))
|
||||
|
||||
if request.space.demo or settings.HOSTED:
|
||||
messages.add_message(request, messages.ERROR, _('This feature is not yet available in the hosted version of tandoor!'))
|
||||
return redirect('index')
|
||||
|
||||
if request.method == "POST":
|
||||
if not has_group_permission(request.user, ['admin']):
|
||||
messages.add_message(request, messages.ERROR, _('You do not have the required permissions to view this page!'))
|
||||
@@ -186,13 +191,12 @@ def import_url(request):
|
||||
|
||||
ingredient.save()
|
||||
step.ingredients.add(ingredient)
|
||||
print(ingredient)
|
||||
|
||||
if 'image' in data and data['image'] != '' and data['image'] is not None:
|
||||
try:
|
||||
response = requests.get(data['image'])
|
||||
|
||||
img, filetype = handle_image(request, BytesIO(response.content))
|
||||
img, filetype = handle_image(request, File(BytesIO(response.content), name='image'))
|
||||
recipe.image = File(
|
||||
img, name=f'{uuid.uuid4()}_{recipe.pk}{filetype}'
|
||||
)
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
import os
|
||||
|
||||
from django.contrib import messages
|
||||
from django.db.models import Q
|
||||
from django.http import HttpResponseRedirect
|
||||
from django.shortcuts import get_object_or_404, render
|
||||
from django.shortcuts import get_object_or_404, render, redirect
|
||||
from django.urls import reverse
|
||||
from django.utils.translation import gettext as _
|
||||
from django.views.generic import UpdateView
|
||||
from django.views.generic.edit import FormMixin
|
||||
from django_scopes import scopes_disabled
|
||||
|
||||
from cookbook.forms import (CommentForm, ExternalRecipeForm,
|
||||
MealPlanForm,
|
||||
@@ -17,12 +15,13 @@ from cookbook.forms import (CommentForm, ExternalRecipeForm,
|
||||
from cookbook.helper.permission_helper import (GroupRequiredMixin,
|
||||
OwnerRequiredMixin,
|
||||
group_required)
|
||||
from cookbook.models import (Comment, Ingredient, MealPlan,
|
||||
MealType, Recipe, RecipeBook, RecipeImport,
|
||||
from cookbook.models import (Comment, MealPlan,
|
||||
MealType, Recipe, RecipeImport,
|
||||
Storage, Sync, UserPreference)
|
||||
from cookbook.provider.dropbox import Dropbox
|
||||
from cookbook.provider.local import Local
|
||||
from cookbook.provider.nextcloud import Nextcloud
|
||||
from recipes import settings
|
||||
|
||||
|
||||
@group_required('guest')
|
||||
@@ -126,6 +125,10 @@ def edit_storage(request, pk):
|
||||
messages.add_message(request, messages.ERROR, _('You cannot edit this storage!'))
|
||||
return HttpResponseRedirect(reverse('list_storage'))
|
||||
|
||||
if request.space.demo or settings.HOSTED:
|
||||
messages.add_message(request, messages.ERROR, _('This feature is not yet available in the hosted version of tandoor!'))
|
||||
return redirect('index')
|
||||
|
||||
if request.method == "POST":
|
||||
form = StorageForm(request.POST, instance=instance)
|
||||
if form.is_valid():
|
||||
|
||||
@@ -221,6 +221,23 @@ def user_file(request):
|
||||
)
|
||||
|
||||
|
||||
@group_required('user')
|
||||
def step(request):
|
||||
# recipe-param is the name of the parameters used when filtering recipes by this attribute
|
||||
# model-name is the models.js name of the model, probably ALL-CAPS
|
||||
return render(
|
||||
request,
|
||||
'generic/model_template.html',
|
||||
{
|
||||
"title": _("Steps"),
|
||||
"config": {
|
||||
'model': "STEP", # *REQUIRED* name of the model in models.js
|
||||
'recipe_param': 'steps',
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@group_required('user')
|
||||
def shopping_list_new(request):
|
||||
# recipe-param is the name of the parameters used when filtering recipes by this attribute
|
||||
|
||||
@@ -19,6 +19,7 @@ from cookbook.helper.permission_helper import (GroupRequiredMixin,
|
||||
from cookbook.models import (InviteLink, MealPlan, MealType, Recipe,
|
||||
RecipeBook, RecipeImport, ShareLink, Step, UserPreference)
|
||||
from cookbook.views.edit import SpaceFormMixing
|
||||
from recipes import settings
|
||||
|
||||
|
||||
class RecipeCreate(GroupRequiredMixin, CreateView):
|
||||
@@ -90,6 +91,9 @@ class StorageCreate(GroupRequiredMixin, CreateView):
|
||||
obj.created_by = self.request.user
|
||||
obj.space = self.request.space
|
||||
obj.save()
|
||||
if self.request.space.demo or settings.HOSTED:
|
||||
messages.add_message(self.request, messages.ERROR, _('This feature is not yet available in the hosted version of tandoor!'))
|
||||
return redirect('index')
|
||||
return HttpResponseRedirect(reverse('edit_storage', kwargs={'pk': obj.pk}))
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
|
||||
@@ -287,7 +287,7 @@ def user_settings(request):
|
||||
if request.method == "POST":
|
||||
if 'preference_form' in request.POST:
|
||||
active_tab = 'preferences'
|
||||
form = UserPreferenceForm(request.POST, prefix='preference')
|
||||
form = UserPreferenceForm(request.POST, prefix='preference', space=request.space)
|
||||
if form.is_valid():
|
||||
if not up:
|
||||
up = UserPreference(user=request.user)
|
||||
@@ -380,9 +380,9 @@ def user_settings(request):
|
||||
|
||||
sp.save()
|
||||
if up:
|
||||
preference_form = UserPreferenceForm(instance=up)
|
||||
preference_form = UserPreferenceForm(instance=up, space=request.space)
|
||||
else:
|
||||
preference_form = UserPreferenceForm()
|
||||
preference_form = UserPreferenceForm( space=request.space)
|
||||
|
||||
fields_searched = len(sp.icontains.all()) + len(sp.istartswith.all()) + len(sp.trigram.all()) + len(
|
||||
sp.fulltext.all())
|
||||
|
||||
@@ -23,7 +23,7 @@ This application is developed using the Django framework for Python. They have e
|
||||
|
||||
1. Clone this repository wherever you like and install the Python language for your OS (at least version 3.8)
|
||||
2. Open it in your favorite editor/IDE (e.g. PyCharm)
|
||||
1. If you want, create a virutal environment for all your packages.
|
||||
1. If you want, create a virtual environment for all your packages.
|
||||
3. Install all required packages: `pip install -r requirements.txt`
|
||||
4. Run the migrations: `python manage.py migrate`
|
||||
5. Start the development server: `python manage.py runserver`
|
||||
@@ -59,6 +59,8 @@ folder of the GitHub repository.
|
||||
|
||||
In order to contribute to the documentation you can fork the repository and edit the markdown files in the browser.
|
||||
|
||||
Now install mkdocs and dependencies: `pip install mkdocs-material mkdocs-include-markdown-plugin`.
|
||||
|
||||
If you want to test the documentation locally run `mkdocs serve` from the project root.
|
||||
|
||||
## Contribute Translations
|
||||
|
||||
11
docs/faq.md
11
docs/faq.md
@@ -37,4 +37,13 @@ There is only one installation of the Dropbox system, but it handles multiple us
|
||||
For Tandoor that means all people that work together on one recipe collection can be in one space.
|
||||
If you want to host the collection of your friends family or your neighbor you can create a separate space for them (trough the admin interface).
|
||||
|
||||
Sharing between spaces is currently not possible but is planned for future releases.
|
||||
Sharing between spaces is currently not possible but is planned for future releases.
|
||||
|
||||
## Create Admin user / reset passwords
|
||||
To create a superuser or reset a lost password if access to the container is lost you need to
|
||||
|
||||
1. execute into the container using `docker-compose exec web_recipes sh`
|
||||
2. activate the virtual environment `source venv/bin/activate`
|
||||
3. run `python manage.py createsuperuser` and follow the steps shown.
|
||||
|
||||
To change a password enter `python manage.py changepassword <username>` in step 3.
|
||||
@@ -2,13 +2,13 @@ This application features a very versatile import and export feature in order
|
||||
to offer the best experience possible and allow you to freely choose where your data goes.
|
||||
|
||||
!!! warning "WIP"
|
||||
The Module is relatively new. There is a know issue with [Timeouts](https://github.com/vabene1111/recipes/issues/417) on large exports.
|
||||
The Module is relatively new. There is a known issue with [Timeouts](https://github.com/vabene1111/recipes/issues/417) on large exports.
|
||||
A fix is being developed and will likely be released with the next version.
|
||||
|
||||
The Module is build with maximum flexibility and expandability in mind and allows to easily add new
|
||||
The Module is built with maximum flexibility and expandability in mind and allows to easily add new
|
||||
integrations to allow you to both import and export your recipes into whatever format you desire.
|
||||
|
||||
Feel like there is an important integration missing ? Just take a look at the [integration issues](https://github.com/vabene1111/recipes/issues?q=is%3Aissue+is%3Aopen+label%3Aintegration) or open a new one
|
||||
Feel like there is an important integration missing? Just take a look at the [integration issues](https://github.com/vabene1111/recipes/issues?q=is%3Aissue+is%3Aopen+label%3Aintegration) or open a new one
|
||||
if your favorite one is missing.
|
||||
|
||||
!!! info "Export"
|
||||
@@ -41,7 +41,7 @@ Overview of the capabilities of the different integrations.
|
||||
✔ = implemented, ❌ = not implemented and not possible/planned, ⌚ = not yet implemented
|
||||
|
||||
## Default
|
||||
The default integration is the build in (and preferred) way to import and export recipes.
|
||||
The default integration is the built in (and preferred) way to import and export recipes.
|
||||
It is maintained with new fields added and contains all data to transfer your recipes from one installation to another.
|
||||
|
||||
It is also one of the few recipe formats that is actually structured in a way that allows for
|
||||
@@ -90,7 +90,7 @@ Mealie provides structured data similar to nextcloud.
|
||||
|
||||
To migrate your recipes
|
||||
|
||||
1. Go to you Mealie settings and create a new Backup
|
||||
1. Go to your Mealie settings and create a new Backup
|
||||
2. Download the backup by clicking on it and pressing download (this wasn't working for me, so I had to manually pull it from the server)
|
||||
3. Upload the entire `.zip` file to the importer page and import everything
|
||||
|
||||
@@ -118,7 +118,7 @@ Recipes.zip/
|
||||
```
|
||||
|
||||
## Safron
|
||||
Go to you safron settings page and export your recipes.
|
||||
Go to your safron settings page and export your recipes.
|
||||
Then simply upload the entire `.zip` file to the importer.
|
||||
|
||||
!!! warning "Images"
|
||||
@@ -131,8 +131,8 @@ The `.paprikarecipes` file is basically just a zip with gzipped contents. Simply
|
||||
all your recipes.
|
||||
|
||||
## Pepperplate
|
||||
Pepperplate provides a `.zip` files contain all your recipes as `.txt` files. These files are well-structured and allow
|
||||
the import of all data without loosing anything.
|
||||
Pepperplate provides a `.zip` file containing all of your recipes as `.txt` files. These files are well-structured and allow
|
||||
the import of all data without losing anything.
|
||||
|
||||
Simply export the recipes from Pepperplate and upload the zip to Tandoor. Images are not included in the export and
|
||||
thus cannot be imported.
|
||||
@@ -145,7 +145,7 @@ This format is basically completely unstructured and every export looks differen
|
||||
and leads to suboptimal results. Images are also not supported as they are not included in the export (at least
|
||||
the tests I had).
|
||||
|
||||
Usually the import should recognize all ingredients and put everything else into the instructions. If you import fails
|
||||
Usually the import should recognize all ingredients and put everything else into the instructions. If your import fails
|
||||
or is worse than this feel free to provide me with more example data and I can try to improve the importer.
|
||||
|
||||
As ChefTap cannot import these files anyway there won't be an exporter implemented in Tandoor.
|
||||
@@ -154,7 +154,7 @@ As ChefTap cannot import these files anyway there won't be an exporter implement
|
||||
Meal master can be imported by uploading one or more meal master files.
|
||||
The files should either be `.txt`, `.MMF` or `.MM` files.
|
||||
|
||||
The MealMaster spec allow for many variations. Currently, only the on column format for ingredients is supported.
|
||||
The MealMaster spec allow for many variations. Currently, only the one column format for ingredients is supported.
|
||||
Second line notes to ingredients are currently also not imported as a note but simply put into the instructions.
|
||||
If you have MealMaster recipes that cannot be imported feel free to raise an issue.
|
||||
|
||||
@@ -166,7 +166,7 @@ The generated file can simply be imported into Tandoor.
|
||||
As I only had limited sample data feel free to open an issue if your RezKonv export cannot be imported.
|
||||
|
||||
## Recipekeeper
|
||||
Recipe keeper allows to export a zip file containing recipes and images using its apps.
|
||||
Recipe keeper allows you to export a zip file containing recipes and images using its apps.
|
||||
This zip file can simply be imported into Tandoor.
|
||||
|
||||
## OpenEats
|
||||
@@ -213,8 +213,8 @@ Store the outputted json string in a `.json` file and simply import it using the
|
||||
|
||||
## Plantoeat
|
||||
|
||||
Plan to eat allow to export a text file containing all your recipes. Simply upload that text file to Tandoor to import all recipes
|
||||
Plan to eat allows you to export a text file containing all your recipes. Simply upload that text file to Tandoor to import all recipes
|
||||
|
||||
## CookBookApp
|
||||
|
||||
CookBookApp can export .zip files containing .html files. Upload the entire ZIP to Tandoor to import all included recipes.
|
||||
CookBookApp can export .zip files containing .html files. Upload the entire ZIP to Tandoor to import all included recipes.
|
||||
|
||||
@@ -17,7 +17,7 @@ from recipe scaling.
|
||||
Currently the only available variable in the Templating context is `ingredients`.
|
||||
|
||||
`ingredients` is an array that contains all ingredients of the current recipe step. You can access an ingredient by using
|
||||
`{{ ingredient[<index in list>] }}` where the index refers to the position in the list of ingredients starting with zero.
|
||||
`{{ ingredients[<index in list>] }}` where the index refers to the position in the list of ingredients starting with zero.
|
||||
You can also use the interaction menu of the ingredient to copy its reference.
|
||||
|
||||
!!! warning
|
||||
@@ -28,10 +28,10 @@ You can also use the interaction menu of the ingredient to copy its reference.
|
||||
|
||||
You can also access only the amount, unit, note or food inside your instruction text using
|
||||
```
|
||||
{{ instructions[0].amount }}
|
||||
{{ instructions[0].unit }}
|
||||
{{ instructions[0].food }}
|
||||
{{ instructions[0].note }}
|
||||
{{ ingredients[0].amount }}
|
||||
{{ ingredients[0].unit }}
|
||||
{{ ingredients[0].food }}
|
||||
{{ ingredients[0].note }}
|
||||
```
|
||||
|
||||
## Technical Reasoning
|
||||
@@ -43,4 +43,4 @@ The template could access them by ID, the food name or the position in the list.
|
||||
2. **Name**: very nice to read and easy but does not work when a food occurs twice in a step. Could have workaround but would then be inconsistent.
|
||||
3. **Position**: easy to write and understand but breaks when ordering is changed and not really nice to read when instructions are not rendered.
|
||||
|
||||
I decided to go for the position based system. If you know of any better way feel free to open an issue or PR.
|
||||
I decided to go for the position based system. If you know of any better way feel free to open an issue or PR.
|
||||
|
||||
@@ -65,47 +65,9 @@ This configuration exposes the application through an nginx web server on port 8
|
||||
wget https://raw.githubusercontent.com/vabene1111/recipes/develop/docs/install/docker/plain/docker-compose.yml
|
||||
```
|
||||
|
||||
```yaml
|
||||
version: "3"
|
||||
services:
|
||||
db_recipes:
|
||||
restart: always
|
||||
image: postgres:11-alpine
|
||||
volumes:
|
||||
- ./postgresql:/var/lib/postgresql/data
|
||||
env_file:
|
||||
- ./.env
|
||||
|
||||
web_recipes:
|
||||
image: vabene1111/recipes
|
||||
restart: always
|
||||
env_file:
|
||||
- ./.env
|
||||
volumes:
|
||||
- staticfiles:/opt/recipes/staticfiles
|
||||
- nginx_config:/opt/recipes/nginx/conf.d
|
||||
- ./mediafiles:/opt/recipes/mediafiles
|
||||
depends_on:
|
||||
- db_recipes
|
||||
|
||||
nginx_recipes:
|
||||
image: nginx:mainline-alpine
|
||||
restart: always
|
||||
ports:
|
||||
- 80:80
|
||||
env_file:
|
||||
- ./.env
|
||||
depends_on:
|
||||
- web_recipes
|
||||
volumes:
|
||||
- nginx_config:/etc/nginx/conf.d:ro
|
||||
- staticfiles:/static
|
||||
- ./mediafiles:/media
|
||||
|
||||
volumes:
|
||||
nginx_config:
|
||||
staticfiles:
|
||||
```
|
||||
~~~yaml
|
||||
{% include "./docker/plain/docker-compose.yml" %}
|
||||
~~~
|
||||
|
||||
### Reverse Proxy
|
||||
|
||||
@@ -123,62 +85,9 @@ If you use traefik, this configuration is the one for you.
|
||||
wget https://raw.githubusercontent.com/vabene1111/recipes/develop/docs/install/docker/traefik-nginx/docker-compose.yml
|
||||
```
|
||||
|
||||
```yaml
|
||||
version: "3"
|
||||
services:
|
||||
db_recipes:
|
||||
restart: always
|
||||
image: postgres:11-alpine
|
||||
volumes:
|
||||
- ./postgresql:/var/lib/postgresql/data
|
||||
env_file:
|
||||
- ./.env
|
||||
networks:
|
||||
- default
|
||||
|
||||
web_recipes:
|
||||
image: vabene1111/recipes
|
||||
restart: always
|
||||
env_file:
|
||||
- ./.env
|
||||
volumes:
|
||||
- staticfiles:/opt/recipes/staticfiles
|
||||
- nginx_config:/opt/recipes/nginx/conf.d
|
||||
- ./mediafiles:/opt/recipes/mediafiles
|
||||
depends_on:
|
||||
- db_recipes
|
||||
networks:
|
||||
- default
|
||||
|
||||
nginx_recipes:
|
||||
image: nginx:mainline-alpine
|
||||
restart: always
|
||||
env_file:
|
||||
- ./.env
|
||||
volumes:
|
||||
- nginx_config:/etc/nginx/conf.d:ro
|
||||
- staticfiles:/static
|
||||
- ./mediafiles:/media
|
||||
labels: # traefik example labels
|
||||
- "traefik.enable=true"
|
||||
- "traefik.http.routers.recipes.rule=Host(`recipes.mydomain.com`, `recipes.myotherdomain.com`)"
|
||||
- "traefik.http.routers.recipes.entrypoints=web_secure" # your https endpoint
|
||||
- "traefik.http.routers.recipes.tls.certresolver=le_resolver" # your cert resolver
|
||||
depends_on:
|
||||
- web_recipes
|
||||
networks:
|
||||
- default
|
||||
- traefik
|
||||
|
||||
networks:
|
||||
default:
|
||||
traefik: # This is you external traefik network
|
||||
external: true
|
||||
|
||||
volumes:
|
||||
nginx_config:
|
||||
staticfiles:
|
||||
```
|
||||
~~~yaml
|
||||
{% include "./docker/traefik-nginx/docker-compose.yml" %}
|
||||
~~~
|
||||
|
||||
#### nginx-proxy
|
||||
|
||||
@@ -198,58 +107,9 @@ LETSENCRYPT_EMAIL=
|
||||
wget https://raw.githubusercontent.com/vabene1111/recipes/develop/docs/install/docker/nginx-proxy/docker-compose.yml
|
||||
```
|
||||
|
||||
```yaml
|
||||
version: "3"
|
||||
services:
|
||||
db_recipes:
|
||||
restart: always
|
||||
image: postgres:11-alpine
|
||||
volumes:
|
||||
- ./postgresql:/var/lib/postgresql/data
|
||||
env_file:
|
||||
- ./.env
|
||||
networks:
|
||||
- default
|
||||
|
||||
web_recipes:
|
||||
image: vabene1111/recipes
|
||||
restart: always
|
||||
env_file:
|
||||
- ./.env
|
||||
volumes:
|
||||
- staticfiles:/opt/recipes/staticfiles
|
||||
- nginx_config:/opt/recipes/nginx/conf.d
|
||||
- ./mediafiles:/opt/recipes/mediafiles
|
||||
depends_on:
|
||||
- db_recipes
|
||||
networks:
|
||||
- default
|
||||
|
||||
nginx_recipes:
|
||||
image: nginx:mainline-alpine
|
||||
restart: always
|
||||
env_file:
|
||||
- ./.env
|
||||
depends_on:
|
||||
- web_recipes
|
||||
volumes:
|
||||
- nginx_config:/etc/nginx/conf.d:ro
|
||||
- staticfiles:/static
|
||||
- ./mediafiles:/media
|
||||
networks:
|
||||
- default
|
||||
- nginx-proxy
|
||||
|
||||
networks:
|
||||
default:
|
||||
nginx-proxy:
|
||||
external:
|
||||
name: nginx-proxy
|
||||
|
||||
volumes:
|
||||
nginx_config:
|
||||
staticfiles:
|
||||
```
|
||||
~~~yaml
|
||||
{% include "./docker/nginx-proxy/docker-compose.yml" %}
|
||||
~~~
|
||||
|
||||
## Additional Information
|
||||
|
||||
|
||||
@@ -4,12 +4,14 @@ metadata:
|
||||
labels:
|
||||
app: recipes
|
||||
name: recipes-nginx-config
|
||||
namespace: default
|
||||
data:
|
||||
nginx-config: |-
|
||||
events {
|
||||
worker_connections 1024;
|
||||
}
|
||||
http {
|
||||
include mime.types;
|
||||
server {
|
||||
listen 80;
|
||||
server_name _;
|
||||
@@ -24,10 +26,5 @@ data:
|
||||
location /media/ {
|
||||
alias /media/;
|
||||
}
|
||||
# pass requests for dynamic content to gunicorn
|
||||
location / {
|
||||
proxy_set_header Host $host;
|
||||
proxy_pass http://localhost:8080;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
13
docs/install/k8s/15-secrets.yaml
Normal file
13
docs/install/k8s/15-secrets.yaml
Normal file
@@ -0,0 +1,13 @@
|
||||
kind: Secret
|
||||
apiVersion: v1
|
||||
metadata:
|
||||
name: recipes
|
||||
namespace: default
|
||||
type: Opaque
|
||||
data:
|
||||
# echo -n 'db-password' | base64
|
||||
postgresql-password: ZGItcGFzc3dvcmQ=
|
||||
# echo -n 'postgres-user-password' | base64
|
||||
postgresql-postgres-password: cG9zdGdyZXMtdXNlci1wYXNzd29yZA==
|
||||
# echo -n 'secret-key' | sha256sum | awk '{ printf $1 }' | base64
|
||||
secret-key: ODVkYmUxNWQ3NWVmOTMwOGM3YWUwZjMzYzdhMzI0Y2M2ZjRiZjUxOWEyZWQyZjMwMjdiZDMzYzE0MGE0ZjlhYQ==
|
||||
5
docs/install/k8s/20-service-account.yml
Normal file
5
docs/install/k8s/20-service-account.yml
Normal file
@@ -0,0 +1,5 @@
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: recipes
|
||||
namespace: default
|
||||
@@ -1,50 +0,0 @@
|
||||
apiVersion: v1
|
||||
kind: PersistentVolume
|
||||
metadata:
|
||||
name: recipes-db
|
||||
labels:
|
||||
app: recipes
|
||||
type: local
|
||||
tier: db
|
||||
spec:
|
||||
storageClassName: manual
|
||||
capacity:
|
||||
storage: 1Gi
|
||||
accessModes:
|
||||
- ReadWriteMany
|
||||
hostPath:
|
||||
path: "/data/recipes/db"
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: PersistentVolume
|
||||
metadata:
|
||||
name: recipes-media
|
||||
labels:
|
||||
app: recipes
|
||||
type: local
|
||||
tier: media
|
||||
spec:
|
||||
storageClassName: manual
|
||||
capacity:
|
||||
storage: 1Gi
|
||||
accessModes:
|
||||
- ReadWriteMany
|
||||
hostPath:
|
||||
path: "/data/recipes/media"
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: PersistentVolume
|
||||
metadata:
|
||||
name: recipes-static
|
||||
labels:
|
||||
app: recipes
|
||||
type: local
|
||||
tier: static
|
||||
spec:
|
||||
storageClassName: manual
|
||||
capacity:
|
||||
storage: 1Gi
|
||||
accessModes:
|
||||
- ReadWriteMany
|
||||
hostPath:
|
||||
path: "/data/recipes/static"
|
||||
@@ -1,34 +1,13 @@
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
name: recipes-db
|
||||
labels:
|
||||
app: recipes
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
tier: db
|
||||
storageClassName: manual
|
||||
accessModes:
|
||||
- ReadWriteMany
|
||||
resources:
|
||||
requests:
|
||||
storage: 1Gi
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
name: recipes-media
|
||||
namespace: default
|
||||
labels:
|
||||
app: recipes
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
tier: media
|
||||
app: recipes
|
||||
storageClassName: manual
|
||||
accessModes:
|
||||
- ReadWriteMany
|
||||
- ReadWriteOnce
|
||||
resources:
|
||||
requests:
|
||||
storage: 1Gi
|
||||
@@ -37,16 +16,12 @@ apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
name: recipes-static
|
||||
namespace: default
|
||||
labels:
|
||||
app: recipes
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
tier: static
|
||||
app: recipes
|
||||
storageClassName: manual
|
||||
accessModes:
|
||||
- ReadWriteMany
|
||||
- ReadWriteOnce
|
||||
resources:
|
||||
requests:
|
||||
storage: 1Gi
|
||||
|
||||
142
docs/install/k8s/40-sts-postgresql.yaml
Normal file
142
docs/install/k8s/40-sts-postgresql.yaml
Normal file
@@ -0,0 +1,142 @@
|
||||
apiVersion: apps/v1
|
||||
kind: StatefulSet
|
||||
metadata:
|
||||
labels:
|
||||
app: recipes
|
||||
tier: database
|
||||
name: recipes-postgresql
|
||||
namespace: default
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: recipes
|
||||
serviceName: recipes-postgresql
|
||||
updateStrategy:
|
||||
type: RollingUpdate
|
||||
template:
|
||||
metadata:
|
||||
annotations:
|
||||
backup.velero.io/backup-volumes: data
|
||||
labels:
|
||||
app: recipes
|
||||
tier: database
|
||||
name: recipes-postgresql
|
||||
namespace: default
|
||||
spec:
|
||||
restartPolicy: Always
|
||||
securityContext:
|
||||
fsGroup: 999
|
||||
serviceAccount: recipes
|
||||
serviceAccountName: recipes
|
||||
terminationGracePeriodSeconds: 30
|
||||
containers:
|
||||
- name: recipes-db
|
||||
env:
|
||||
- name: BITNAMI_DEBUG
|
||||
value: "false"
|
||||
- name: POSTGRESQL_PORT_NUMBER
|
||||
value: "5432"
|
||||
- name: POSTGRESQL_VOLUME_DIR
|
||||
value: /bitnami/postgresql
|
||||
- name: PGDATA
|
||||
value: /bitnami/postgresql/data
|
||||
- name: POSTGRES_USER
|
||||
value: recipes
|
||||
- name: POSTGRES_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: recipes
|
||||
key: postgresql-password
|
||||
- name: POSTGRESQL_POSTGRES_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: recipes
|
||||
key: postgresql-postgres-password
|
||||
- name: POSTGRES_DB
|
||||
value: recipes
|
||||
image: docker.io/bitnami/postgresql:11.5.0-debian-9-r60
|
||||
imagePullPolicy: IfNotPresent
|
||||
livenessProbe:
|
||||
exec:
|
||||
command:
|
||||
- sh
|
||||
- -c
|
||||
- exec pg_isready -U "postgres" -d "wiki" -h 127.0.0.1 -p 5432
|
||||
failureThreshold: 6
|
||||
initialDelaySeconds: 30
|
||||
periodSeconds: 10
|
||||
successThreshold: 1
|
||||
timeoutSeconds: 5
|
||||
ports:
|
||||
- containerPort: 5432
|
||||
name: postgresql
|
||||
protocol: TCP
|
||||
readinessProbe:
|
||||
exec:
|
||||
command:
|
||||
- sh
|
||||
- -c
|
||||
- -e
|
||||
- |
|
||||
pg_isready -U "postgres" -d "wiki" -h 127.0.0.1 -p 5432
|
||||
[ -f /opt/bitnami/postgresql/tmp/.initialized ]
|
||||
failureThreshold: 6
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 10
|
||||
successThreshold: 1
|
||||
timeoutSeconds: 5
|
||||
resources:
|
||||
requests:
|
||||
cpu: 250m
|
||||
memory: 256Mi
|
||||
securityContext:
|
||||
runAsUser: 1001
|
||||
terminationMessagePath: /dev/termination-log
|
||||
terminationMessagePolicy: File
|
||||
volumeMounts:
|
||||
- mountPath: /bitnami/postgresql
|
||||
name: data
|
||||
dnsPolicy: ClusterFirst
|
||||
initContainers:
|
||||
- command:
|
||||
- sh
|
||||
- -c
|
||||
- |
|
||||
mkdir -p /bitnami/postgresql/data
|
||||
chmod 700 /bitnami/postgresql/data
|
||||
find /bitnami/postgresql -mindepth 0 -maxdepth 1 -not -name ".snapshot" -not -name "lost+found" | \
|
||||
xargs chown -R 1001:1001
|
||||
image: docker.io/bitnami/minideb:stretch
|
||||
imagePullPolicy: Always
|
||||
name: init-chmod-data
|
||||
resources:
|
||||
requests:
|
||||
cpu: 250m
|
||||
memory: 256Mi
|
||||
securityContext:
|
||||
runAsUser: 0
|
||||
volumeMounts:
|
||||
- mountPath: /bitnami/postgresql
|
||||
name: data
|
||||
restartPolicy: Always
|
||||
securityContext:
|
||||
fsGroup: 1001
|
||||
serviceAccount: recipes
|
||||
serviceAccountName: recipes
|
||||
terminationGracePeriodSeconds: 30
|
||||
updateStrategy:
|
||||
type: RollingUpdate
|
||||
volumeClaimTemplates:
|
||||
- apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
name: data
|
||||
namespace: default
|
||||
spec:
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
resources:
|
||||
requests:
|
||||
storage: 2Gi
|
||||
volumeMode: Filesystem
|
||||
19
docs/install/k8s/45-service-db.yaml
Normal file
19
docs/install/k8s/45-service-db.yaml
Normal file
@@ -0,0 +1,19 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
labels:
|
||||
app: recipes
|
||||
tier: database
|
||||
name: recipes-postgresql
|
||||
namespace: default
|
||||
spec:
|
||||
ports:
|
||||
- name: postgresql
|
||||
port: 5432
|
||||
protocol: TCP
|
||||
targetPort: postgresql
|
||||
selector:
|
||||
app: recipes
|
||||
tier: database
|
||||
sessionAffinity: None
|
||||
type: ClusterIP
|
||||
@@ -2,6 +2,7 @@ apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: recipes
|
||||
namespace: default
|
||||
labels:
|
||||
app: recipes
|
||||
environment: production
|
||||
@@ -9,17 +10,78 @@ metadata:
|
||||
spec:
|
||||
replicas: 1
|
||||
strategy:
|
||||
type: RollingUpdate
|
||||
type: Recreate
|
||||
selector:
|
||||
matchLabels:
|
||||
app: recipes
|
||||
environment: production
|
||||
template:
|
||||
metadata:
|
||||
annotations:
|
||||
backup.velero.io/backup-volumes: media,static
|
||||
labels:
|
||||
app: recipes
|
||||
tier: frontend
|
||||
environment: production
|
||||
spec:
|
||||
restartPolicy: Always
|
||||
serviceAccount: recipes
|
||||
serviceAccountName: recipes
|
||||
initContainers:
|
||||
- name: init-chmod-data
|
||||
env:
|
||||
- name: SECRET_KEY
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: recipes
|
||||
key: secret-key
|
||||
- name: DB_ENGINE
|
||||
value: django.db.backends.postgresql_psycopg2
|
||||
- name: POSTGRES_HOST
|
||||
value: recipes-postgresql
|
||||
- name: POSTGRES_PORT
|
||||
value: "5432"
|
||||
- name: POSTGRES_USER
|
||||
value: postgres
|
||||
- name: POSTGRES_DB
|
||||
value: recipes
|
||||
- name: POSTGRES_PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: recipes
|
||||
key: postgresql-postgres-password
|
||||
image: vabene1111/recipes:1.0.1
|
||||
imagePullPolicy: Always
|
||||
resources:
|
||||
requests:
|
||||
cpu: 250m
|
||||
memory: 64Mi
|
||||
command:
|
||||
- sh
|
||||
- -c
|
||||
- |
|
||||
set -e
|
||||
source venv/bin/activate
|
||||
echo "Updating database"
|
||||
python manage.py migrate
|
||||
python manage.py collectstatic_js_reverse
|
||||
python manage.py collectstatic --noinput
|
||||
echo "Setting media file attributes"
|
||||
chown -R 65534:65534 /opt/recipes/mediafiles
|
||||
find /opt/recipes/mediafiles -type d | xargs -r chmod 755
|
||||
find /opt/recipes/mediafiles -type f | xargs -r chmod 644
|
||||
echo "Done"
|
||||
securityContext:
|
||||
runAsUser: 0
|
||||
volumeMounts:
|
||||
- mountPath: /opt/recipes/mediafiles
|
||||
name: media
|
||||
# mount as subPath due to lost+found on ext4 pvc
|
||||
subPath: files
|
||||
- mountPath: /opt/recipes/staticfiles
|
||||
name: static
|
||||
# mount as subPath due to lost+found on ext4 pvc
|
||||
subPath: files
|
||||
containers:
|
||||
- name: recipes-nginx
|
||||
image: nginx:latest
|
||||
@@ -28,69 +90,94 @@ spec:
|
||||
- containerPort: 80
|
||||
protocol: TCP
|
||||
name: http
|
||||
- containerPort: 8080
|
||||
protocol: TCP
|
||||
name: gunicorn
|
||||
resources:
|
||||
requests:
|
||||
cpu: 250m
|
||||
memory: 64Mi
|
||||
volumeMounts:
|
||||
- mountPath: '/media'
|
||||
- mountPath: /media
|
||||
name: media
|
||||
- mountPath: '/static'
|
||||
# mount as subPath due to lost+found on ext4 pvc
|
||||
subPath: files
|
||||
- mountPath: /static
|
||||
name: static
|
||||
# mount as subPath due to lost+found on ext4 pvc
|
||||
subPath: files
|
||||
- name: nginx-config
|
||||
mountPath: /etc/nginx/nginx.conf
|
||||
subPath: nginx-config
|
||||
readOnly: true
|
||||
- name: recipes
|
||||
image: 'vabene1111/recipes:latest'
|
||||
image: vabene1111/recipes:1.0.1
|
||||
imagePullPolicy: IfNotPresent
|
||||
command:
|
||||
- /opt/recipes/venv/bin/gunicorn
|
||||
- -b
|
||||
- :8080
|
||||
- --access-logfile
|
||||
- "-"
|
||||
- --error-logfile
|
||||
- "-"
|
||||
- --log-level
|
||||
- INFO
|
||||
- recipes.wsgi
|
||||
livenessProbe:
|
||||
failureThreshold: 3
|
||||
httpGet:
|
||||
path: /
|
||||
port: 8080
|
||||
scheme: HTTP
|
||||
periodSeconds: 30
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /
|
||||
port: 8080
|
||||
scheme: HTTP
|
||||
periodSeconds: 30
|
||||
resources:
|
||||
requests:
|
||||
cpu: 250m
|
||||
memory: 64Mi
|
||||
volumeMounts:
|
||||
- mountPath: '/opt/recipes/mediafiles'
|
||||
- mountPath: /opt/recipes/mediafiles
|
||||
name: media
|
||||
- mountPath: '/opt/recipes/staticfiles'
|
||||
# mount as subPath due to lost+found on ext4 pvc
|
||||
subPath: files
|
||||
- mountPath: /opt/recipes/staticfiles
|
||||
name: static
|
||||
# mount as subPath due to lost+found on ext4 pvc
|
||||
subPath: files
|
||||
env:
|
||||
- name: DEBUG
|
||||
value: "0"
|
||||
- name: ALLOWED_HOSTS
|
||||
value: '*'
|
||||
- name: SECRET_KEY
|
||||
value: # CHANGEME
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: recipes
|
||||
key: secret-key
|
||||
- name: DB_ENGINE
|
||||
value: django.db.backends.postgresql_psycopg2
|
||||
- name: POSTGRES_HOST
|
||||
value: localhost
|
||||
value: recipes-postgresql
|
||||
- name: POSTGRES_PORT
|
||||
value: "5432"
|
||||
- name: POSTGRES_USER
|
||||
value: recipes
|
||||
value: postgres
|
||||
- name: POSTGRES_DB
|
||||
value: recipes
|
||||
- name: POSTGRES_PASSWORD
|
||||
value: # CHANGEME
|
||||
- name: recipes-db
|
||||
image: 'postgres:latest'
|
||||
imagePullPolicy: IfNotPresent
|
||||
ports:
|
||||
- containerPort: 5432
|
||||
volumeMounts:
|
||||
- mountPath: '/var/lib/postgresql/data'
|
||||
name: database
|
||||
env:
|
||||
- name: POSTGRES_USER
|
||||
value: recipes
|
||||
- name: POSTGRES_DB
|
||||
value: recipes
|
||||
- name: POSTGRES_PASSWORD
|
||||
value: # CHANGEME
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: recipes
|
||||
key: postgresql-postgres-password
|
||||
securityContext:
|
||||
runAsUser: 65534
|
||||
volumes:
|
||||
- name: database
|
||||
persistentVolumeClaim:
|
||||
claimName: recipes-db
|
||||
- name: media
|
||||
persistentVolumeClaim:
|
||||
claimName: recipes-media
|
||||
|
||||
@@ -2,14 +2,21 @@ apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: recipes
|
||||
namespace: default
|
||||
labels:
|
||||
app: recipes
|
||||
tier: frontend
|
||||
spec:
|
||||
selector:
|
||||
app: recipes
|
||||
tier: frontend
|
||||
environment: production
|
||||
ports:
|
||||
- port: 80
|
||||
targetPort: http
|
||||
name: http
|
||||
protocol: TCP
|
||||
- port: 8080
|
||||
targetPort: gunicorn
|
||||
name: gunicorn
|
||||
protocol: TCP
|
||||
|
||||
38
docs/install/k8s/70-ingress.yaml
Normal file
38
docs/install/k8s/70-ingress.yaml
Normal file
@@ -0,0 +1,38 @@
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
annotations:
|
||||
#cert-manager.io/cluster-issuer: letsencrypt-prod
|
||||
#kubernetes.io/ingress.class: nginx
|
||||
name: recipes
|
||||
namespace: default
|
||||
spec:
|
||||
rules:
|
||||
- host: recipes.local
|
||||
http:
|
||||
paths:
|
||||
- backend:
|
||||
service:
|
||||
name: recipes
|
||||
port:
|
||||
number: 8080
|
||||
path: /
|
||||
pathType: Prefix
|
||||
- backend:
|
||||
service:
|
||||
name: recipes
|
||||
port:
|
||||
number: 80
|
||||
path: /media
|
||||
pathType: Prefix
|
||||
- backend:
|
||||
service:
|
||||
name: recipes
|
||||
port:
|
||||
number: 80
|
||||
path: /static
|
||||
pathType: Prefix
|
||||
#tls:
|
||||
#- hosts:
|
||||
# - recipes.local
|
||||
# secretName: recipes-local-tls
|
||||
@@ -1,31 +1,98 @@
|
||||
!!! info "Community Contributed"
|
||||
This guide was contributed by the community and is neither officially supported, nor updated or tested.
|
||||
**!!! info "Community Contributed" This guide was contributed by the community and is neither officially supported, nor updated or tested.**
|
||||
|
||||
This is a basic kubernetes setup.
|
||||
Please note that this does not necessarily follow Kubernetes best practices and should only used as a
|
||||
basis to build your own setup from!
|
||||
# K8s Setup
|
||||
|
||||
All files con be found here in the Github Repo:
|
||||
[docs/install/k8s](https://github.com/vabene1111/recipes/tree/develop/docs/install/k8s)
|
||||
This is a setup which should be sufficent for production use. Be sure to replace the default secrets!
|
||||
|
||||
## Important notes
|
||||
# Files
|
||||
|
||||
State (database, static files and media files) is handled via `PersistentVolumes`.
|
||||
## 10-configmap.yaml
|
||||
|
||||
Note that you will most likely have to change the `PersistentVolumes` in `30-pv.yaml`. The current setup is only usable for a single-node cluster because it uses local storage on the kubernetes worker nodes under `/data/recipes/`. It should just serve as an example.
|
||||
The nginx config map. This is loaded as nginx.conf in the nginx sidecar to configure nginx to deliver static content.
|
||||
|
||||
Currently, the deployment in `50-deployment.yaml` just pulls the `latest` tag of all containers. In a production setup, you should set this to a fixed version!
|
||||
## 15-secrets.yaml
|
||||
|
||||
See env variables tagged with `CHANGEME` in `50-deployment.yaml` and make sure to change those! A better setup would use kubernetes secrets but this is not implemented yet.
|
||||
The secrets **replace them!!** This file is only here for a quick start. Be aware that changing secrets after installation will be messy and is not documented here. **You should set new secrets before the installation.** As you are reading this document **before** the installation ;-)
|
||||
|
||||
## Updates
|
||||
Create your own postgresql passwords and the secret key for the django app
|
||||
|
||||
These manifests are not tested against new versions.
|
||||
see also [Managing Secrets using kubectl](https://kubernetes.io/docs/tasks/configmap-secret/managing-secret-using-kubectl/)
|
||||
|
||||
## Apply the manifets
|
||||
**Replace** `db-password`, `postgres-user-password` and `secret-key` **with something - well - secret :-)**
|
||||
|
||||
To apply the manifest with `kubectl`, use the following command:
|
||||
~~~
|
||||
echo -n 'db-password' > ./db-password.txt
|
||||
echo -n 'postgres-user-password' > ./postgres-password.txt
|
||||
echo -n 'secret-key' | sha256sum | awk '{ printf $1 }' > ./secret-key.txt
|
||||
~~~
|
||||
|
||||
```
|
||||
Delete the default secrets file `15-secrets.yaml` and generate the K8s secret from your files.
|
||||
|
||||
~~~
|
||||
kubectl create secret generic recipes \
|
||||
--from-file=postgresql-password=./db-password.txt \
|
||||
--from-file=postgresql-postgres-password=./postgres-password.txt \
|
||||
--from-file=secret-key=./secret-key.txt
|
||||
~~~
|
||||
|
||||
## 20-service-account.yml
|
||||
|
||||
Creating service account `recipes` for deployment and stateful set.
|
||||
|
||||
## 30-pvc.yaml
|
||||
|
||||
The creation of the persistent volume claims for media and static content. May you want to increase the size. This expects to have a storage class installed.
|
||||
|
||||
## 40-sts-postgresql.yaml
|
||||
|
||||
The PostgreSQL stateful set, based on a bitnami image. It runs a init container as root to do the preparations. The postgres container itsef runs as a lower privileged user. The recipes app uses the database super user (postgres) as the recipies app is doing some db migrations on startup, which needs super user privileges.
|
||||
|
||||
## 45-service-db.yaml
|
||||
|
||||
Creating the database service.
|
||||
|
||||
## 50-deployment.yaml
|
||||
|
||||
The deployment first fires up a init container to do the database migrations and file modifications. This init container runs as root. The init conainer runs part of the [boot.sh](https://github.com/TandoorRecipes/recipes/blob/develop/boot.sh) script from the `vabene1111/recipes` image.
|
||||
|
||||
The deployment then runs two containers, the recipes-nginx and the recipes container which runs the gunicorn app. The nginx container gets it's nginx.conf via config map to deliver static content `/static` and `/media`. The guincorn container gets it's secret key and the database password from the secret `recipes`. `gunicorn` runs as user `nobody`.
|
||||
|
||||
## 60-service.yaml
|
||||
|
||||
Creating the app service.
|
||||
|
||||
## 70-ingress.yaml
|
||||
|
||||
Setting up the ingress for the recipes service. Requests for static content `/static` and `/media` are send to the nginx container, everything else to gunicorn. TLS setup via cert-manager is prepared. You have to **change the host** from `recipes.local` to your specific domain.
|
||||
|
||||
# Conclusion
|
||||
|
||||
All in all:
|
||||
|
||||
- The database is set up as a stateful set.
|
||||
- The database container runs as a low privileged user.
|
||||
- Database and application use secrets.
|
||||
- The application also runs as a low privileged user.
|
||||
- nginx runs as root but forks children with a low privileged user.
|
||||
- There's an ingress rule to access the application from outside.
|
||||
|
||||
I tried the setup with [kind](https://kind.sigs.k8s.io/) and it runs well on my local cluster.
|
||||
|
||||
There is a warning, when you check your system as super user:
|
||||
|
||||
**Media Serving Warning**
|
||||
Serving media files directly using gunicorn/python is not recommend! Please follow the steps described here to update your installation.
|
||||
|
||||
I don't know how this check works, but this warning is simply wrong! ;-) Media and static files are routed by ingress to the nginx container - I promise :-)
|
||||
|
||||
# Updates
|
||||
|
||||
These manifests are tested against Release 1.0.1. Newer versions may not work without changes.
|
||||
|
||||
# Apply the manifets
|
||||
|
||||
To apply the manifest with kubectl, use the following command:
|
||||
|
||||
~~~
|
||||
kubectl apply -f ./docs/k8s/
|
||||
```
|
||||
~~~
|
||||
|
||||
21
docs/install/kubesail.md
Normal file
21
docs/install/kubesail.md
Normal file
@@ -0,0 +1,21 @@
|
||||
!!! info "Community Contributed"
|
||||
This guide was contributed by the community and is neither officially supported, nor updated or tested.
|
||||
|
||||
[KubeSail](https://kubesail.com/) lets you install Tandoor by providing a simple web interface for installing and managing apps. You can connect any server running Kubernetes, or get a pre-configured [PiBox](https://pibox.io).
|
||||
|
||||
<!-- A portion of every PiBox sale goes toward supporting Tandoor development. -->
|
||||
|
||||
The KubeSail template is closely based on the [Kubernetes installation]([docs/install/k8s](https://github.com/vabene1111/recipes/tree/develop/docs/install/k8s)) configs
|
||||
|
||||
## Quick Start
|
||||
|
||||
Load the [Tandoor Recipes](https://kubesail.com/template/PastuDan/Tandoor%20Recipes) template, and click **Launch Template**.
|
||||
|
||||
If you have not yet attached your server to KubeSail, see the [Getting a Cluster](https://docs.kubesail.com/guides/bare-metal/) section on the KubeSail docs.
|
||||
|
||||
## Important notes
|
||||
|
||||
In the "Template Variables" section you will see two input fields. These should show `RANDOM(16)`, indicating they will be randomly generated and specific to your install when you launch the template. If you prefer to set these yourself, you can type them in before launching the template.
|
||||
|
||||

|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
These intructions are inspired from a standard django/gunicorn/postgresql instructions ([for example](https://www.digitalocean.com/community/tutorials/how-to-set-up-django-with-postgres-nginx-and-gunicorn-on-ubuntu-16-04))
|
||||
|
||||
!!! warning
|
||||
Be sure to use pyton3.9 and pip related to python 3.9. Depending on your distribution calling `python` or `pip` will use python2 instead of pyton 3.9.
|
||||
Be sure to use python 3.9 and pip related to python 3.9. Depending on your distribution calling `python` or `pip` will use python2 instead of python 3.9.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
|
||||
@@ -21,6 +21,9 @@ markdown_extensions:
|
||||
- pymdownx.highlight
|
||||
- pymdownx.superfences
|
||||
|
||||
plugins:
|
||||
- include-markdown
|
||||
|
||||
nav:
|
||||
- Home: 'index.md'
|
||||
- Installation:
|
||||
@@ -28,6 +31,7 @@ nav:
|
||||
- Unraid: install/unraid.md
|
||||
- Synology: install/synology.md
|
||||
- Kubernetes: install/kubernetes.md
|
||||
- KubeSail or PiBox: install/kubesail.md
|
||||
- Manual: install/manual.md
|
||||
- Other setups: install/other.md
|
||||
- Features:
|
||||
@@ -41,4 +45,4 @@ nav:
|
||||
- Permission System: system/permissions.md
|
||||
- Backup: system/backup.md
|
||||
- Contributing: contribute.md
|
||||
- FAQ: faq.md
|
||||
- FAQ: faq.md
|
||||
|
||||
@@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2021-11-04 14:56+0100\n"
|
||||
"POT-Creation-Date: 2021-11-08 16:27+0100\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
|
||||
@@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2021-11-04 14:56+0100\n"
|
||||
"POT-Creation-Date: 2021-11-08 16:27+0100\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
|
||||
@@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2021-11-04 14:56+0100\n"
|
||||
"POT-Creation-Date: 2021-11-08 16:27+0100\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
|
||||
@@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2021-11-04 14:56+0100\n"
|
||||
"POT-Creation-Date: 2021-11-08 16:27+0100\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
|
||||
@@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2021-11-04 14:56+0100\n"
|
||||
"POT-Creation-Date: 2021-11-08 16:27+0100\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
|
||||
@@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2021-11-04 14:56+0100\n"
|
||||
"POT-Creation-Date: 2021-11-08 16:27+0100\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
|
||||
@@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2021-11-04 14:56+0100\n"
|
||||
"POT-Creation-Date: 2021-11-08 16:27+0100\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
|
||||
@@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2021-11-04 14:56+0100\n"
|
||||
"POT-Creation-Date: 2021-11-08 16:27+0100\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
|
||||
@@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2021-11-04 14:56+0100\n"
|
||||
"POT-Creation-Date: 2021-11-08 16:27+0100\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
|
||||
@@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2021-11-04 14:56+0100\n"
|
||||
"POT-Creation-Date: 2021-11-08 16:27+0100\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
|
||||
@@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2021-11-04 14:56+0100\n"
|
||||
"POT-Creation-Date: 2021-11-08 16:27+0100\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
|
||||
@@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2021-11-04 14:56+0100\n"
|
||||
"POT-Creation-Date: 2021-11-08 16:27+0100\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
|
||||
@@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2021-11-04 14:56+0100\n"
|
||||
"POT-Creation-Date: 2021-11-08 16:27+0100\n"
|
||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||
|
||||
@@ -151,6 +151,7 @@ MIDDLEWARE = [
|
||||
]
|
||||
|
||||
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)))
|
||||
|
||||
if bool(int(os.getenv('SQL_DEBUG', False))):
|
||||
MIDDLEWARE += ('recipes.middleware.SqlPrintingMiddleware',)
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<div class="row">
|
||||
<div class="col col-md-12">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-12 col-lg-10 col-xl-8 mt-3 mb-3">
|
||||
<div class="col-12 col-lg-10 mt-3 mb-3">
|
||||
<b-input-group>
|
||||
<b-input class="form-control form-control-lg form-control-borderless form-control-search"
|
||||
v-model="search"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div>
|
||||
<b-tabs content-class="mt-3">
|
||||
<b-tabs content-class="mt-3" v-model="current_tab">
|
||||
<b-tab :title="$t('Planner')" active>
|
||||
<div class="row">
|
||||
<div class="col-12 calender-parent">
|
||||
@@ -35,7 +35,7 @@
|
||||
</b-tab>
|
||||
<b-tab :title="$t('Settings')">
|
||||
<div class="row mt-3">
|
||||
<div class="col-3 calender-options">
|
||||
<div class="col-12 col-md-3 calender-options">
|
||||
<h5>{{ $t('Planner_Settings') }}</h5>
|
||||
<b-form>
|
||||
<b-form-group id="UomInput"
|
||||
@@ -76,7 +76,7 @@
|
||||
</b-form-group>
|
||||
</b-form>
|
||||
</div>
|
||||
<div class="col-9 col-lg-6">
|
||||
<div class="col-12 col-md-9 col-lg-6">
|
||||
<h5>{{ $t('Meal_Types') }}</h5>
|
||||
<div>
|
||||
<draggable :list="meal_types" group="meal_types"
|
||||
@@ -186,46 +186,52 @@
|
||||
</b-sidebar>
|
||||
</div>
|
||||
</template>
|
||||
<div class="row fixed-bottom p-2 b-1 border-top text-center" style="background:rgba(255,255,255,0.6);">
|
||||
<div class="col-md-3 col-6">
|
||||
<button class="btn btn-block btn-success shadow-none" @click="createEntryClick(new Date())"><i
|
||||
class="fas fa-calendar-plus"></i> {{ $t('Create') }}
|
||||
</button>
|
||||
<transition name="slide-fade">
|
||||
<div class="row fixed-bottom p-2 b-1 border-top text-center" style="background:rgba(255,255,255,0.6);"
|
||||
v-if="current_tab === 0">
|
||||
<div class="col-md-3 col-6">
|
||||
<button class="btn btn-block btn-success shadow-none" @click="createEntryClick(new Date())"><i
|
||||
class="fas fa-calendar-plus"></i> {{ $t('Create') }}
|
||||
</button>
|
||||
</div>
|
||||
<div class="col-md-3 col-6">
|
||||
<button class="btn btn-block btn-primary shadow-none" v-b-toggle.sidebar-shopping><i
|
||||
class="fas fa-shopping-cart"></i> {{ $t('Shopping_list') }}
|
||||
</button>
|
||||
</div>
|
||||
<div class="col-md-3 col-6">
|
||||
<a class="btn btn-block btn-primary shadow-none" :href="iCalUrl"><i class="fas fa-download"></i>
|
||||
{{ $t('Export_To_ICal') }}
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-md-3 col-6">
|
||||
<button class="btn btn-block btn-primary shadow-none disabled" v-b-tooltip.focus.top
|
||||
:title="$t('Coming_Soon')">
|
||||
{{ $t('Auto_Planner') }}
|
||||
</button>
|
||||
</div>
|
||||
<div class="col-12 d-flex justify-content-center mt-2 d-block d-md-none">
|
||||
<b-button-toolbar key-nav aria-label="Toolbar with button groups">
|
||||
<b-button-group class="mx-1">
|
||||
<b-button v-html="'<<'" @click="setShowDate($refs.header.headerProps.previousPeriod)"></b-button>
|
||||
<b-button v-html="'<'" @click="setStartingDay(-1)"></b-button>
|
||||
</b-button-group>
|
||||
<b-button-group class="mx-1">
|
||||
<b-button @click="setShowDate($refs.header.headerProps.currentPeriod)"><i class="fas fa-home"></i>
|
||||
</b-button>
|
||||
<b-form-datepicker
|
||||
button-only
|
||||
button-variant="secondary"
|
||||
></b-form-datepicker>
|
||||
</b-button-group>
|
||||
<b-button-group class="mx-1">
|
||||
<b-button v-html="'>'" @click="setStartingDay(1)"></b-button>
|
||||
<b-button v-html="'>>'" @click="setShowDate($refs.header.headerProps.nextPeriod)"></b-button>
|
||||
</b-button-group>
|
||||
</b-button-toolbar>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3 col-6">
|
||||
<button class="btn btn-block btn-primary shadow-none" v-b-toggle.sidebar-shopping><i
|
||||
class="fas fa-shopping-cart"></i> {{ $t('Shopping_list') }}
|
||||
</button>
|
||||
</div>
|
||||
<div class="col-md-3 col-6">
|
||||
<a class="btn btn-block btn-primary shadow-none" :href="iCalUrl"><i class="fas fa-download"></i>
|
||||
{{ $t('Export_To_ICal') }}
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-md-3 col-6">
|
||||
<button class="btn btn-block btn-primary shadow-none disabled" v-b-tooltip.focus.top :title="$t('Coming_Soon')">{{ $t('Auto_Planner') }}</button>
|
||||
</div>
|
||||
<div class="col-12 d-flex justify-content-center mt-2 d-block d-md-none">
|
||||
<b-button-toolbar key-nav aria-label="Toolbar with button groups">
|
||||
<b-button-group class="mx-1">
|
||||
<b-button v-html="'<<'" @click="setShowDate($refs.header.headerProps.previousPeriod)"></b-button>
|
||||
<b-button v-html="'<'" @click="setStartingDay(-1)"></b-button>
|
||||
</b-button-group>
|
||||
<b-button-group class="mx-1">
|
||||
<b-button @click="setShowDate($refs.header.headerProps.currentPeriod)"><i class="fas fa-home"></i>
|
||||
</b-button>
|
||||
<b-form-datepicker
|
||||
button-only
|
||||
button-variant="secondary"
|
||||
></b-form-datepicker>
|
||||
</b-button-group>
|
||||
<b-button-group class="mx-1">
|
||||
<b-button v-html="'>'" @click="setStartingDay(1)"></b-button>
|
||||
<b-button v-html="'>>'" @click="setShowDate($refs.header.headerProps.nextPeriod)"></b-button>
|
||||
</b-button-group>
|
||||
</b-button-toolbar>
|
||||
</div>
|
||||
</div>
|
||||
</transition>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -283,6 +289,7 @@ export default {
|
||||
displayWeekNumbers: true
|
||||
},
|
||||
dragged_item: null,
|
||||
current_tab: 0,
|
||||
meal_types: [],
|
||||
current_context_menu_item: null,
|
||||
options: {
|
||||
@@ -629,6 +636,19 @@ export default {
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.slide-fade-enter-active {
|
||||
transition: all .3s ease;
|
||||
}
|
||||
|
||||
.slide-fade-leave-active {
|
||||
transition: all .1s cubic-bezier(1.0, 0.5, 0.8, 1.0);
|
||||
}
|
||||
|
||||
.slide-fade-enter, .slide-fade-leave-to {
|
||||
transform: translateY(10px);
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.calender-parent {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
@@ -28,8 +28,8 @@
|
||||
<!-- <span><b-button variant="link" size="sm" class="text-dark shadow-none"><i class="fas fa-chevron-down"></i></b-button></span> -->
|
||||
<model-menu/>
|
||||
<span>{{ this.this_model.name }}</span>
|
||||
<span><b-button variant="link" @click="startAction({'action':'new'})"><i
|
||||
class="fas fa-plus-circle fa-2x"></i></b-button></span>
|
||||
<span v-if="this_model.name !== 'Step'"><b-button variant="link" @click="startAction({'action':'new'})"><i
|
||||
class="fas fa-plus-circle fa-2x"></i></b-button></span><!-- TODO add proper field to model config to determine if create should be available or not -->
|
||||
</h3>
|
||||
</div>
|
||||
<div class="col-md-3" style="position: relative; margin-top: 1vh">
|
||||
@@ -431,7 +431,7 @@ export default {
|
||||
// TODO: make this generic
|
||||
let params = {'pageSize': 50}
|
||||
params[this.this_recipe_param] = item.id
|
||||
|
||||
console.log('RECIPE PARAM', this.this_recipe_param, params, item.id)
|
||||
this.genericAPI(this.Models.RECIPE, this.Actions.LIST, params).then((result) => {
|
||||
parent = this.findCard(item.id, this['items_' + col])
|
||||
if (parent) {
|
||||
|
||||
@@ -48,10 +48,10 @@
|
||||
</div>
|
||||
|
||||
<div class="col-md-6 mt-1">
|
||||
<label for="id_name"> {{ $t('Preparation') }} {{ $t('Time') }}</label>
|
||||
<label for="id_name"> {{ $t('Preparation') }} {{ $t('Time') }} ({{ $t('min') }})</label>
|
||||
<input class="form-control" id="id_prep_time" v-model="recipe.working_time">
|
||||
<br/>
|
||||
<label for="id_name"> {{ $t('Waiting') }} {{ $t('Time') }}</label>
|
||||
<label for="id_name"> {{ $t('Waiting') }} {{ $t('Time') }} ({{ $t('min') }})</label>
|
||||
<input class="form-control" id="id_wait_time" v-model="recipe.waiting_time">
|
||||
<br/>
|
||||
<label for="id_name"> {{ $t('Servings') }}</label>
|
||||
|
||||
@@ -93,7 +93,7 @@
|
||||
<table class="table table-sm">
|
||||
<!-- eslint-disable vue/no-v-for-template-key-on-child -->
|
||||
<template v-for="s in recipe.steps" >
|
||||
<template v-if="s.show_as_header && s.name !== ''">
|
||||
<template v-if="s.show_as_header && s.name !== '' && s.ingredients.length > 0">
|
||||
<b v-bind:key="s.id">{{s.name}}</b>
|
||||
</template>
|
||||
<template v-for="i in s.ingredients">
|
||||
|
||||
@@ -34,6 +34,10 @@
|
||||
<i class="fas fa-file fa-fw"></i> {{ Models['USERFILE'].name }}
|
||||
</b-dropdown-item>
|
||||
|
||||
<b-dropdown-item :href="resolveDjangoUrl('list_step')">
|
||||
<i class="fas fa-puzzle-piece fa-fw"></i>{{ Models['STEP'].name }}
|
||||
</b-dropdown-item>
|
||||
|
||||
</b-dropdown>
|
||||
</span>
|
||||
</template>
|
||||
|
||||
@@ -1,212 +1,212 @@
|
||||
{
|
||||
"Import": "Importieren",
|
||||
"import_running": "Import läuft, bitte warten!",
|
||||
"Import_finished": "Import fertig",
|
||||
"View_Recipes": "Rezepte Ansehen",
|
||||
"Information": "Information",
|
||||
"all_fields_optional": "Alle Felder sind optional und können leer gelassen werden.",
|
||||
"convert_internal": "Zu internem Rezept wandeln",
|
||||
"Log_Recipe_Cooking": "Kochen protokollieren",
|
||||
"External_Recipe_Image": "Externes Rezept Bild",
|
||||
"Add_to_Book": "Zu Buch hinzufügen",
|
||||
"Add_to_Shopping": "Zu Einkaufsliste hinzufügen",
|
||||
"Add_to_Plan": "Zu Plan hinzufügen",
|
||||
"Step_start_time": "Schritt Startzeit",
|
||||
"Select_Book": "Buch wählen",
|
||||
"Recipe_Image": "Rezept Bild",
|
||||
"Log_Cooking": "Kochen protokollieren",
|
||||
"Proteins": "Proteine",
|
||||
"Fats": "Fette",
|
||||
"Carbohydrates": "Kohlenhydrate",
|
||||
"Calories": "Kalorien",
|
||||
"Energy": "Energie",
|
||||
"Nutrition": "Nährwerte",
|
||||
"Keywords": "Stichwörter",
|
||||
"Books": "Bücher",
|
||||
"show_only_internal": "Nur interne Rezepte anzeigen",
|
||||
"Ingredients": "Zutaten",
|
||||
"min": "min",
|
||||
"Servings": "Portionen",
|
||||
"Waiting": "Wartezeit",
|
||||
"Preparation": "Zubereitung",
|
||||
"Edit": "Bearbeiten",
|
||||
"Open": "Öffnen",
|
||||
"Save": "Speichern",
|
||||
"Step": "Schritt",
|
||||
"Search": "Suchen",
|
||||
"Print": "Drucken",
|
||||
"New_Recipe": "Neues Rezept",
|
||||
"Url_Import": "URL Import",
|
||||
"Reset_Search": "Suche zurücksetzen",
|
||||
"or": "oder",
|
||||
"and": "und",
|
||||
"Recently_Viewed": "Kürzlich angesehen",
|
||||
"External": "Extern",
|
||||
"Settings": "Einstellungen",
|
||||
"Meal_Plan": "Speiseplan",
|
||||
"Date": "Datum",
|
||||
"Share": "Teilen",
|
||||
"Export": "Exportieren",
|
||||
"Rating": "Bewertung",
|
||||
"Close": "Schließen",
|
||||
"Add": "Hinzufügen",
|
||||
"Copy": "Kopieren",
|
||||
"New": "Neu",
|
||||
"Categories": "Kategorien",
|
||||
"Category": "Kategorie",
|
||||
"Selected": "Ausgewählt",
|
||||
"Supermarket": "Supermarkt",
|
||||
"Files": "Dateien",
|
||||
"Size": "Größe",
|
||||
"success_fetching_resource": "Ressource erfolgreich abgerufen!",
|
||||
"Download": "Herunterladen",
|
||||
"Success": "Erfolgreich",
|
||||
"err_fetching_resource": "Ein Fehler trat während dem Abrufen einer Ressource auf!",
|
||||
"err_creating_resource": "Ein Fehler trat während dem Erstellen einer Ressource auf!",
|
||||
"err_updating_resource": "Ein Fehler trat während dem Aktualisieren einer Ressource auf!",
|
||||
"success_creating_resource": "Ressource erfolgreich erstellt!",
|
||||
"success_updating_resource": "Ressource erfolgreich aktualisiert!",
|
||||
"File": "Datei",
|
||||
"Delete": "Löschen",
|
||||
"err_deleting_resource": "Ein Fehler trat während dem Löschen einer Ressource auf!",
|
||||
"Cancel": "Abbrechen",
|
||||
"success_deleting_resource": "Ressource erfolgreich gelöscht!",
|
||||
"Load_More": "Mehr laden",
|
||||
"Ok": "Öffnen",
|
||||
"Link": "Link",
|
||||
"Key_Ctrl": "Strg",
|
||||
"move_title": "Verschieben {type}",
|
||||
"Food": "Essen",
|
||||
"Recipe_Book": "Kochbuch",
|
||||
"delete_title": "Löschen {type}",
|
||||
"create_title": "Neu {type}",
|
||||
"edit_title": "Bearbeiten {type}",
|
||||
"Name": "Name",
|
||||
"Empty": "Leer",
|
||||
"Key_Shift": "Umschalttaste",
|
||||
"Text": "Text",
|
||||
"Icon": "Icon",
|
||||
"Automation": "Automatisierung",
|
||||
"Ignore_Shopping": "Einkauf Ignorieren",
|
||||
"Parameter": "Parameter",
|
||||
"Sort_by_new": "Sortieren nach neueste",
|
||||
"Shopping_Category": "Einkauf Kategorie",
|
||||
"Edit_Food": "Essen bearbeiten",
|
||||
"Move_Food": "Essen verschieben",
|
||||
"New_Food": "Neues Essen",
|
||||
"Hide_Food": "Essen verbergen",
|
||||
"Food_Alias": "Essen Alias",
|
||||
"Unit_Alias": "Einheit Alias",
|
||||
"Keyword_Alias": "Schlagwort Alias",
|
||||
"Delete_Food": "Essen löschen",
|
||||
"No_ID": "Nr. nicht gefunden, Objekt kann nicht gelöscht werden",
|
||||
"create_rule": "und erstelle Automatisierung",
|
||||
"Table_of_Contents": "Inhaltsverzeichnis",
|
||||
"merge_title": "Zusammenführen {type}",
|
||||
"del_confirmation_tree": "Sicher das {source} und alle untergeordneten Objekte gelöscht werden soll?",
|
||||
"warning_feature_beta": "Diese Funktion ist aktuell in einer BETA (Test) Phase. Fehler sind zu erwarten und Änderungen in der Zukunft können die Funktionsweise möglicherweise Verändern oder Daten die mit dieser Funktion zusammen hängen entfernen.",
|
||||
"Edit_Keyword": "Schlagwort bearbeiten",
|
||||
"Move_Keyword": "Schlagwort verschieben",
|
||||
"Merge_Keyword": "Schlagworte zusammenführen",
|
||||
"Hide_Keywords": "Schlagworte verstecken",
|
||||
"Meal_Plan_Days": "Zukünftige Pläne",
|
||||
"Description": "Beschreibung",
|
||||
"Create_New_Shopping Category": "Erstelle neue Einkaufs Kategorie",
|
||||
"Automate": "Automatisieren",
|
||||
"Type": "Typ",
|
||||
"and_up": "& Hoch",
|
||||
"Unrated": "Unbewertet",
|
||||
"Shopping_list": "Einkaufsliste",
|
||||
"step_time_minutes": "Schritt Zeit in Minuten",
|
||||
"Save_and_View": "Speichern & Ansehen",
|
||||
"Edit_Recipe": "Rezept bearbeiten",
|
||||
"Hide_Recipes": "Rezepte verstecken",
|
||||
"Move_Up": "Hoch",
|
||||
"confirm_delete": "Soll dieses {object} wirklich gelöscht werden?",
|
||||
"Show_as_header": "Als Überschrift",
|
||||
"Hide_as_header": "Keine Überschrift",
|
||||
"Copy_template_reference": "Template Referenz kopieren",
|
||||
"Step_Type": "Schritt Typ",
|
||||
"Make_Ingredient": "In Zutat wandeln",
|
||||
"Make_Header": "In Überschrift wandeln",
|
||||
"Enable_Amount": "Menge aktivieren",
|
||||
"Disable_Amount": "Menge deaktivieren",
|
||||
"Add_Step": "Schritt hinzufügen",
|
||||
"Note": "Notiz",
|
||||
"Failure": "Fehler",
|
||||
"Move_Down": "Runter",
|
||||
"Step_Name": "Schritt Name",
|
||||
"Create": "Erstellen",
|
||||
"Advanced Search Settings": "Erweiterte Sucheinstellungen",
|
||||
"View": "Ansicht",
|
||||
"Recipes": "Rezepte",
|
||||
"Move": "Verschieben",
|
||||
"Merge": "Zusammenführen",
|
||||
"Parent": "Eltern",
|
||||
"move_confirmation": "Verschiebe <i>{child}</i> zu Elternelement <i>{parent}</i>",
|
||||
"merge_confirmation": "<i>{source}</i> durch <i>{target}</i> ersetzen",
|
||||
"move_selection": "Wähle Elternelement {type} um {source} zu verschieben.",
|
||||
"Root": "Wurzel",
|
||||
"Recipe": "Rezept",
|
||||
"tree_root": "Ursprung des Baums",
|
||||
"Unit": "Einheit",
|
||||
"No_Results": "Keine Ergebnisse",
|
||||
"New_Unit": "Neue Einheit",
|
||||
"Create_New_Food": "Neues Essen",
|
||||
"Create_New_Keyword": "Neues Schlagwort",
|
||||
"Create_New_Unit": "Neue Einheit",
|
||||
"Instructions": "Anleitung",
|
||||
"Time": "Zeit",
|
||||
"New_Keyword": "Neues Schlagwort",
|
||||
"Delete_Keyword": "Schlagwort löschen",
|
||||
"show_split_screen": "Geteilte Ansicht",
|
||||
"Recipes_per_page": "Rezepte pro Seite",
|
||||
"Manage_Books": "Bücher verwalten",
|
||||
"delete_confirmation": "Soll {source} wirklich gelöscht werden?",
|
||||
"merge_selection": "Alle Vorkommnisse von {source} mit ausgewählten {type} ersetzen.",
|
||||
"Plan_Period_To_Show": "Wochen, Monate oder Jahre anzeigen",
|
||||
"Title": "Titel",
|
||||
"Week": "Woche",
|
||||
"Month": "Monat",
|
||||
"Year": "Jahr",
|
||||
"Drag_Here_To_Delete": "Ziehen zum löschen",
|
||||
"Select_File": "Datei auswählen",
|
||||
"Image": "Bild",
|
||||
"Planner": "Planer",
|
||||
"Planner_Settings": "Einstellungen Essensplan",
|
||||
"Period": "Zeitraum",
|
||||
"Clone": "Kopieren",
|
||||
"file_upload_disabled": "Das Hochladen von Dateien ist für deinen Space nicht aktiviert.",
|
||||
"Meal_Type_Required": "Mahlzeitentyp ist erforderlich",
|
||||
"Remove_nutrition_recipe": "Nährwerte aus Rezept löschen",
|
||||
"Add_nutrition_recipe": "Nährwerte zu Rezept hinzufügen",
|
||||
"Title_or_Recipe_Required": "Titel oder Rezept benötigt",
|
||||
"Next_Day": "Tag vor",
|
||||
"Previous_Day": "Tag zurück",
|
||||
"Edit_Meal_Plan_Entry": "Eintrag bearbeiten",
|
||||
"Create_New_Meal_Type": "Neue Mahlzeit",
|
||||
"Create_Meal_Plan_Entry": "Neuer Eintrag",
|
||||
"Make_header": "Erstelle Überschrift",
|
||||
"Color": "Farbe",
|
||||
"New_Meal_Type": "Neue Mahlzeit",
|
||||
"Periods": "Zeiträume",
|
||||
"Plan_Show_How_Many_Periods": "Wie viele Zeiträume angezeigt werden",
|
||||
"Starting_Day": "Wochenbeginn am",
|
||||
"Meal_Type": "Mahlzeit",
|
||||
"Meal_Types": "Mahlzeiten",
|
||||
"Export_As_ICal": "Aktuellen Zeitraum im iCal Format exportieren",
|
||||
"Week_Numbers": "Kalenderwochen",
|
||||
"Show_Week_Numbers": "Kalenderwochen anzeigen ?",
|
||||
"Added_To_Shopping_List": "Zur Einkaufsliste hinzugefügt",
|
||||
"Export_To_ICal": "Export als .ics",
|
||||
"Cannot_Add_Notes_To_Shopping": "Notizen können nicht auf die Einkaufsliste gesetzt werden",
|
||||
"Shopping_List_Empty": "Deine Einkaufsliste ist aktuell leer, Einträge können via dem Kontextmenü hinzugefügt werden (Rechtsklick auf einen Eintrag oder Klick auf das Menü-Icon)",
|
||||
"Next_Period": "Zeitraum vor",
|
||||
"Previous_Period": "Zeitraum zurück",
|
||||
"Current_Period": "Aktueller Zeitraum",
|
||||
"New_Cookbook": "Neues Kochbuch",
|
||||
"Coming_Soon": "Bald verfügbar",
|
||||
"Auto_Planner": "Automatisch Planen",
|
||||
"Hide_Keyword": "Keywords schließen",
|
||||
"Clear": "Leeren"
|
||||
"Import": "Importieren",
|
||||
"import_running": "Import läuft, bitte warten!",
|
||||
"Import_finished": "Import fertig",
|
||||
"View_Recipes": "Rezepte Ansehen",
|
||||
"Information": "Information",
|
||||
"all_fields_optional": "Alle Felder sind optional und können leer gelassen werden.",
|
||||
"convert_internal": "Zu internem Rezept wandeln",
|
||||
"Log_Recipe_Cooking": "Kochen protokollieren",
|
||||
"External_Recipe_Image": "Externes Rezept Bild",
|
||||
"Add_to_Book": "Zu Buch hinzufügen",
|
||||
"Add_to_Shopping": "Zu Einkaufsliste hinzufügen",
|
||||
"Add_to_Plan": "Zu Plan hinzufügen",
|
||||
"Step_start_time": "Schritt Startzeit",
|
||||
"Select_Book": "Buch wählen",
|
||||
"Recipe_Image": "Rezept Bild",
|
||||
"Log_Cooking": "Kochen protokollieren",
|
||||
"Proteins": "Proteine",
|
||||
"Fats": "Fette",
|
||||
"Carbohydrates": "Kohlenhydrate",
|
||||
"Calories": "Kalorien",
|
||||
"Energy": "Energie",
|
||||
"Nutrition": "Nährwerte",
|
||||
"Keywords": "Stichwörter",
|
||||
"Books": "Bücher",
|
||||
"show_only_internal": "Nur interne Rezepte anzeigen",
|
||||
"Ingredients": "Zutaten",
|
||||
"min": "min",
|
||||
"Servings": "Portionen",
|
||||
"Waiting": "Wartezeit",
|
||||
"Preparation": "Zubereitung",
|
||||
"Edit": "Bearbeiten",
|
||||
"Open": "Öffnen",
|
||||
"Save": "Speichern",
|
||||
"Step": "Schritt",
|
||||
"Search": "Suchen",
|
||||
"Print": "Drucken",
|
||||
"New_Recipe": "Neues Rezept",
|
||||
"Url_Import": "URL Import",
|
||||
"Reset_Search": "Suche zurücksetzen",
|
||||
"or": "oder",
|
||||
"and": "und",
|
||||
"Recently_Viewed": "Kürzlich angesehen",
|
||||
"External": "Extern",
|
||||
"Settings": "Einstellungen",
|
||||
"Meal_Plan": "Speiseplan",
|
||||
"Date": "Datum",
|
||||
"Share": "Teilen",
|
||||
"Export": "Exportieren",
|
||||
"Rating": "Bewertung",
|
||||
"Close": "Schließen",
|
||||
"Add": "Hinzufügen",
|
||||
"Copy": "Kopieren",
|
||||
"New": "Neu",
|
||||
"Categories": "Kategorien",
|
||||
"Category": "Kategorie",
|
||||
"Selected": "Ausgewählt",
|
||||
"Supermarket": "Supermarkt",
|
||||
"Files": "Dateien",
|
||||
"Size": "Größe",
|
||||
"success_fetching_resource": "Ressource erfolgreich abgerufen!",
|
||||
"Download": "Herunterladen",
|
||||
"Success": "Erfolgreich",
|
||||
"err_fetching_resource": "Ein Fehler trat während dem Abrufen einer Ressource auf!",
|
||||
"err_creating_resource": "Ein Fehler trat während dem Erstellen einer Ressource auf!",
|
||||
"err_updating_resource": "Ein Fehler trat während dem Aktualisieren einer Ressource auf!",
|
||||
"success_creating_resource": "Ressource erfolgreich erstellt!",
|
||||
"success_updating_resource": "Ressource erfolgreich aktualisiert!",
|
||||
"File": "Datei",
|
||||
"Delete": "Löschen",
|
||||
"err_deleting_resource": "Ein Fehler trat während dem Löschen einer Ressource auf!",
|
||||
"Cancel": "Abbrechen",
|
||||
"success_deleting_resource": "Ressource erfolgreich gelöscht!",
|
||||
"Load_More": "Mehr laden",
|
||||
"Ok": "Öffnen",
|
||||
"Link": "Link",
|
||||
"Key_Ctrl": "Strg",
|
||||
"move_title": "Verschieben {type}",
|
||||
"Food": "Essen",
|
||||
"Recipe_Book": "Kochbuch",
|
||||
"delete_title": "Löschen {type}",
|
||||
"create_title": "Neu {type}",
|
||||
"edit_title": "Bearbeiten {type}",
|
||||
"Name": "Name",
|
||||
"Empty": "Leer",
|
||||
"Key_Shift": "Umschalttaste",
|
||||
"Text": "Text",
|
||||
"Icon": "Icon",
|
||||
"Automation": "Automatisierung",
|
||||
"Ignore_Shopping": "Einkauf Ignorieren",
|
||||
"Parameter": "Parameter",
|
||||
"Sort_by_new": "Sortieren nach neueste",
|
||||
"Shopping_Category": "Einkauf Kategorie",
|
||||
"Edit_Food": "Essen bearbeiten",
|
||||
"Move_Food": "Essen verschieben",
|
||||
"New_Food": "Neues Essen",
|
||||
"Hide_Food": "Essen verbergen",
|
||||
"Food_Alias": "Essen Alias",
|
||||
"Unit_Alias": "Einheit Alias",
|
||||
"Keyword_Alias": "Schlagwort Alias",
|
||||
"Delete_Food": "Essen löschen",
|
||||
"No_ID": "Nr. nicht gefunden, Objekt kann nicht gelöscht werden",
|
||||
"create_rule": "und erstelle Automatisierung",
|
||||
"Table_of_Contents": "Inhaltsverzeichnis",
|
||||
"merge_title": "Zusammenführen {type}",
|
||||
"del_confirmation_tree": "Sicher das {source} und alle untergeordneten Objekte gelöscht werden soll?",
|
||||
"warning_feature_beta": "Diese Funktion ist aktuell in einer BETA (Test) Phase. Fehler sind zu erwarten und Änderungen in der Zukunft können die Funktionsweise möglicherweise Verändern oder Daten die mit dieser Funktion zusammen hängen entfernen.",
|
||||
"Edit_Keyword": "Schlagwort bearbeiten",
|
||||
"Move_Keyword": "Schlagwort verschieben",
|
||||
"Merge_Keyword": "Schlagworte zusammenführen",
|
||||
"Hide_Keywords": "Schlagworte verstecken",
|
||||
"Meal_Plan_Days": "Zukünftige Pläne",
|
||||
"Description": "Beschreibung",
|
||||
"Create_New_Shopping Category": "Erstelle neue Einkaufs Kategorie",
|
||||
"Automate": "Automatisieren",
|
||||
"Type": "Typ",
|
||||
"and_up": "& Hoch",
|
||||
"Unrated": "Unbewertet",
|
||||
"Shopping_list": "Einkaufsliste",
|
||||
"step_time_minutes": "Schritt Zeit in Minuten",
|
||||
"Save_and_View": "Speichern & Ansehen",
|
||||
"Edit_Recipe": "Rezept bearbeiten",
|
||||
"Hide_Recipes": "Rezepte verstecken",
|
||||
"Move_Up": "Hoch",
|
||||
"confirm_delete": "Soll dieses {object} wirklich gelöscht werden?",
|
||||
"Show_as_header": "Als Überschrift",
|
||||
"Hide_as_header": "Keine Überschrift",
|
||||
"Copy_template_reference": "Template Referenz kopieren",
|
||||
"Step_Type": "Schritt Typ",
|
||||
"Make_Ingredient": "In Zutat wandeln",
|
||||
"Make_Header": "In Überschrift wandeln",
|
||||
"Enable_Amount": "Menge aktivieren",
|
||||
"Disable_Amount": "Menge deaktivieren",
|
||||
"Add_Step": "Schritt hinzufügen",
|
||||
"Note": "Notiz",
|
||||
"Failure": "Fehler",
|
||||
"Move_Down": "Runter",
|
||||
"Step_Name": "Schritt Name",
|
||||
"Create": "Erstellen",
|
||||
"Advanced Search Settings": "Erweiterte Sucheinstellungen",
|
||||
"View": "Ansicht",
|
||||
"Recipes": "Rezepte",
|
||||
"Move": "Verschieben",
|
||||
"Merge": "Zusammenführen",
|
||||
"Parent": "Eltern",
|
||||
"move_confirmation": "Verschiebe <i>{child}</i> zu Elternelement <i>{parent}</i>",
|
||||
"merge_confirmation": "<i>{source}</i> durch <i>{target}</i> ersetzen",
|
||||
"move_selection": "Wähle Elternelement {type} um {source} zu verschieben.",
|
||||
"Root": "Wurzel",
|
||||
"Recipe": "Rezept",
|
||||
"tree_root": "Ursprung des Baums",
|
||||
"Unit": "Einheit",
|
||||
"No_Results": "Keine Ergebnisse",
|
||||
"New_Unit": "Neue Einheit",
|
||||
"Create_New_Food": "Neues Essen",
|
||||
"Create_New_Keyword": "Neues Schlagwort",
|
||||
"Create_New_Unit": "Neue Einheit",
|
||||
"Instructions": "Anleitung",
|
||||
"Time": "Zeit",
|
||||
"New_Keyword": "Neues Schlagwort",
|
||||
"Delete_Keyword": "Schlagwort löschen",
|
||||
"show_split_screen": "Geteilte Ansicht",
|
||||
"Recipes_per_page": "Rezepte pro Seite",
|
||||
"Manage_Books": "Bücher verwalten",
|
||||
"delete_confirmation": "Soll {source} wirklich gelöscht werden?",
|
||||
"merge_selection": "Alle Vorkommnisse von {source} mit ausgewählten {type} ersetzen.",
|
||||
"Plan_Period_To_Show": "Wochen, Monate oder Jahre anzeigen",
|
||||
"Title": "Titel",
|
||||
"Week": "Woche",
|
||||
"Month": "Monat",
|
||||
"Year": "Jahr",
|
||||
"Drag_Here_To_Delete": "Ziehen zum Löschen",
|
||||
"Select_File": "Datei auswählen",
|
||||
"Image": "Bild",
|
||||
"Planner": "Planer",
|
||||
"Planner_Settings": "Einstellungen Essensplan",
|
||||
"Period": "Zeitraum",
|
||||
"Clone": "Kopieren",
|
||||
"file_upload_disabled": "Das Hochladen von Dateien ist für deinen Space nicht aktiviert.",
|
||||
"Meal_Type_Required": "Mahlzeitentyp ist erforderlich",
|
||||
"Remove_nutrition_recipe": "Nährwerte aus Rezept löschen",
|
||||
"Add_nutrition_recipe": "Nährwerte zu Rezept hinzufügen",
|
||||
"Title_or_Recipe_Required": "Titel oder Rezept benötigt",
|
||||
"Next_Day": "Tag vor",
|
||||
"Previous_Day": "Tag zurück",
|
||||
"Edit_Meal_Plan_Entry": "Eintrag bearbeiten",
|
||||
"Create_New_Meal_Type": "Neue Mahlzeit",
|
||||
"Create_Meal_Plan_Entry": "Neuer Eintrag",
|
||||
"Make_header": "Erstelle Überschrift",
|
||||
"Color": "Farbe",
|
||||
"New_Meal_Type": "Neue Mahlzeit",
|
||||
"Periods": "Zeiträume",
|
||||
"Plan_Show_How_Many_Periods": "Wie viele Zeiträume angezeigt werden",
|
||||
"Starting_Day": "Wochenbeginn am",
|
||||
"Meal_Type": "Mahlzeit",
|
||||
"Meal_Types": "Mahlzeiten",
|
||||
"Export_As_ICal": "Aktuellen Zeitraum im iCal Format exportieren",
|
||||
"Week_Numbers": "Kalenderwochen",
|
||||
"Show_Week_Numbers": "Kalenderwochen anzeigen ?",
|
||||
"Added_To_Shopping_List": "Zur Einkaufsliste hinzugefügt",
|
||||
"Export_To_ICal": "Export als .ics",
|
||||
"Cannot_Add_Notes_To_Shopping": "Notizen können nicht auf die Einkaufsliste gesetzt werden",
|
||||
"Shopping_List_Empty": "Deine Einkaufsliste ist aktuell leer, Einträge können via dem Kontextmenü hinzugefügt werden (Rechtsklick auf einen Eintrag oder Klick auf das Menü-Icon)",
|
||||
"Next_Period": "Zeitraum vor",
|
||||
"Previous_Period": "Zeitraum zurück",
|
||||
"Current_Period": "Aktueller Zeitraum",
|
||||
"New_Cookbook": "Neues Kochbuch",
|
||||
"Coming_Soon": "Bald verfügbar",
|
||||
"Auto_Planner": "Smart Planen",
|
||||
"Hide_Keyword": "Keywords schließen",
|
||||
"Clear": "Leeren"
|
||||
}
|
||||
|
||||
210
vue/src/locales/fi.json
Normal file
210
vue/src/locales/fi.json
Normal file
@@ -0,0 +1,210 @@
|
||||
{
|
||||
"warning_feature_beta": "Tämä ominaisuus on BETA (testaus) vaiheessa. Bugeja ja hajottavia muutoksia saattaa ilmaantua tulevaisuudessa tätä ominaisuutta (mahdollisesti menettää ominaisuuksiin liittyvää tietoa)käytettäessä.",
|
||||
"err_fetching_resource": "Resurssin noutamisessa tapahtui virhe!",
|
||||
"err_creating_resource": "Resurssin luomisessa tapahtui virhe!",
|
||||
"err_updating_resource": "Resurssin päivittämisessä tapahtui virhe!",
|
||||
"err_deleting_resource": "Resurssin poistamisessa tapahtui virhe!",
|
||||
"success_fetching_resource": "Resurssin hakeminen onnistui!",
|
||||
"success_creating_resource": "Resurssin luominen onnistui!",
|
||||
"success_updating_resource": "Resurssin päivitys onnistui!",
|
||||
"success_deleting_resource": "Resurssin poistaminen onnistui!",
|
||||
"file_upload_disabled": "Tiedoston lähetys ei ole käytössä tilassasi.",
|
||||
"step_time_minutes": "Askelaika minuutteina",
|
||||
"confirm_delete": "Haluatko varmasti poistaa tämän {object}?",
|
||||
"import_running": "Tuonti käynnissä, odota!",
|
||||
"all_fields_optional": "Kaikki kentät ovat valinnaisia ja voidaan jättää tyhjiksi.",
|
||||
"convert_internal": "Muunna sisäiseksi reseptiksi",
|
||||
"show_only_internal": "Näytä vain sisäiset reseptit",
|
||||
"show_split_screen": "Jaettu näkymä",
|
||||
"Log_Recipe_Cooking": "Kirjaa Reseptin Kokkaus",
|
||||
"External_Recipe_Image": "Ulkoinen reseptin kuva",
|
||||
"Add_to_Shopping": "Lisää ostoksiin",
|
||||
"Add_to_Plan": "Lisää suunnitelmaan",
|
||||
"Step_start_time": "Vaiheen aloitusaika",
|
||||
"Sort_by_new": "Lajittele uusien mukaan",
|
||||
"Table_of_Contents": "Sisällysluettelo",
|
||||
"Recipes_per_page": "Reseptejä sivulla",
|
||||
"Show_as_header": "Näytä otsikkona",
|
||||
"Hide_as_header": "Piilota otsikko",
|
||||
"Add_nutrition_recipe": "Lisää ravintoaine reseptiin",
|
||||
"Remove_nutrition_recipe": "Poista ravintoaine reseptistä",
|
||||
"Copy_template_reference": "Kopioi malliviittaus",
|
||||
"Save_and_View": "Tallenna & Katso",
|
||||
"Manage_Books": "Hallinnoi kirjoja",
|
||||
"Meal_Plan": "Ateriasuunnitelma",
|
||||
"Select_Book": "Valitse Kirja",
|
||||
"Select_File": "Valitse Tiedosto",
|
||||
"Recipe_Image": "Reseptin Kuva",
|
||||
"Import_finished": "Tuonti valmistui",
|
||||
"View_Recipes": "Näytä Reseptit",
|
||||
"Log_Cooking": "Kirjaa kokkaus",
|
||||
"New_Recipe": "Uusi Resepti",
|
||||
"Url_Import": "URL Tuonti",
|
||||
"Reset_Search": "Nollaa haku",
|
||||
"Recently_Viewed": "Äskettäin katsotut",
|
||||
"Load_More": "Lataa Lisää",
|
||||
"New_Keyword": "Uusi avainsana",
|
||||
"Delete_Keyword": "Poista avainsana",
|
||||
"Edit_Keyword": "Muokkaa avainsanaa",
|
||||
"Edit_Recipe": "Muokkaa Reseptiä",
|
||||
"Move_Keyword": "Siirrä Avainsana",
|
||||
"Merge_Keyword": "Yhdistä Avainsana",
|
||||
"Hide_Keywords": "Piilota Avainsana",
|
||||
"Hide_Recipes": "Piilota Reseptit",
|
||||
"Move_Up": "Siirry ylös",
|
||||
"Move_Down": "Siirry alas",
|
||||
"Step_Name": "Vaiheen Nimi",
|
||||
"Step_Type": "Vaiheen Tyyppi",
|
||||
"Make_header": "Valmista_Otsikko",
|
||||
"Make_Ingredient": "Valmista_Ainesosa",
|
||||
"Enable_Amount": "Ota Määrä käyttöön",
|
||||
"Disable_Amount": "Poista Määrä käytöstä",
|
||||
"Add_Step": "Lisää Vaihe",
|
||||
"Keywords": "Avainsanat",
|
||||
"Books": "Kirjat",
|
||||
"Proteins": "Proteiinit",
|
||||
"Fats": "Rasvat",
|
||||
"Carbohydrates": "Hiilihydraatit",
|
||||
"Calories": "Kalorit",
|
||||
"Energy": "Energia",
|
||||
"Nutrition": "Ravitsemus",
|
||||
"Date": "Päivämäärä",
|
||||
"Share": "Jaa",
|
||||
"Automation": "Automaatio",
|
||||
"Parameter": "Parametri",
|
||||
"Export": "Vie",
|
||||
"Copy": "Kopioi",
|
||||
"Rating": "Luokitus",
|
||||
"Close": "Sulje",
|
||||
"Cancel": "Peruuta",
|
||||
"Link": "Linkki",
|
||||
"Add": "Lisää",
|
||||
"New": "Uusi",
|
||||
"Note": "Lisätiedot",
|
||||
"Success": "Onnistui",
|
||||
"Failure": "Epäonnistui",
|
||||
"Ingredients": "Ainesosat",
|
||||
"Supermarket": "Supermarket",
|
||||
"Categories": "Luokat",
|
||||
"Category": "Luokka",
|
||||
"Selected": "Valittu",
|
||||
"min": "minimi",
|
||||
"Servings": "Annokset",
|
||||
"Waiting": "Odottaa",
|
||||
"Preparation": "Valmistautuminen",
|
||||
"External": "Ulkoinen",
|
||||
"Size": "Koko",
|
||||
"Files": "Tiedostot",
|
||||
"File": "Tiedosto",
|
||||
"Edit": "Muokkaa",
|
||||
"Image": "Kuva",
|
||||
"Delete": "Poista",
|
||||
"Open": "Avaa",
|
||||
"Ok": "Avaa",
|
||||
"Save": "Tallenna",
|
||||
"Step": "Vaihe",
|
||||
"Search": "Haku",
|
||||
"Import": "Tuo",
|
||||
"Print": "Tulosta",
|
||||
"Settings": "Asetukset",
|
||||
"or": "tai",
|
||||
"and": "ja",
|
||||
"Information": "Informaatio",
|
||||
"Download": "Lataa",
|
||||
"Create": "Luo",
|
||||
"Advanced Search Settings": "Tarkennetun Haun Asetukset",
|
||||
"View": "Katso",
|
||||
"Recipes": "Reseptit",
|
||||
"Move": "Siirry",
|
||||
"Merge": "Yhdistä",
|
||||
"Parent": "Yläluokka",
|
||||
"delete_confirmation": "Haluatko varmasti poistaa {source}?",
|
||||
"move_confirmation": "Siirrä <i>{child}</i> yläluokkaan <i>{parent}</i>",
|
||||
"merge_confirmation": "Korvaa <i>{source}</i> esiintymiset <i>{target}:lla</i>",
|
||||
"create_rule": "ja luo automaatio",
|
||||
"move_selection": "Valitse yläluokka {type} johon {suorce} siirretään.",
|
||||
"merge_selection": "Korvaa kaikki {source} esiintymiset valitulla {type}:llä.",
|
||||
"Root": "Root",
|
||||
"Ignore_Shopping": "Ohita Ostokset",
|
||||
"Shopping_Category": "Ostosluokka",
|
||||
"Edit_Food": "Muokkaa Ruokaa",
|
||||
"Move_Food": "Siirrä Ruoka",
|
||||
"New_Food": "Uusi Ruoka",
|
||||
"Hide_Food": "Piilota Ruoka",
|
||||
"Food_Alias": "Ruoan Nimimerkki",
|
||||
"Unit_Alias": "Yksikkö alias",
|
||||
"Keyword_Alias": "Avainsana alias",
|
||||
"Delete_Food": "Poista Ruoka",
|
||||
"No_ID": "Poistaminen epäonnistui, ID:tä ei löytynyt.",
|
||||
"Meal_Plan_Days": "Tulevat Ruokasuunnitelmat",
|
||||
"merge_title": "Yhdistä {type}",
|
||||
"move_title": "Siirrä {type}",
|
||||
"Food": "Ruoka",
|
||||
"Recipe_Book": "Keittokirja",
|
||||
"del_confirmation_tree": "Haluatko varmasti poistaa {source} ja kaikki sen alaosat?",
|
||||
"delete_title": "Poista {type}",
|
||||
"create_title": "Uusi {type}",
|
||||
"edit_title": "Muokkaa {type}",
|
||||
"Name": "Nimi",
|
||||
"Type": "Tyyppi",
|
||||
"Description": "Kuvaus",
|
||||
"Recipe": "Resepti",
|
||||
"tree_root": "Root of Tree",
|
||||
"Icon": "Kuvake",
|
||||
"Unit": "Yksikkö",
|
||||
"No_Results": "Ei Tuloksia",
|
||||
"New_Unit": "Uusi Yksikkö",
|
||||
"Create_New_Shopping Category": "Luo Uusi Ostoskategoria",
|
||||
"Create_New_Food": "Lisää Uusi Ruoka",
|
||||
"Create_New_Keyword": "Lisää Uusi Avainsana",
|
||||
"Create_New_Unit": "Lisää Uusi Yksikkö",
|
||||
"Create_New_Meal_Type": "Lisää Uusi Ateriatyyppi",
|
||||
"and_up": "& Ylös",
|
||||
"Instructions": "Ohjeet",
|
||||
"Unrated": "Luokittelematon",
|
||||
"Automate": "Automatisoi",
|
||||
"Empty": "Tyhjä",
|
||||
"Key_Ctrl": "Ctrl",
|
||||
"Key_Shift": "Shift",
|
||||
"Time": "Aika",
|
||||
"Text": "Teksi",
|
||||
"Shopping_list": "Ostoslista",
|
||||
"Create_Meal_Plan_Entry": "Luo ateriasuunnitelma merkintä",
|
||||
"Edit_Meal_Plan_Entry": "Muokkaa ateriasuunnitelma merkintää",
|
||||
"Title": "Otsikko",
|
||||
"Week": "Viikko",
|
||||
"Month": "Kuukausi",
|
||||
"Year": "Vuosi",
|
||||
"Planner": "Suunnittelija",
|
||||
"Planner_Settings": "Suunnittelijan asetukset",
|
||||
"Period": "Jakso",
|
||||
"Plan_Period_To_Show": "Näytä viikot, kuukaudet tai vuodet",
|
||||
"Periods": "Jaksot",
|
||||
"Plan_Show_How_Many_Periods": "Kuinka monta jaksoa näyttää",
|
||||
"Starting_Day": "Viikon aloituspäivä",
|
||||
"Meal_Types": "Ateriatyypit",
|
||||
"Meal_Type": "Ateriatyyppi",
|
||||
"Clone": "Luo kopio",
|
||||
"Drag_Here_To_Delete": "Vedä tänne poistaaksesi",
|
||||
"Meal_Type_Required": "Ateriatyyppi pakollinen",
|
||||
"Title_or_Recipe_Required": "Otsikko tai resepti valinta vaadittu",
|
||||
"Color": "Väri",
|
||||
"New_Meal_Type": "Uusi Ateriatyyppi",
|
||||
"Week_Numbers": "Viikkonumerot",
|
||||
"Show_Week_Numbers": "Näytä viikkonumerot ?",
|
||||
"Export_As_ICal": "Vie nykyinen jakso iCal muotoon",
|
||||
"Export_To_ICal": "Vie .ics",
|
||||
"Cannot_Add_Notes_To_Shopping": "Lisätietoja ei voida lisätä ostoslistaan",
|
||||
"Added_To_Shopping_List": "Lisätty ostoslistaan",
|
||||
"Shopping_List_Empty": "Ostoslistasi on tällä hetkellä tyhjä, voit lisätä tuotteita ateriasuunnitelmamerkinnän valikon kautta(klikkaa korttia hiiren kaksoispainikkeella tai valikkokuvaketta)",
|
||||
"Next_Period": "Seuraava Jakso",
|
||||
"Previous_Period": "Edellinen Jakso",
|
||||
"Current_Period": "Nykyinen Jakso",
|
||||
"Next_Day": "Seuraava Päivä",
|
||||
"Previous_Day": "Edellinen Päivä",
|
||||
"Coming_Soon": "Tulossa pian",
|
||||
"Auto_Planner": "Automaattinen Suunnittelija",
|
||||
"New_Cookbook": "Uusi keittokirja",
|
||||
"Hide_Keyword": "Piilota avainsana",
|
||||
"Clear": "Pyyhi"
|
||||
}
|
||||
@@ -190,5 +190,7 @@
|
||||
"Hide_as_header": "Cacher comme entête",
|
||||
"Copy_template_reference": "Copier le modèle de référence",
|
||||
"Edit_Recipe": "Modifier une Recette",
|
||||
"Move_Up": "Monter"
|
||||
"Move_Up": "Monter",
|
||||
"Time": "Temps",
|
||||
"Coming_Soon": "Bientôt disponible"
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"err_fetching_resource": "Si è verificato un errore nel recupero della risorsa!",
|
||||
"err_creating_resource": "Si è verificato un errore durante la creazione di una risorsa!",
|
||||
"err_updating_resource": "Si è verificato un errore nell'aggiornamento della risorsa!",
|
||||
"err_deleting_resource": "Si è verificato un errore nella cancellazione della risorsa!",
|
||||
"err_updating_resource": "Si è verificato un errore durante l'aggiornamento della risorsa!",
|
||||
"err_deleting_resource": "Si è verificato un errore durante la cancellazione della risorsa!",
|
||||
"success_fetching_resource": "Risorsa recuperata con successo!",
|
||||
"success_creating_resource": "Risorsa creata con successo!",
|
||||
"success_updating_resource": "Risorsa aggiornata con successo!",
|
||||
@@ -29,7 +29,7 @@
|
||||
"New_Recipe": "Nuova Ricetta",
|
||||
"Url_Import": "Importa da URL",
|
||||
"Reset_Search": "Ripristina Ricerca",
|
||||
"Recently_Viewed": "Visualizzati di recente",
|
||||
"Recently_Viewed": "Visualizzato di recente",
|
||||
"Load_More": "Carica di più",
|
||||
"New_Keyword": "Nuova parola chiave",
|
||||
"Delete_Keyword": "Elimina parola chiave",
|
||||
@@ -44,7 +44,7 @@
|
||||
"Fats": "Grassi",
|
||||
"Carbohydrates": "Carboidrati",
|
||||
"Calories": "Calorie",
|
||||
"Energy": "",
|
||||
"Energy": "Energia",
|
||||
"Nutrition": "Nutrienti",
|
||||
"Date": "Data",
|
||||
"Share": "Condividi",
|
||||
@@ -125,7 +125,7 @@
|
||||
"Disable_Amount": "Disabilita Quantità",
|
||||
"Key_Ctrl": "Ctrl",
|
||||
"No_Results": "Nessun risultato",
|
||||
"Create_New_Shopping Category": "Crea nuova categoria di spesa",
|
||||
"Create_New_Shopping Category": "Crea nuova categoria della spesa",
|
||||
"Create_New_Keyword": "Aggiungi nuova parola chiave",
|
||||
"and_up": "& Su",
|
||||
"step_time_minutes": "Tempo dello step in minuti",
|
||||
@@ -149,7 +149,7 @@
|
||||
"Create_New_Unit": "Aggiungi nuova unità",
|
||||
"Instructions": "Istruzioni",
|
||||
"Time": "Tempo",
|
||||
"Shopping_Category": "Categoria di spesa",
|
||||
"Shopping_Category": "Categoria Spesa",
|
||||
"Meal_Plan_Days": "Piani alimentari futuri",
|
||||
"tree_root": "Radice dell'albero",
|
||||
"Automation": "Automazione",
|
||||
@@ -202,5 +202,11 @@
|
||||
"Next_Day": "Giorno successivo",
|
||||
"Previous_Day": "Giorno precedente",
|
||||
"Add_nutrition_recipe": "Aggiungi nutrienti alla ricetta",
|
||||
"Remove_nutrition_recipe": "Elimina nutrienti dalla ricetta"
|
||||
"Remove_nutrition_recipe": "Elimina nutrienti dalla ricetta",
|
||||
"Coming_Soon": "In-Arrivo",
|
||||
"Auto_Planner": "Pianificazione automatica",
|
||||
"New_Cookbook": "Nuovo libro di ricette",
|
||||
"Hide_Keyword": "Nascondi parole chiave",
|
||||
"Clear": "Pulisci",
|
||||
"Shopping_List_Empty": "La tua lista della spesa è vuota, puoi aggiungere elementi dal menù contestuale di una voce nel piano alimentare (clicca con il tasto destro sulla scheda o clicca con il tasto sinistro sull'icona del menù)"
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
"Fats": "Vetten",
|
||||
"Carbohydrates": "Koolhydraten",
|
||||
"Calories": "Calorieën",
|
||||
"Energy": "",
|
||||
"Energy": "Energie",
|
||||
"Nutrition": "Voedingswaarde",
|
||||
"Date": "Datum",
|
||||
"Share": "Deel",
|
||||
@@ -112,8 +112,8 @@
|
||||
"Step_Type": "Stap Type",
|
||||
"Make_Header": "Maak_Koptekst",
|
||||
"Make_Ingredient": "Maak_Ingrediënt",
|
||||
"Enable_Amount": "Schakel Hoeveelheid in",
|
||||
"Disable_Amount": "Schakel Hoeveelheid uit",
|
||||
"Enable_Amount": "Schakel hoeveelheid in",
|
||||
"Disable_Amount": "Schakel hoeveelheid uit",
|
||||
"Add_Step": "Voeg Stap toe",
|
||||
"Note": "Notitie",
|
||||
"delete_confirmation": "Weet je zeker dat je {source} wil verwijderen?",
|
||||
@@ -171,7 +171,7 @@
|
||||
"Title": "Titel",
|
||||
"Week": "Week",
|
||||
"Month": "Maand",
|
||||
"Make_header": "Maak_Koptekst",
|
||||
"Make_header": "Maak dit de koptekst",
|
||||
"Color": "Kleur",
|
||||
"New_Meal_Type": "Nieuw Maaltype",
|
||||
"Image": "Afbeelding",
|
||||
@@ -204,5 +204,10 @@
|
||||
"Previous_Day": "Vorige dag",
|
||||
"Cannot_Add_Notes_To_Shopping": "Notities kunnen niet aan de boodschappenlijst toegevoegd worden",
|
||||
"Remove_nutrition_recipe": "Verwijder voedingswaarde van recept",
|
||||
"Add_nutrition_recipe": "Voeg voedingswaarde toe aan recept"
|
||||
"Add_nutrition_recipe": "Voeg voedingswaarde toe aan recept",
|
||||
"Coming_Soon": "Binnenkort beschikbaar",
|
||||
"Auto_Planner": "Autoplanner",
|
||||
"New_Cookbook": "Nieuw kookboek",
|
||||
"Hide_Keyword": "Verberg etiketten",
|
||||
"Clear": "Maak leeg"
|
||||
}
|
||||
|
||||
@@ -61,7 +61,7 @@
|
||||
"Fats": "Tłuszcze",
|
||||
"Carbohydrates": "Węglowodany",
|
||||
"Calories": "Kalorie",
|
||||
"Energy": "",
|
||||
"Energy": "Energia",
|
||||
"Nutrition": "Odżywianie",
|
||||
"Date": "Data",
|
||||
"Share": "Udostępnij",
|
||||
@@ -200,5 +200,12 @@
|
||||
"Previous_Day": "Poprzedni dzień",
|
||||
"Export_To_ICal": "Eksportuj .ics",
|
||||
"Cannot_Add_Notes_To_Shopping": "Notatki nie mogą być dodawane do listy zakupów",
|
||||
"Shopping_List_Empty": "Twoja lista zakupów jest obecnie pusta, możesz dodawać pozycje za pomocą menu kontekstowego wpisu planu posiłków (kliknij prawym przyciskiem myszy na karcie lub lewym przyciskiem myszy ikonę menu)"
|
||||
"Shopping_List_Empty": "Twoja lista zakupów jest obecnie pusta, możesz dodawać pozycje za pomocą menu kontekstowego wpisu planu posiłków (kliknij prawym przyciskiem myszy na karcie lub lewym przyciskiem myszy ikonę menu)",
|
||||
"Add_nutrition_recipe": "Dodaj wartości odżywcze do przepisu",
|
||||
"Remove_nutrition_recipe": "Usuń wartości odżywcze z przepisu",
|
||||
"Coming_Soon": "Dostępne wkrótce",
|
||||
"Auto_Planner": "Plan automatyczny",
|
||||
"New_Cookbook": "Nowa książka kucharska",
|
||||
"Hide_Keyword": "Ukryj słowa kluczowe",
|
||||
"Clear": "Wyczyść"
|
||||
}
|
||||
|
||||
210
vue/src/locales/ro.json
Normal file
210
vue/src/locales/ro.json
Normal file
@@ -0,0 +1,210 @@
|
||||
{
|
||||
"warning_feature_beta": "Momentan această funcționalitate este în fază de testare (BETA). Vă rugăm să vă așteptați la erori și, eventual, modificări de întrerupere în viitor atunci când utilizați această caracteristică (cu posibila pierdere a datelor legate de funcționalitate).",
|
||||
"err_fetching_resource": "A apărut o eroare la apelarea unei resurse!",
|
||||
"err_creating_resource": "A apărut o eroare la crearea unei resurse!",
|
||||
"err_updating_resource": "A apărut o eroare la actualizarea unei resurse!",
|
||||
"err_deleting_resource": "A apărut o eroare la ștergerea unei resurse!",
|
||||
"success_fetching_resource": "Apelare cu succes a unei resurse!",
|
||||
"success_creating_resource": "Creare cu succes a unei resurse!",
|
||||
"success_updating_resource": "Actualizare cu succes a unei resurse!",
|
||||
"success_deleting_resource": "Ștergere cu succes a unei resurse!",
|
||||
"file_upload_disabled": "Încărcarea fișierelor nu este activată pentru spațiul dvs.",
|
||||
"step_time_minutes": "Timpul pasului în minute",
|
||||
"confirm_delete": "Sunteți sigur că vreți să ștergeți acest {object}?",
|
||||
"import_running": "Import în desfășurare, așteptați!",
|
||||
"all_fields_optional": "Toate câmpurile sunt opționale și pot fi lăsate necompletate.",
|
||||
"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": "",
|
||||
"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": ""
|
||||
}
|
||||
210
vue/src/locales/sl.json
Normal file
210
vue/src/locales/sl.json
Normal file
@@ -0,0 +1,210 @@
|
||||
{
|
||||
"warning_feature_beta": "Ta funkcija je trenutno v stanju BETA (testiranje). Pri uporabi te funkcije pričakujte napake in morebitne prelomne spremembe v prihodnosti (morda izgubite podatke, povezane s to funkcijo).",
|
||||
"err_fetching_resource": "",
|
||||
"err_creating_resource": "",
|
||||
"err_updating_resource": "",
|
||||
"err_deleting_resource": "",
|
||||
"success_fetching_resource": "",
|
||||
"success_creating_resource": "",
|
||||
"success_updating_resource": "",
|
||||
"success_deleting_resource": "",
|
||||
"file_upload_disabled": "",
|
||||
"step_time_minutes": "",
|
||||
"confirm_delete": "",
|
||||
"import_running": "",
|
||||
"all_fields_optional": "",
|
||||
"convert_internal": "",
|
||||
"show_only_internal": "",
|
||||
"show_split_screen": "",
|
||||
"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": "Nov Recept",
|
||||
"Url_Import": "",
|
||||
"Reset_Search": "",
|
||||
"Recently_Viewed": "",
|
||||
"Load_More": "",
|
||||
"New_Keyword": "",
|
||||
"Delete_Keyword": "",
|
||||
"Edit_Keyword": "",
|
||||
"Edit_Recipe": "Uredi Recept",
|
||||
"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": "Knjige",
|
||||
"Proteins": "",
|
||||
"Fats": "",
|
||||
"Carbohydrates": "",
|
||||
"Calories": "",
|
||||
"Energy": "",
|
||||
"Nutrition": "",
|
||||
"Date": "Datum",
|
||||
"Share": "Deli",
|
||||
"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": "Izbriši",
|
||||
"Open": "Odpri",
|
||||
"Ok": "Odpri",
|
||||
"Save": "Shrani",
|
||||
"Step": "",
|
||||
"Search": "Iskanje",
|
||||
"Import": "Uvozi",
|
||||
"Print": "Natisni",
|
||||
"Settings": "",
|
||||
"or": "",
|
||||
"and": "",
|
||||
"Information": "",
|
||||
"Download": "Prenesi",
|
||||
"Create": "",
|
||||
"Advanced Search Settings": "",
|
||||
"View": "",
|
||||
"Recipes": "Recepti",
|
||||
"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": "Hrana",
|
||||
"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": "Dodaj Novo Hrano",
|
||||
"Create_New_Keyword": "",
|
||||
"Create_New_Unit": "",
|
||||
"Create_New_Meal_Type": "",
|
||||
"and_up": "",
|
||||
"Instructions": "",
|
||||
"Unrated": "",
|
||||
"Automate": "",
|
||||
"Empty": "",
|
||||
"Key_Ctrl": "",
|
||||
"Key_Shift": "",
|
||||
"Time": "",
|
||||
"Text": "",
|
||||
"Shopping_list": "Nakupovalni Seznam",
|
||||
"Create_Meal_Plan_Entry": "",
|
||||
"Edit_Meal_Plan_Entry": "",
|
||||
"Title": "",
|
||||
"Week": "Teden",
|
||||
"Month": "Mesec",
|
||||
"Year": "Leto",
|
||||
"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": "Barva",
|
||||
"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": "Naslednji Dan",
|
||||
"Previous_Day": "Prejšnji Dan",
|
||||
"Coming_Soon": "",
|
||||
"Auto_Planner": "",
|
||||
"New_Cookbook": "",
|
||||
"Hide_Keyword": "",
|
||||
"Clear": ""
|
||||
}
|
||||
@@ -371,14 +371,25 @@ export class Models {
|
||||
'name': i18n.t('Recipe'),
|
||||
'apiName': 'Recipe',
|
||||
'list': {
|
||||
'params': ['query', 'keywords', 'foods', 'units', 'rating', 'books', 'keywordsOr', 'foodsOr', 'booksOr', 'internal', 'random', '_new', 'page', 'pageSize', 'options'],
|
||||
'params': ['query', 'keywords', 'foods', 'units', 'rating', 'books', 'steps', 'keywordsOr', 'foodsOr', 'booksOr', 'internal', 'random', '_new', 'page', 'pageSize', 'options'],
|
||||
'config': {
|
||||
'foods': {'type': 'string'},
|
||||
'keywords': {'type': 'string'},
|
||||
'books': {'type': 'string'},
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
static STEP = {
|
||||
'name': i18n.t('Step'),
|
||||
'apiName': 'Step',
|
||||
'paginated': true,
|
||||
'list': {
|
||||
'header_component': {
|
||||
'name': 'BetaWarning'
|
||||
},
|
||||
'params': ['query', 'page', 'pageSize', 'options'],
|
||||
},
|
||||
}
|
||||
|
||||
static USER_NAME = {
|
||||
|
||||
@@ -215,12 +215,6 @@ export interface Food {
|
||||
* @memberof Food
|
||||
*/
|
||||
supermarket_category?: FoodSupermarketCategory | null;
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof Food
|
||||
*/
|
||||
image?: string;
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
@@ -233,12 +227,6 @@ export interface Food {
|
||||
* @memberof Food
|
||||
*/
|
||||
numchild?: number;
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof Food
|
||||
*/
|
||||
numrecipe?: string;
|
||||
}
|
||||
/**
|
||||
*
|
||||
@@ -387,12 +375,6 @@ export interface ImportLogKeyword {
|
||||
* @memberof ImportLogKeyword
|
||||
*/
|
||||
description?: string;
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof ImportLogKeyword
|
||||
*/
|
||||
image?: string;
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
@@ -405,12 +387,6 @@ export interface ImportLogKeyword {
|
||||
* @memberof ImportLogKeyword
|
||||
*/
|
||||
numchild?: number;
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof ImportLogKeyword
|
||||
*/
|
||||
numrecipe?: string;
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
@@ -444,10 +420,10 @@ export interface Ingredient {
|
||||
food: StepFood | null;
|
||||
/**
|
||||
*
|
||||
* @type {StepUnit}
|
||||
* @type {FoodSupermarketCategory}
|
||||
* @memberof Ingredient
|
||||
*/
|
||||
unit: StepUnit | null;
|
||||
unit: FoodSupermarketCategory | null;
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
@@ -629,10 +605,10 @@ export interface InlineResponse2004 {
|
||||
previous?: string | null;
|
||||
/**
|
||||
*
|
||||
* @type {Array<RecipeOverview>}
|
||||
* @type {Array<Step>}
|
||||
* @memberof InlineResponse2004
|
||||
*/
|
||||
results?: Array<RecipeOverview>;
|
||||
results?: Array<Step>;
|
||||
}
|
||||
/**
|
||||
*
|
||||
@@ -660,10 +636,10 @@ export interface InlineResponse2005 {
|
||||
previous?: string | null;
|
||||
/**
|
||||
*
|
||||
* @type {Array<ViewLog>}
|
||||
* @type {Array<RecipeOverview>}
|
||||
* @memberof InlineResponse2005
|
||||
*/
|
||||
results?: Array<ViewLog>;
|
||||
results?: Array<RecipeOverview>;
|
||||
}
|
||||
/**
|
||||
*
|
||||
@@ -691,10 +667,10 @@ export interface InlineResponse2006 {
|
||||
previous?: string | null;
|
||||
/**
|
||||
*
|
||||
* @type {Array<CookLog>}
|
||||
* @type {Array<ViewLog>}
|
||||
* @memberof InlineResponse2006
|
||||
*/
|
||||
results?: Array<CookLog>;
|
||||
results?: Array<ViewLog>;
|
||||
}
|
||||
/**
|
||||
*
|
||||
@@ -722,10 +698,10 @@ export interface InlineResponse2007 {
|
||||
previous?: string | null;
|
||||
/**
|
||||
*
|
||||
* @type {Array<SupermarketCategoryRelation>}
|
||||
* @type {Array<CookLog>}
|
||||
* @memberof InlineResponse2007
|
||||
*/
|
||||
results?: Array<SupermarketCategoryRelation>;
|
||||
results?: Array<CookLog>;
|
||||
}
|
||||
/**
|
||||
*
|
||||
@@ -753,9 +729,40 @@ export interface InlineResponse2008 {
|
||||
previous?: string | null;
|
||||
/**
|
||||
*
|
||||
* @type {Array<ImportLog>}
|
||||
* @type {Array<SupermarketCategoryRelation>}
|
||||
* @memberof InlineResponse2008
|
||||
*/
|
||||
results?: Array<SupermarketCategoryRelation>;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @export
|
||||
* @interface InlineResponse2009
|
||||
*/
|
||||
export interface InlineResponse2009 {
|
||||
/**
|
||||
*
|
||||
* @type {number}
|
||||
* @memberof InlineResponse2009
|
||||
*/
|
||||
count?: number;
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof InlineResponse2009
|
||||
*/
|
||||
next?: string | null;
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof InlineResponse2009
|
||||
*/
|
||||
previous?: string | null;
|
||||
/**
|
||||
*
|
||||
* @type {Array<ImportLog>}
|
||||
* @memberof InlineResponse2009
|
||||
*/
|
||||
results?: Array<ImportLog>;
|
||||
}
|
||||
/**
|
||||
@@ -794,12 +801,6 @@ export interface Keyword {
|
||||
* @memberof Keyword
|
||||
*/
|
||||
description?: string;
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof Keyword
|
||||
*/
|
||||
image?: string;
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
@@ -812,12 +813,6 @@ export interface Keyword {
|
||||
* @memberof Keyword
|
||||
*/
|
||||
numchild?: number;
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof Keyword
|
||||
*/
|
||||
numrecipe?: string;
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
@@ -881,10 +876,10 @@ export interface MealPlan {
|
||||
date: string;
|
||||
/**
|
||||
*
|
||||
* @type {number}
|
||||
* @type {MealPlanMealType}
|
||||
* @memberof MealPlan
|
||||
*/
|
||||
meal_type: number;
|
||||
meal_type: MealPlanMealType;
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
@@ -910,6 +905,55 @@ export interface MealPlan {
|
||||
*/
|
||||
meal_type_name?: string;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @export
|
||||
* @interface MealPlanMealType
|
||||
*/
|
||||
export interface MealPlanMealType {
|
||||
/**
|
||||
*
|
||||
* @type {number}
|
||||
* @memberof MealPlanMealType
|
||||
*/
|
||||
id?: number;
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof MealPlanMealType
|
||||
*/
|
||||
name: string;
|
||||
/**
|
||||
*
|
||||
* @type {number}
|
||||
* @memberof MealPlanMealType
|
||||
*/
|
||||
order?: number;
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof MealPlanMealType
|
||||
*/
|
||||
icon?: string | null;
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof MealPlanMealType
|
||||
*/
|
||||
color?: string | null;
|
||||
/**
|
||||
*
|
||||
* @type {boolean}
|
||||
* @memberof MealPlanMealType
|
||||
*/
|
||||
_default?: boolean;
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof MealPlanMealType
|
||||
*/
|
||||
created_by?: string;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @export
|
||||
@@ -1037,6 +1081,24 @@ export interface MealType {
|
||||
* @memberof MealType
|
||||
*/
|
||||
order?: number;
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof MealType
|
||||
*/
|
||||
icon?: string | null;
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof MealType
|
||||
*/
|
||||
color?: string | null;
|
||||
/**
|
||||
*
|
||||
* @type {boolean}
|
||||
* @memberof MealType
|
||||
*/
|
||||
_default?: boolean;
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
@@ -1288,12 +1350,6 @@ export interface RecipeKeywords {
|
||||
* @memberof RecipeKeywords
|
||||
*/
|
||||
description?: string;
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof RecipeKeywords
|
||||
*/
|
||||
image?: string;
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
@@ -1306,12 +1362,6 @@ export interface RecipeKeywords {
|
||||
* @memberof RecipeKeywords
|
||||
*/
|
||||
numchild?: number;
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof RecipeKeywords
|
||||
*/
|
||||
numrecipe?: string;
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
@@ -1574,6 +1624,12 @@ export interface RecipeSteps {
|
||||
* @memberof RecipeSteps
|
||||
*/
|
||||
step_recipe_data?: string;
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof RecipeSteps
|
||||
*/
|
||||
numrecipe?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1680,10 +1736,10 @@ export interface ShoppingListEntries {
|
||||
food: StepFood | null;
|
||||
/**
|
||||
*
|
||||
* @type {StepUnit}
|
||||
* @type {FoodSupermarketCategory}
|
||||
* @memberof ShoppingListEntries
|
||||
*/
|
||||
unit?: StepUnit | null;
|
||||
unit?: FoodSupermarketCategory | null;
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
@@ -1729,10 +1785,10 @@ export interface ShoppingListEntry {
|
||||
food: StepFood | null;
|
||||
/**
|
||||
*
|
||||
* @type {StepUnit}
|
||||
* @type {FoodSupermarketCategory}
|
||||
* @memberof ShoppingListEntry
|
||||
*/
|
||||
unit?: StepUnit | null;
|
||||
unit?: FoodSupermarketCategory | null;
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
@@ -2004,6 +2060,12 @@ export interface Step {
|
||||
* @memberof Step
|
||||
*/
|
||||
step_recipe_data?: string;
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof Step
|
||||
*/
|
||||
numrecipe?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -2084,12 +2146,6 @@ export interface StepFood {
|
||||
* @memberof StepFood
|
||||
*/
|
||||
supermarket_category?: FoodSupermarketCategory | null;
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof StepFood
|
||||
*/
|
||||
image?: string;
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
@@ -2102,12 +2158,6 @@ export interface StepFood {
|
||||
* @memberof StepFood
|
||||
*/
|
||||
numchild?: number;
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof StepFood
|
||||
*/
|
||||
numrecipe?: string;
|
||||
}
|
||||
/**
|
||||
*
|
||||
@@ -2129,10 +2179,10 @@ export interface StepIngredients {
|
||||
food: StepFood | null;
|
||||
/**
|
||||
*
|
||||
* @type {StepUnit}
|
||||
* @type {FoodSupermarketCategory}
|
||||
* @memberof StepIngredients
|
||||
*/
|
||||
unit: StepUnit | null;
|
||||
unit: FoodSupermarketCategory | null;
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
@@ -2164,43 +2214,6 @@ export interface StepIngredients {
|
||||
*/
|
||||
no_amount?: boolean;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @export
|
||||
* @interface StepUnit
|
||||
*/
|
||||
export interface StepUnit {
|
||||
/**
|
||||
*
|
||||
* @type {number}
|
||||
* @memberof StepUnit
|
||||
*/
|
||||
id?: number;
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof StepUnit
|
||||
*/
|
||||
name: string;
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof StepUnit
|
||||
*/
|
||||
description?: string | null;
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof StepUnit
|
||||
*/
|
||||
numrecipe?: string;
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof StepUnit
|
||||
*/
|
||||
image?: string;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @export
|
||||
@@ -2458,18 +2471,6 @@ export interface Unit {
|
||||
* @memberof Unit
|
||||
*/
|
||||
description?: string | null;
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof Unit
|
||||
*/
|
||||
numrecipe?: string;
|
||||
/**
|
||||
*
|
||||
* @type {string}
|
||||
* @memberof Unit
|
||||
*/
|
||||
image?: string;
|
||||
}
|
||||
/**
|
||||
*
|
||||
@@ -4782,6 +4783,7 @@ export const ApiApiAxiosParamCreator = function (configuration?: Configuration)
|
||||
* @param {number} [units] Id of unit a recipe should have.
|
||||
* @param {number} [rating] Id of unit a recipe should have.
|
||||
* @param {string} [books] Id of book a recipe should have. For multiple repeat parameter.
|
||||
* @param {string} [steps] Id of a step a recipe should have. For multiple repeat parameter.
|
||||
* @param {string} [keywordsOr] If recipe should have all (AND) or any (OR) of the provided keywords.
|
||||
* @param {string} [foodsOr] If recipe should have all (AND) or any (OR) any of the provided foods.
|
||||
* @param {string} [booksOr] If recipe should be in all (AND) or any (OR) any of the provided books.
|
||||
@@ -4793,7 +4795,7 @@ export const ApiApiAxiosParamCreator = function (configuration?: Configuration)
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
listRecipes: async (query?: string, keywords?: string, foods?: string, units?: number, rating?: number, books?: string, keywordsOr?: string, foodsOr?: string, booksOr?: string, internal?: string, random?: string, _new?: string, page?: number, pageSize?: number, options: any = {}): Promise<RequestArgs> => {
|
||||
listRecipes: async (query?: string, keywords?: string, foods?: string, units?: number, rating?: number, books?: string, steps?: string, keywordsOr?: string, foodsOr?: string, booksOr?: string, internal?: string, random?: string, _new?: string, page?: number, pageSize?: number, options: any = {}): Promise<RequestArgs> => {
|
||||
const localVarPath = `/api/recipe/`;
|
||||
// use dummy base URL string because the URL constructor only accepts absolute URLs.
|
||||
const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL);
|
||||
@@ -4830,6 +4832,10 @@ export const ApiApiAxiosParamCreator = function (configuration?: Configuration)
|
||||
localVarQueryParameter['books'] = books;
|
||||
}
|
||||
|
||||
if (steps !== undefined) {
|
||||
localVarQueryParameter['steps'] = steps;
|
||||
}
|
||||
|
||||
if (keywordsOr !== undefined) {
|
||||
localVarQueryParameter['keywords_or'] = keywordsOr;
|
||||
}
|
||||
@@ -4962,10 +4968,13 @@ export const ApiApiAxiosParamCreator = function (configuration?: Configuration)
|
||||
},
|
||||
/**
|
||||
*
|
||||
* @param {string} [query] Query string matched (fuzzy) against object name.
|
||||
* @param {number} [page] A page number within the paginated result set.
|
||||
* @param {number} [pageSize] Number of results to return per page.
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
listSteps: async (options: any = {}): Promise<RequestArgs> => {
|
||||
listSteps: async (query?: string, page?: number, pageSize?: number, options: any = {}): Promise<RequestArgs> => {
|
||||
const localVarPath = `/api/step/`;
|
||||
// use dummy base URL string because the URL constructor only accepts absolute URLs.
|
||||
const localVarUrlObj = new URL(localVarPath, DUMMY_BASE_URL);
|
||||
@@ -4978,6 +4987,18 @@ export const ApiApiAxiosParamCreator = function (configuration?: Configuration)
|
||||
const localVarHeaderParameter = {} as any;
|
||||
const localVarQueryParameter = {} as any;
|
||||
|
||||
if (query !== undefined) {
|
||||
localVarQueryParameter['query'] = query;
|
||||
}
|
||||
|
||||
if (page !== undefined) {
|
||||
localVarQueryParameter['page'] = page;
|
||||
}
|
||||
|
||||
if (pageSize !== undefined) {
|
||||
localVarQueryParameter['page_size'] = pageSize;
|
||||
}
|
||||
|
||||
|
||||
|
||||
setSearchParams(localVarUrlObj, localVarQueryParameter, options.query);
|
||||
@@ -8892,7 +8913,7 @@ export const ApiApiFp = function(configuration?: Configuration) {
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
async listCookLogs(page?: number, pageSize?: number, options?: any): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<InlineResponse2006>> {
|
||||
async listCookLogs(page?: number, pageSize?: number, options?: any): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<InlineResponse2007>> {
|
||||
const localVarAxiosArgs = await localVarAxiosParamCreator.listCookLogs(page, pageSize, options);
|
||||
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
|
||||
},
|
||||
@@ -8917,7 +8938,7 @@ export const ApiApiFp = function(configuration?: Configuration) {
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
async listImportLogs(page?: number, pageSize?: number, options?: any): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<InlineResponse2008>> {
|
||||
async listImportLogs(page?: number, pageSize?: number, options?: any): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<InlineResponse2009>> {
|
||||
const localVarAxiosArgs = await localVarAxiosParamCreator.listImportLogs(page, pageSize, options);
|
||||
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
|
||||
},
|
||||
@@ -8988,6 +9009,7 @@ export const ApiApiFp = function(configuration?: Configuration) {
|
||||
* @param {number} [units] Id of unit a recipe should have.
|
||||
* @param {number} [rating] Id of unit a recipe should have.
|
||||
* @param {string} [books] Id of book a recipe should have. For multiple repeat parameter.
|
||||
* @param {string} [steps] Id of a step a recipe should have. For multiple repeat parameter.
|
||||
* @param {string} [keywordsOr] If recipe should have all (AND) or any (OR) of the provided keywords.
|
||||
* @param {string} [foodsOr] If recipe should have all (AND) or any (OR) any of the provided foods.
|
||||
* @param {string} [booksOr] If recipe should be in all (AND) or any (OR) any of the provided books.
|
||||
@@ -8999,8 +9021,8 @@ export const ApiApiFp = function(configuration?: Configuration) {
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
async listRecipes(query?: string, keywords?: string, foods?: string, units?: number, rating?: number, books?: string, keywordsOr?: string, foodsOr?: string, booksOr?: string, internal?: string, random?: string, _new?: string, page?: number, pageSize?: number, options?: any): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<InlineResponse2004>> {
|
||||
const localVarAxiosArgs = await localVarAxiosParamCreator.listRecipes(query, keywords, foods, units, rating, books, keywordsOr, foodsOr, booksOr, internal, random, _new, page, pageSize, options);
|
||||
async listRecipes(query?: string, keywords?: string, foods?: string, units?: number, rating?: number, books?: string, steps?: string, keywordsOr?: string, foodsOr?: string, booksOr?: string, internal?: string, random?: string, _new?: string, page?: number, pageSize?: number, options?: any): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<InlineResponse2005>> {
|
||||
const localVarAxiosArgs = await localVarAxiosParamCreator.listRecipes(query, keywords, foods, units, rating, books, steps, keywordsOr, foodsOr, booksOr, internal, random, _new, page, pageSize, options);
|
||||
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
|
||||
},
|
||||
/**
|
||||
@@ -9032,11 +9054,14 @@ export const ApiApiFp = function(configuration?: Configuration) {
|
||||
},
|
||||
/**
|
||||
*
|
||||
* @param {string} [query] Query string matched (fuzzy) against object name.
|
||||
* @param {number} [page] A page number within the paginated result set.
|
||||
* @param {number} [pageSize] Number of results to return per page.
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
async listSteps(options?: any): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<Array<Step>>> {
|
||||
const localVarAxiosArgs = await localVarAxiosParamCreator.listSteps(options);
|
||||
async listSteps(query?: string, page?: number, pageSize?: number, options?: any): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<InlineResponse2004>> {
|
||||
const localVarAxiosArgs = await localVarAxiosParamCreator.listSteps(query, page, pageSize, options);
|
||||
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
|
||||
},
|
||||
/**
|
||||
@@ -9055,7 +9080,7 @@ export const ApiApiFp = function(configuration?: Configuration) {
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
async listSupermarketCategoryRelations(page?: number, pageSize?: number, options?: any): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<InlineResponse2007>> {
|
||||
async listSupermarketCategoryRelations(page?: number, pageSize?: number, options?: any): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<InlineResponse2008>> {
|
||||
const localVarAxiosArgs = await localVarAxiosParamCreator.listSupermarketCategoryRelations(page, pageSize, options);
|
||||
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
|
||||
},
|
||||
@@ -9143,7 +9168,7 @@ export const ApiApiFp = function(configuration?: Configuration) {
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
async listViewLogs(page?: number, pageSize?: number, options?: any): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<InlineResponse2005>> {
|
||||
async listViewLogs(page?: number, pageSize?: number, options?: any): Promise<(axios?: AxiosInstance, basePath?: string) => AxiosPromise<InlineResponse2006>> {
|
||||
const localVarAxiosArgs = await localVarAxiosParamCreator.listViewLogs(page, pageSize, options);
|
||||
return createRequestFunction(localVarAxiosArgs, globalAxios, BASE_PATH, configuration);
|
||||
},
|
||||
@@ -10529,7 +10554,7 @@ export const ApiApiFactory = function (configuration?: Configuration, basePath?:
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
listCookLogs(page?: number, pageSize?: number, options?: any): AxiosPromise<InlineResponse2006> {
|
||||
listCookLogs(page?: number, pageSize?: number, options?: any): AxiosPromise<InlineResponse2007> {
|
||||
return localVarFp.listCookLogs(page, pageSize, options).then((request) => request(axios, basePath));
|
||||
},
|
||||
/**
|
||||
@@ -10552,7 +10577,7 @@ export const ApiApiFactory = function (configuration?: Configuration, basePath?:
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
listImportLogs(page?: number, pageSize?: number, options?: any): AxiosPromise<InlineResponse2008> {
|
||||
listImportLogs(page?: number, pageSize?: number, options?: any): AxiosPromise<InlineResponse2009> {
|
||||
return localVarFp.listImportLogs(page, pageSize, options).then((request) => request(axios, basePath));
|
||||
},
|
||||
/**
|
||||
@@ -10616,6 +10641,7 @@ export const ApiApiFactory = function (configuration?: Configuration, basePath?:
|
||||
* @param {number} [units] Id of unit a recipe should have.
|
||||
* @param {number} [rating] Id of unit a recipe should have.
|
||||
* @param {string} [books] Id of book a recipe should have. For multiple repeat parameter.
|
||||
* @param {string} [steps] Id of a step a recipe should have. For multiple repeat parameter.
|
||||
* @param {string} [keywordsOr] If recipe should have all (AND) or any (OR) of the provided keywords.
|
||||
* @param {string} [foodsOr] If recipe should have all (AND) or any (OR) any of the provided foods.
|
||||
* @param {string} [booksOr] If recipe should be in all (AND) or any (OR) any of the provided books.
|
||||
@@ -10627,8 +10653,8 @@ export const ApiApiFactory = function (configuration?: Configuration, basePath?:
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
listRecipes(query?: string, keywords?: string, foods?: string, units?: number, rating?: number, books?: string, keywordsOr?: string, foodsOr?: string, booksOr?: string, internal?: string, random?: string, _new?: string, page?: number, pageSize?: number, options?: any): AxiosPromise<InlineResponse2004> {
|
||||
return localVarFp.listRecipes(query, keywords, foods, units, rating, books, keywordsOr, foodsOr, booksOr, internal, random, _new, page, pageSize, options).then((request) => request(axios, basePath));
|
||||
listRecipes(query?: string, keywords?: string, foods?: string, units?: number, rating?: number, books?: string, steps?: string, keywordsOr?: string, foodsOr?: string, booksOr?: string, internal?: string, random?: string, _new?: string, page?: number, pageSize?: number, options?: any): AxiosPromise<InlineResponse2005> {
|
||||
return localVarFp.listRecipes(query, keywords, foods, units, rating, books, steps, keywordsOr, foodsOr, booksOr, internal, random, _new, page, pageSize, options).then((request) => request(axios, basePath));
|
||||
},
|
||||
/**
|
||||
*
|
||||
@@ -10656,11 +10682,14 @@ export const ApiApiFactory = function (configuration?: Configuration, basePath?:
|
||||
},
|
||||
/**
|
||||
*
|
||||
* @param {string} [query] Query string matched (fuzzy) against object name.
|
||||
* @param {number} [page] A page number within the paginated result set.
|
||||
* @param {number} [pageSize] Number of results to return per page.
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
listSteps(options?: any): AxiosPromise<Array<Step>> {
|
||||
return localVarFp.listSteps(options).then((request) => request(axios, basePath));
|
||||
listSteps(query?: string, page?: number, pageSize?: number, options?: any): AxiosPromise<InlineResponse2004> {
|
||||
return localVarFp.listSteps(query, page, pageSize, options).then((request) => request(axios, basePath));
|
||||
},
|
||||
/**
|
||||
*
|
||||
@@ -10677,7 +10706,7 @@ export const ApiApiFactory = function (configuration?: Configuration, basePath?:
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
listSupermarketCategoryRelations(page?: number, pageSize?: number, options?: any): AxiosPromise<InlineResponse2007> {
|
||||
listSupermarketCategoryRelations(page?: number, pageSize?: number, options?: any): AxiosPromise<InlineResponse2008> {
|
||||
return localVarFp.listSupermarketCategoryRelations(page, pageSize, options).then((request) => request(axios, basePath));
|
||||
},
|
||||
/**
|
||||
@@ -10756,7 +10785,7 @@ export const ApiApiFactory = function (configuration?: Configuration, basePath?:
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
*/
|
||||
listViewLogs(page?: number, pageSize?: number, options?: any): AxiosPromise<InlineResponse2005> {
|
||||
listViewLogs(page?: number, pageSize?: number, options?: any): AxiosPromise<InlineResponse2006> {
|
||||
return localVarFp.listViewLogs(page, pageSize, options).then((request) => request(axios, basePath));
|
||||
},
|
||||
/**
|
||||
@@ -12270,6 +12299,7 @@ export class ApiApi extends BaseAPI {
|
||||
* @param {number} [units] Id of unit a recipe should have.
|
||||
* @param {number} [rating] Id of unit a recipe should have.
|
||||
* @param {string} [books] Id of book a recipe should have. For multiple repeat parameter.
|
||||
* @param {string} [steps] Id of a step a recipe should have. For multiple repeat parameter.
|
||||
* @param {string} [keywordsOr] If recipe should have all (AND) or any (OR) of the provided keywords.
|
||||
* @param {string} [foodsOr] If recipe should have all (AND) or any (OR) any of the provided foods.
|
||||
* @param {string} [booksOr] If recipe should be in all (AND) or any (OR) any of the provided books.
|
||||
@@ -12282,8 +12312,8 @@ export class ApiApi extends BaseAPI {
|
||||
* @throws {RequiredError}
|
||||
* @memberof ApiApi
|
||||
*/
|
||||
public listRecipes(query?: string, keywords?: string, foods?: string, units?: number, rating?: number, books?: string, keywordsOr?: string, foodsOr?: string, booksOr?: string, internal?: string, random?: string, _new?: string, page?: number, pageSize?: number, options?: any) {
|
||||
return ApiApiFp(this.configuration).listRecipes(query, keywords, foods, units, rating, books, keywordsOr, foodsOr, booksOr, internal, random, _new, page, pageSize, options).then((request) => request(this.axios, this.basePath));
|
||||
public listRecipes(query?: string, keywords?: string, foods?: string, units?: number, rating?: number, books?: string, steps?: string, keywordsOr?: string, foodsOr?: string, booksOr?: string, internal?: string, random?: string, _new?: string, page?: number, pageSize?: number, options?: any) {
|
||||
return ApiApiFp(this.configuration).listRecipes(query, keywords, foods, units, rating, books, steps, keywordsOr, foodsOr, booksOr, internal, random, _new, page, pageSize, options).then((request) => request(this.axios, this.basePath));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -12318,12 +12348,15 @@ export class ApiApi extends BaseAPI {
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string} [query] Query string matched (fuzzy) against object name.
|
||||
* @param {number} [page] A page number within the paginated result set.
|
||||
* @param {number} [pageSize] Number of results to return per page.
|
||||
* @param {*} [options] Override http request option.
|
||||
* @throws {RequiredError}
|
||||
* @memberof ApiApi
|
||||
*/
|
||||
public listSteps(options?: any) {
|
||||
return ApiApiFp(this.configuration).listSteps(options).then((request) => request(this.axios, this.basePath));
|
||||
public listSteps(query?: string, page?: number, pageSize?: number, options?: any) {
|
||||
return ApiApiFp(this.configuration).listSteps(query, page, pageSize, options).then((request) => request(this.axios, this.basePath));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user