Merge branch 'develop' into api_performance

This commit is contained in:
vabene1111 2021-10-12 16:28:49 +02:00 committed by GitHub
commit fe99324c38
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
62 changed files with 296 additions and 98 deletions

View File

@ -123,10 +123,10 @@ REVERSE_PROXY_AUTH=0
# SESSION_COOKIE_NAME=sessionid # use this only to not interfere with non unified django applications under the same top level domain
# by default SORT_TREE_BY_NAME is enabled this will store all Keywords and Food in case sensitive order
# this setting makes saving new keywords and foods very slow, which doesn't matter in most usecases.
# however, when doing large imports of recipes that will create new objects, can increase total run time by 5-10x
# Disabling SORT_TREE_BY_NAME (setting value to 0) will store objects unsorted, but will substantially increase speed of imports.
# by default SORT_TREE_BY_NAME is disabled this will store all Keywords and Food in the order they are created
# enabling this setting makes saving new keywords and foods very slow, which doesn't matter in most usecases.
# however, when doing large imports of recipes that will create new objects, can increase total run time by 10-15x
# Keywords and Food can be manually sorted by name in Admin
# This value can also be temporarily changed in Admin, it will revert the next time the application is started
# This will be fixed/changed in the future by changing the implementation or finding a better workaround for sorting
# SORT_TREE_BY_NAME=0

View File

@ -1,6 +1,6 @@
from django.apps import AppConfig
from django.conf import settings
from django.db import OperationalError
from django.db import OperationalError, ProgrammingError
from django_scopes import scopes_disabled
@ -21,4 +21,6 @@ class CookbookConfig(AppConfig):
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
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

View File

@ -61,7 +61,7 @@ def search_recipes(request, queryset, params):
# return queryset.annotate(last_view=Max('viewlog__pk')).annotate(new=Case(When(pk__in=last_viewed_recipes, then=('last_view')), default=Value(0))).filter(new__gt=0).order_by('-new')
# queryset that only annotates most recent view (higher pk = lastest view)
queryset = queryset.annotate(last_view=Max('viewlog__pk')).annotate(recent=Coalesce(When(pk__in=last_viewed_recipes, then=('last_view')), Value(0)))
queryset = queryset.annotate(recent=Coalesce(Max('viewlog__pk'), Value(0)))
orderby += ['-recent']
# TODO create setting for default ordering - most cooked, rating,

View File

@ -5,7 +5,9 @@ import uuid
from io import BytesIO, StringIO
from zipfile import ZipFile, BadZipFile
from django.core.exceptions import ObjectDoesNotExist
from django.core.files import File
from django.db import IntegrityError
from django.http import HttpResponse
from django.utils.formats import date_format
from django.utils.translation import gettext as _
@ -35,20 +37,24 @@ class Integration:
description = f'Imported by {request.user.get_user_name()} at {date_format(datetime.datetime.now(), "DATETIME_FORMAT")}. Type: {export_type}'
icon = '📥'
count = Keyword.objects.filter(name__icontains='Import', space=request.space).count()
name = f'Import {count + 1}'
if DATABASES['default']['ENGINE'] in ['django.db.backends.postgresql_psycopg2', 'django.db.backends.postgresql']:
parent, created = Keyword.objects.get_or_create(name='Import', space=request.space)
try:
last_kw = Keyword.objects.filter(name__regex=r'^(Import [0-9]+)', space=request.space).latest('created_at')
name = f'Import {int(last_kw.name.replace("Import ", "")) + 1}'
except ObjectDoesNotExist:
name = 'Import 1'
parent, created = Keyword.objects.get_or_create(name='Import', space=request.space)
try:
self.keyword = parent.add_child(
name=name,
description=description,
icon=icon,
space=request.space
)
else:
self.keyword, created = Keyword.objects.get_or_create(
name=name,
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,
icon=icon,
space=request.space

View File

@ -15,16 +15,16 @@ msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2021-09-13 22:40+0200\n"
"PO-Revision-Date: 2021-06-24 15:49+0000\n"
"Last-Translator: Maximilian J <maxijannack@t-online.de>\n"
"Language-Team: German <http://translate.tandoor.dev/projects/tandoor/recipes-"
"backend/de/>\n"
"PO-Revision-Date: 2021-10-07 19:06+0000\n"
"Last-Translator: vabene1111 <vabene1234@googlemail.com>\n"
"Language-Team: German <http://translate.tandoor.dev/projects/tandoor/"
"recipes-backend/de/>\n"
"Language: de\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Weblate 4.7\n"
"X-Generator: Weblate 4.8\n"
#: .\cookbook\filters.py:23 .\cookbook\templates\base.html:125
#: .\cookbook\templates\forms\ingredients.html:34
@ -286,38 +286,32 @@ msgid ""
msgstr ""
#: .\cookbook\forms.py:497
#, fuzzy
#| msgid "Search"
msgid "Search Method"
msgstr "Suche"
msgstr "Suchmethode"
#: .\cookbook\forms.py:498
msgid "Fuzzy Lookups"
msgstr ""
msgstr "Unpräzise Suche"
#: .\cookbook\forms.py:499
msgid "Ignore Accent"
msgstr ""
msgstr "Akzente ignorieren"
#: .\cookbook\forms.py:500
msgid "Partial Match"
msgstr ""
msgstr "Teilweise Übereinstimmung"
#: .\cookbook\forms.py:501
msgid "Starts Wtih"
msgstr ""
msgstr "Beginnt mit"
#: .\cookbook\forms.py:502
#, fuzzy
#| msgid "Search"
msgid "Fuzzy Search"
msgstr "Suche"
msgstr "Unpräzise Suche"
#: .\cookbook\forms.py:503
#, fuzzy
#| msgid "Text"
msgid "Full Text"
msgstr "Text"
msgstr "Volltext"
#: .\cookbook\helper\AllAuthCustomAdapter.py:36
msgid ""
@ -410,7 +404,6 @@ msgstr "Quelle"
#: .\cookbook\templates\include\log_cooking.html:16
#: .\cookbook\templates\url_import.html:224
#: .\cookbook\templates\url_import.html:455
#, fuzzy
msgid "Servings"
msgstr "Portionen"
@ -475,7 +468,7 @@ msgstr ""
#: .\cookbook\models.py:196 .\cookbook\templates\search.html:7
#: .\cookbook\templates\shopping_list.html:52
msgid "Search"
msgstr "Suche"
msgstr "Suchen"
#: .\cookbook\models.py:197 .\cookbook\templates\base.html:82
#: .\cookbook\templates\meal_plan.html:5 .\cookbook\views\delete.py:152
@ -503,7 +496,7 @@ msgstr "Neu"
#: .\cookbook\models.py:389
msgid " is part of a recipe step and cannot be deleted"
msgstr ""
msgstr " ist Teil eines Rezepts und kann nicht gelöscht werden"
#: .\cookbook\models.py:429
msgid "Text"

View File

@ -1 +1 @@
.touchable[data-v-18b1d8a0]{padding-right:2em;padding-left:2em;margin-right:-2em;margin-left:-2em}.flip-enter-active[data-v-8633bda0]{-webkit-animation-name:bounceUp-data-v-8633bda0;animation-name:bounceUp-data-v-8633bda0;-webkit-animation-duration:.5s;animation-duration:.5s;-webkit-animation-fill-mode:both;animation-fill-mode:both;-webkit-animation-timing-function:linear;animation-timing-function:linear;animation-iteration-count:infinite;-webkit-animation-iteration-count:infinite}.bounceleft[data-v-8633bda0]{-webkit-animation-name:bounceLeft-data-v-8633bda0;animation-name:bounceLeft-data-v-8633bda0;-webkit-animation-duration:.5s;animation-duration:.5s;-webkit-animation-fill-mode:both;animation-fill-mode:both;-webkit-animation-timing-function:linear;animation-timing-function:linear;animation-iteration-count:1;-webkit-animation-iteration-count:1}.bounceright[data-v-8633bda0]{-webkit-animation-name:bounceRight-data-v-8633bda0;animation-name:bounceRight-data-v-8633bda0;-webkit-animation-duration:.5s;animation-duration:.5s;-webkit-animation-fill-mode:both;animation-fill-mode:both;-webkit-animation-timing-function:linear;animation-timing-function:linear;animation-iteration-count:1;-webkit-animation-iteration-count:1}@-webkit-keyframes bounceUp-data-v-8633bda0{0%,to{-webkit-transform:translateY(0)}50%{-webkit-transform:translateY(-7px)}}@keyframes bounceUp-data-v-8633bda0{0%,to{transform:translateY(0)}50%{transform:translateY(-7px)}}@-webkit-keyframes bounceLeft-data-v-8633bda0{0%,to{-webkit-transform:translateY(0)}50%{-webkit-transform:translateX(-10px)}}@keyframes bounceLeft-data-v-8633bda0{0%,to{transform:translateY(0)}50%{transform:translateX(-10px)}}@-webkit-keyframes bounceRight-data-v-8633bda0{0%,to{-webkit-transform:translateY(0)}50%{-webkit-transform:translateX(10px)}}@keyframes bounceRight-data-v-8633bda0{0%,to{transform:translateY(0)}50%{transform:translateX(10px)}}.slide-fade-enter-active{transition:all .6s ease}.slide-fade-enter,.slide-fade-leave-to{transform:translateX(10px);opacity:0}
.touchable[data-v-5fa7e004]{padding-right:2em;padding-left:2em;margin-right:-2em;margin-left:-2em}.flip-enter-active[data-v-8633bda0]{-webkit-animation-name:bounceUp-data-v-8633bda0;animation-name:bounceUp-data-v-8633bda0;-webkit-animation-duration:.5s;animation-duration:.5s;-webkit-animation-fill-mode:both;animation-fill-mode:both;-webkit-animation-timing-function:linear;animation-timing-function:linear;animation-iteration-count:infinite;-webkit-animation-iteration-count:infinite}.bounceleft[data-v-8633bda0]{-webkit-animation-name:bounceLeft-data-v-8633bda0;animation-name:bounceLeft-data-v-8633bda0;-webkit-animation-duration:.5s;animation-duration:.5s;-webkit-animation-fill-mode:both;animation-fill-mode:both;-webkit-animation-timing-function:linear;animation-timing-function:linear;animation-iteration-count:1;-webkit-animation-iteration-count:1}.bounceright[data-v-8633bda0]{-webkit-animation-name:bounceRight-data-v-8633bda0;animation-name:bounceRight-data-v-8633bda0;-webkit-animation-duration:.5s;animation-duration:.5s;-webkit-animation-fill-mode:both;animation-fill-mode:both;-webkit-animation-timing-function:linear;animation-timing-function:linear;animation-iteration-count:1;-webkit-animation-iteration-count:1}@-webkit-keyframes bounceUp-data-v-8633bda0{0%,to{-webkit-transform:translateY(0)}50%{-webkit-transform:translateY(-7px)}}@keyframes bounceUp-data-v-8633bda0{0%,to{transform:translateY(0)}50%{transform:translateY(-7px)}}@-webkit-keyframes bounceLeft-data-v-8633bda0{0%,to{-webkit-transform:translateY(0)}50%{-webkit-transform:translateX(-10px)}}@keyframes bounceLeft-data-v-8633bda0{0%,to{transform:translateY(0)}50%{transform:translateX(-10px)}}@-webkit-keyframes bounceRight-data-v-8633bda0{0%,to{-webkit-transform:translateY(0)}50%{-webkit-transform:translateX(10px)}}@keyframes bounceRight-data-v-8633bda0{0%,to{transform:translateY(0)}50%{transform:translateX(10px)}}.slide-fade-enter-active{transition:all .6s ease}.slide-fade-enter,.slide-fade-leave-to{transform:translateX(10px);opacity:0}

View File

@ -1 +1 @@
.context-menu[data-v-b808df3a]{position:fixed;z-index:999;overflow:hidden;background:#fff;border-radius:4px;box-shadow:0 1px 4px 0 #eee}.context-menu[data-v-b808df3a]:focus{outline:none}.context-menu ul[data-v-b808df3a]{padding:0;margin:0}.dropdown-menu[data-v-b808df3a]{display:block;position:relative}.touchable[data-v-18b1d8a0]{padding-right:2em;padding-left:2em;margin-right:-2em;margin-left:-2em}.meal-plan-card[data-v-08ffff62]{background-color:#fff}.theme-default .cv-day.draghover[data-v-08ffff62]{box-shadow:inset 0 0 .2em .2em grey}.cv-header{display:flex;flex:0 1 auto;flex-flow:row nowrap;align-items:center;min-height:2.5em;border-width:1px 1px 0 1px}.cv-header .actionArea,.cv-header .periodLabel{display:flex;flex:1 1 auto;flex-flow:row nowrap;min-height:1.5em;line-height:1;font-size:1.5em}.delete-area{border-style:dotted;margin-left:auto;order:2;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.delete-area.draghover{box-shadow:inset 0 0 .1em .1em #a7240e!important}.cv-header,.cv-header button{border-style:solid;border-color:#ddd}.cv-header-nav,.cv-header .periodLabel{margin:.1em .6em}.cv-header-nav button,.cv-header .periodLabel{padding:.4em .6em}.cv-header button{box-sizing:border-box;line-height:1em;font-size:1em;border-width:1px}.calender-parent{display:flex;flex-direction:column;flex-grow:1;overflow-x:hidden;overflow-y:hidden;max-height:80vh;min-height:40rem}.cv-item{white-space:inherit!important}.isHovered{box-shadow:0 .5rem 1rem rgba(0,0,0,.15)!important}.cv-day.draghover{box-shadow:inset 0 0 .2em .2em grey!important}.modal-backdrop{opacity:.5}
.context-menu[data-v-b808df3a]{position:fixed;z-index:999;overflow:hidden;background:#fff;border-radius:4px;box-shadow:0 1px 4px 0 #eee}.context-menu[data-v-b808df3a]:focus{outline:none}.context-menu ul[data-v-b808df3a]{padding:0;margin:0}.dropdown-menu[data-v-b808df3a]{display:block;position:relative}.touchable[data-v-5fa7e004]{padding-right:2em;padding-left:2em;margin-right:-2em;margin-left:-2em}.meal-plan-card[data-v-08ffff62]{background-color:#fff}.theme-default .cv-day.draghover[data-v-08ffff62]{box-shadow:inset 0 0 .2em .2em grey}.cv-header{display:flex;flex:0 1 auto;flex-flow:row nowrap;align-items:center;min-height:2.5em;border-width:1px 1px 0 1px}.cv-header .actionArea,.cv-header .periodLabel{display:flex;flex:1 1 auto;flex-flow:row nowrap;min-height:1.5em;line-height:1;font-size:1.5em}.delete-area{border-style:dotted;margin-left:auto;order:2;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.delete-area.draghover{box-shadow:inset 0 0 .1em .1em #a7240e!important}.cv-header,.cv-header button{border-style:solid;border-color:#ddd}.cv-header-nav,.cv-header .periodLabel{margin:.1em .6em}.cv-header-nav button,.cv-header .periodLabel{padding:.4em .6em}.cv-header button{box-sizing:border-box;line-height:1em;font-size:1em;border-width:1px}.calender-parent{display:flex;flex-direction:column;flex-grow:1;overflow-x:hidden;overflow-y:hidden;max-height:80vh;min-height:40rem}.cv-item{white-space:inherit!important}.isHovered{box-shadow:0 .5rem 1rem rgba(0,0,0,.15)!important}.cv-day.draghover{box-shadow:inset 0 0 .2em .2em grey!important}.modal-backdrop{opacity:.5}

View File

@ -1 +1 @@
.touchable[data-v-18b1d8a0]{padding-right:2em;padding-left:2em;margin-right:-2em;margin-left:-2em}.shake[data-v-51a59cab]{-webkit-animation:shake-data-v-51a59cab .82s cubic-bezier(.36,.07,.19,.97) both;animation:shake-data-v-51a59cab .82s cubic-bezier(.36,.07,.19,.97) both;transform:translateZ(0);-webkit-backface-visibility:hidden;backface-visibility:hidden;perspective:1000px}@-webkit-keyframes shake-data-v-51a59cab{10%,90%{transform:translate3d(-1px,0,0)}20%,80%{transform:translate3d(2px,0,0)}30%,50%,70%{transform:translate3d(-4px,0,0)}40%,60%{transform:translate3d(4px,0,0)}}@keyframes shake-data-v-51a59cab{10%,90%{transform:translate3d(-1px,0,0)}20%,80%{transform:translate3d(2px,0,0)}30%,50%,70%{transform:translate3d(-4px,0,0)}40%,60%{transform:translate3d(4px,0,0)}}
.touchable[data-v-5fa7e004]{padding-right:2em;padding-left:2em;margin-right:-2em;margin-left:-2em}.shake[data-v-51a59cab]{-webkit-animation:shake-data-v-51a59cab .82s cubic-bezier(.36,.07,.19,.97) both;animation:shake-data-v-51a59cab .82s cubic-bezier(.36,.07,.19,.97) both;transform:translateZ(0);-webkit-backface-visibility:hidden;backface-visibility:hidden;perspective:1000px}@-webkit-keyframes shake-data-v-51a59cab{10%,90%{transform:translate3d(-1px,0,0)}20%,80%{transform:translate3d(2px,0,0)}30%,50%,70%{transform:translate3d(-4px,0,0)}40%,60%{transform:translate3d(4px,0,0)}}@keyframes shake-data-v-51a59cab{10%,90%{transform:translate3d(-1px,0,0)}20%,80%{transform:translate3d(2px,0,0)}30%,50%,70%{transform:translate3d(-4px,0,0)}40%,60%{transform:translate3d(4px,0,0)}}

View File

@ -1 +1 @@
.touchable[data-v-18b1d8a0]{padding-right:2em;padding-left:2em;margin-right:-2em;margin-left:-2em}
.touchable[data-v-5fa7e004]{padding-right:2em;padding-left:2em;margin-right:-2em;margin-left:-2em}

View File

@ -1 +1 @@
.touchable[data-v-18b1d8a0]{padding-right:2em;padding-left:2em;margin-right:-2em;margin-left:-2em}
.touchable[data-v-5fa7e004]{padding-right:2em;padding-left:2em;margin-right:-2em;margin-left:-2em}

File diff suppressed because one or more lines are too long

View File

@ -1 +1 @@
(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-2d0da313"],{"6b0a":function(e,t,i){"use strict";i.r(t);var a=function(){var e=this,t=e.$createElement,i=e._self._c||t;return i("b-card",{directives:[{name:"hover",rawName:"v-hover"}],attrs:{"no-body":""}},[i("a",{attrs:{href:e.clickUrl()}},[i("b-card-img-lazy",{staticStyle:{height:"15vh","object-fit":"cover"},attrs:{src:e.recipe_image,alt:e.$t("Recipe_Image"),top:""}}),i("div",{staticClass:"card-img-overlay h-100 d-flex flex-column justify-content-right float-right text-right pt-2 pr-1"},[i("a",[null!==e.recipe?i("recipe-context-menu",{staticClass:"float-right",attrs:{recipe:e.recipe}}):e._e()],1)]),0!==e.recipe.waiting_time?i("div",{staticClass:"card-img-overlay w-50 d-flex flex-column justify-content-left float-left text-left pt-2"},[i("b-badge",{staticClass:"mt-1 font-weight-normal",attrs:{pill:"",variant:"light"}},[i("i",{staticClass:"fa fa-clock"}),e._v(" "+e._s(e.recipe.working_time)+" "+e._s(e.$t("min"))+" ")]),i("b-badge",{staticClass:"mt-1 font-weight-normal",attrs:{pill:"",variant:"secondary"}},[i("i",{staticClass:"fa fa-pause"}),e._v(" "+e._s(e.recipe.waiting_time)+" "+e._s(e.$t("min"))+" ")])],1):e._e()],1),i("b-card-body",{staticClass:"p-4"},[i("h6",[i("a",{attrs:{href:e.clickUrl()}},[null!==e.recipe?[e._v(e._s(e.recipe.name))]:[e._v(e._s(e.meal_plan.title))]],2)]),i("b-card-text",{staticStyle:{"text-overflow":"ellipsis"}},[null!==e.recipe?[i("recipe-rating",{attrs:{recipe:e.recipe}}),null!==e.recipe.description?[e.recipe.description.length>e.text_length?i("span",[e._v(" "+e._s(e.recipe.description.substr(0,e.text_length)+"…")+" ")]):e._e(),e.recipe.description.length<=e.text_length?i("span",[e._v(" "+e._s(e.recipe.description)+" ")]):e._e()]:e._e(),i("p",{staticClass:"mt-1"},[i("last-cooked",{attrs:{recipe:e.recipe}}),i("keywords",{staticStyle:{"margin-top":"4px"},attrs:{recipe:e.recipe}})],1),e.detailed?i("div",{staticClass:"row mt-3"},[i("div",{staticClass:"col-md-12"},[i("h6",{staticClass:"card-title"},[i("i",{staticClass:"fas fa-pepper-hot"}),e._v(" "+e._s(e.$t("Ingredients")))]),i("table",{staticClass:"table table-sm text-wrap"},[e._l(e.recipe.steps,(function(t){return[e._l(t.ingredients,(function(e){return[i("Ingredient",{key:e.id,attrs:{detailed:!1,ingredient:e,ingredient_factor:1}})]}))]}))],2)])]):e._e(),e.recipe.internal?e._e():i("b-badge",{attrs:{pill:"",variant:"info"}},[e._v(e._s(e.$t("External")))])]:[e._v(e._s(e.meal_plan.note))]],2)],1),void 0!==e.footer_text?i("b-card-footer",[i("i",{class:e.footer_icon}),e._v(" "+e._s(e.footer_text)+" ")]):e._e()],1)},r=[],s=i("fc0d"),n=i("81d5"),c=i("fa7d"),l=i("ca5b"),o=i("c1df"),p=i.n(o),d=i("a026"),_=i("830a"),f=i("118a");d["default"].prototype.moment=p.a;var m={name:"RecipeCard",mixins:[c["d"]],components:{LastCooked:_["a"],RecipeRating:l["a"],Keywords:n["a"],RecipeContextMenu:s["a"],Ingredient:f["a"]},props:{recipe:Object,meal_plan:Object,footer_text:String,footer_icon:String},computed:{detailed:function(){return void 0!==this.recipe.steps},text_length:function(){return this.detailed?200:120},recipe_image:function(){return null==this.recipe||null===this.recipe.image?window.IMAGE_PLACEHOLDER:this.recipe.image}},methods:{clickUrl:function(){return null!==this.recipe?Object(c["l"])("view_recipe",this.recipe.id):Object(c["l"])("view_plan_entry",this.meal_plan.id)}},directives:{hover:{inserted:function(e){e.addEventListener("mouseenter",(function(){e.classList.add("shadow")})),e.addEventListener("mouseleave",(function(){e.classList.remove("shadow")}))}}}},u=m,g=i("2877"),h=Object(g["a"])(u,a,r,!1,null,"2338d8bc",null);t["default"]=h.exports}}]);
(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["chunk-2d0da313"],{"6b0a":function(e,t,i){"use strict";i.r(t);var a=function(){var e=this,t=e.$createElement,i=e._self._c||t;return i("b-card",{directives:[{name:"hover",rawName:"v-hover"}],attrs:{"no-body":""}},[i("a",{attrs:{href:e.clickUrl()}},[i("b-card-img-lazy",{staticStyle:{height:"15vh","object-fit":"cover"},attrs:{src:e.recipe_image,alt:e.$t("Recipe_Image"),top:""}}),i("div",{staticClass:"card-img-overlay h-100 d-flex flex-column justify-content-right float-right text-right pt-2 pr-1"},[i("a",[null!==e.recipe?i("recipe-context-menu",{staticClass:"float-right",attrs:{recipe:e.recipe}}):e._e()],1)]),0!==e.recipe.waiting_time?i("div",{staticClass:"card-img-overlay w-50 d-flex flex-column justify-content-left float-left text-left pt-2"},[i("b-badge",{staticClass:"mt-1 font-weight-normal",attrs:{pill:"",variant:"light"}},[i("i",{staticClass:"fa fa-clock"}),e._v(" "+e._s(e.recipe.working_time)+" "+e._s(e.$t("min"))+" ")]),i("b-badge",{staticClass:"mt-1 font-weight-normal",attrs:{pill:"",variant:"secondary"}},[i("i",{staticClass:"fa fa-pause"}),e._v(" "+e._s(e.recipe.waiting_time)+" "+e._s(e.$t("min"))+" ")])],1):e._e()],1),i("b-card-body",{staticClass:"p-4"},[i("h6",[i("a",{attrs:{href:e.clickUrl()}},[null!==e.recipe?[e._v(e._s(e.recipe.name))]:[e._v(e._s(e.meal_plan.title))]],2)]),i("b-card-text",{staticStyle:{"text-overflow":"ellipsis"}},[null!==e.recipe?[i("recipe-rating",{attrs:{recipe:e.recipe}}),null!==e.recipe.description?[e.recipe.description.length>e.text_length?i("span",[e._v(" "+e._s(e.recipe.description.substr(0,e.text_length)+"…")+" ")]):e._e(),e.recipe.description.length<=e.text_length?i("span",[e._v(" "+e._s(e.recipe.description)+" ")]):e._e()]:e._e(),i("p",{staticClass:"mt-1"},[i("last-cooked",{attrs:{recipe:e.recipe}}),i("keywords",{staticStyle:{"margin-top":"4px"},attrs:{recipe:e.recipe}})],1),e.detailed?i("div",{staticClass:"row mt-3"},[i("div",{staticClass:"col-md-12"},[i("h6",{staticClass:"card-title"},[i("i",{staticClass:"fas fa-pepper-hot"}),e._v(" "+e._s(e.$t("Ingredients")))]),i("table",{staticClass:"table table-sm text-wrap"},[e._l(e.recipe.steps,(function(t){return[e._l(t.ingredients,(function(e){return[i("Ingredient",{key:e.id,attrs:{detailed:!1,ingredient:e,ingredient_factor:1}})]}))]}))],2)])]):e._e(),e.recipe.internal?e._e():i("b-badge",{attrs:{pill:"",variant:"info"}},[e._v(e._s(e.$t("External")))])]:[e._v(e._s(e.meal_plan.note))]],2)],1),void 0!==e.footer_text?i("b-card-footer",[i("i",{class:e.footer_icon}),e._v(" "+e._s(e.footer_text)+" ")]):e._e()],1)},r=[],s=i("fc0d"),n=i("81d5"),c=i("fa7d"),l=i("ca5b"),o=i("c1df"),p=i.n(o),d=i("a026"),_=i("830a"),f=i("118a");d["default"].prototype.moment=p.a;var m={name:"RecipeCard",mixins:[c["ResolveUrlMixin"]],components:{LastCooked:_["a"],RecipeRating:l["a"],Keywords:n["a"],RecipeContextMenu:s["a"],Ingredient:f["a"]},props:{recipe:Object,meal_plan:Object,footer_text:String,footer_icon:String},computed:{detailed:function(){return void 0!==this.recipe.steps},text_length:function(){return this.detailed?200:120},recipe_image:function(){return null==this.recipe||null===this.recipe.image?window.IMAGE_PLACEHOLDER:this.recipe.image}},methods:{clickUrl:function(){return null!==this.recipe?Object(c["resolveDjangoUrl"])("view_recipe",this.recipe.id):Object(c["resolveDjangoUrl"])("view_plan_entry",this.meal_plan.id)}},directives:{hover:{inserted:function(e){e.addEventListener("mouseenter",(function(){e.classList.add("shadow")})),e.addEventListener("mouseleave",(function(){e.classList.remove("shadow")}))}}}},u=m,v=i("2877"),g=Object(v["a"])(u,a,r,!1,null,"2338d8bc",null);t["default"]=g.exports}}]);

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -246,6 +246,8 @@
class="fab fa-markdown fa-fw"></i> {% trans 'Markdown Guide' %}</a>
<a class="dropdown-item" href="https://github.com/vabene1111/recipes"><i
class="fab fa-github fa-fw"></i> {% trans 'GitHub' %}</a>
<a class="dropdown-item" href="https://translate.tandoor.dev/projects/tandoor/"><i
class="fas fa-language fa-fw"></i> {% trans 'Translate Tandoor' %}</a>
<a class="dropdown-item" href="{% url 'docs_api' %}"><i
class="fas fa-passport fa-fw"></i> {% trans 'API Documentation' %}</a>
<a class="dropdown-item" href="{% url 'api:api-root' %}"><i

View File

@ -149,7 +149,7 @@ MIDDLEWARE = [
'cookbook.helper.scope_middleware.ScopeMiddleware',
]
SORT_TREE_BY_NAME = bool(int(os.getenv('SORT_TREE_BY_NAME', True)))
SORT_TREE_BY_NAME = bool(int(os.getenv('SORT_TREE_BY_NAME', False)))
if bool(int(os.getenv('SQL_DEBUG', False))):
MIDDLEWARE += ('recipes.middleware.SqlPrintingMiddleware',)

View File

@ -31,11 +31,11 @@
<b-tab :title="$t('Settings')">
<div class="row mt-3">
<div class="col-3 calender-options">
<h5>{{ $t('CalenderSettings') }}</h5>
<h5>{{ $t('Planner_Settings') }}</h5>
<b-form>
<b-form-group id="UomInput"
:label="$t('Period')"
:description="$t('PeriodToShow')"
:description="$t('Plan_Period_To_Show')"
label-for="UomInput">
<b-form-select
id="UomInput"
@ -44,8 +44,8 @@
></b-form-select>
</b-form-group>
<b-form-group id="PeriodInput"
:label="$t('PeriodCount')"
:description="$t('ShowHowManyPeriods')"
:label="$t('Periods')"
:description="$t('Plan_Show_How_Many_Periods')"
label-for="PeriodInput">
<b-form-select
id="PeriodInput"
@ -54,8 +54,8 @@
></b-form-select>
</b-form-group>
<b-form-group id="DaysInput"
:label="$t('StartingDay')"
:description="$t('StartingDay')"
:label="$t('Starting_Day')"
:description="$t('Starting_Day')"
label-for="DaysInput">
<b-form-select
id="DaysInput"
@ -66,7 +66,7 @@
</b-form>
</div>
<div class="col-6">
<h5>{{ $t('MealTypes') }}</h5>
<h5>{{ $t('Meal_Types') }}</h5>
<b-form>
</b-form>
@ -82,10 +82,10 @@
<a class="dropdown-item p-2" href="#"><i class="fas fa-pen"></i> {{ $t("Edit") }}</a>
</ContextMenuItem>
<ContextMenuItem @click="$refs.menu.close();moveEntryLeft(contextData)">
<a class="dropdown-item p-2" href="#"><i class="fas fa-arrow-left"></i> {{ $t("DayBack") }}</a>
<a class="dropdown-item p-2" href="#"><i class="fas fa-arrow-left"></i> {{ $t("Move") }}</a>
</ContextMenuItem>
<ContextMenuItem @click="$refs.menu.close();moveEntryRight(contextData)">
<a class="dropdown-item p-2" href="#"><i class="fas fa-arrow-right"></i> {{ $t("DayForward") }}</a>
<a class="dropdown-item p-2" href="#"><i class="fas fa-arrow-right"></i> {{ $t("Move") }}</a>
</ContextMenuItem>
<ContextMenuItem @click="$refs.menu.close();createEntry(contextData.originalItem.entry)">
<a class="dropdown-item p-2" href="#"><i class="fas fa-copy"></i> {{ $t("Clone") }}</a>
@ -98,7 +98,7 @@
<meal-plan-edit-modal :entry="entryEditing" :entryEditing_initial_recipe="entryEditing_initial_recipe"
:entry-editing_initial_meal_type="entryEditing_initial_meal_type" :modal_title="modal_title"
:edit_modal_show="edit_modal_show" @save-entry="editEntry"
@delete-entry="deleteEntry"></meal-plan-edit-modal>
@delete-entry="deleteEntry" @reload-meal-types="refreshMealTypes"></meal-plan-edit-modal>
</div>
</template>
@ -178,9 +178,9 @@ export default {
computed: {
modal_title: function () {
if (this.entryEditing.id === -1) {
return this.$t('CreateMealPlanEntry')
return this.$t('Create_Meal_Plan_Entry')
} else {
return this.$t('EditMealPlanEntry')
return this.$t('Edit_Meal_Plan_Entry')
}
},
entryEditing_initial_recipe: function () {
@ -332,6 +332,11 @@ export default {
}).then(result => {
this.plan_entries = result.data
})
this.refreshMealTypes()
},
refreshMealTypes() {
let apiClient = new ApiApiFactory()
apiClient.listMealTypes().then(result => {
this.meal_types = result.data
})

View File

@ -24,7 +24,7 @@
</div>
<div class="row pt-2">
<div class="col-md-6" style="max-height: 50vh">
<div class="col-md-6" style="max-height: 50vh; min-height: 30vh">
<input id="id_file_upload" ref="file_upload" type="file" hidden @change="uploadImage($event.target.files[0])">
@ -47,7 +47,7 @@
</button>
</div>
<div class="col-md-6">
<div class="col-md-6 mt-1">
<label for="id_name"> {{ $t('Preparation') }} {{ $t('Time') }}</label>
<input class="form-control" id="id_prep_time" v-model="recipe.working_time">
<br/>

View File

@ -13,7 +13,8 @@
<div class="row text-center">
<div class="col col-md-12">
<recipe-rating :recipe="recipe"></recipe-rating> <br/>
<recipe-rating :recipe="recipe"></recipe-rating>
<br/>
<last-cooked :recipe="recipe"></last-cooked>
</div>
</div>
@ -60,12 +61,14 @@
<i class="fas fa-pizza-slice fa-2x text-primary"></i>
</div>
<div class="my-auto" style="padding-right: 4px">
<input style="text-align: right; border-width:0px;border:none; padding:0px; padding-left: 0.5vw; padding-right: 8px; max-width: 80px"
value="1" maxlength="3" min="0"
type="number" class="form-control form-control-lg" v-model.number="servings"/>
<input
style="text-align: right; border-width:0px;border:none; padding:0px; padding-left: 0.5vw; padding-right: 8px; max-width: 80px"
value="1" maxlength="3" min="0"
type="number" class="form-control form-control-lg" v-model.number="servings"/>
</div>
<div class="my-auto ">
<span class="text-primary"><b><template v-if="recipe.servings_text === ''">{{ $t('Servings') }}</template><template v-else>{{recipe.servings_text}}</template></b></span>
<span class="text-primary"><b><template v-if="recipe.servings_text === ''">{{ $t('Servings') }}</template><template
v-else>{{ recipe.servings_text }}</template></b></span>
</div>
</div>
</div>
@ -90,12 +93,15 @@
<div class="col-md-12">
<table class="table table-sm">
<!-- eslint-disable vue/no-v-for-template-key-on-child -->
<template v-for="s in recipe.steps">
<div v-for="s in recipe.steps" v-bind:key="s.id">
<template v-if="s.show_as_header && s.name !== ''">
<b>{{s.name}}</b>
</template>
<template v-for="i in s.ingredients">
<Ingredient :ingredient="i" :ingredient_factor="ingredient_factor" :key="i.id"
@checked-state-changed="updateIngredientCheckedState"></Ingredient>
</template>
</template>
</div>
<!-- eslint-enable vue/no-v-for-template-key-on-child -->
</table>
</div>
@ -144,7 +150,7 @@
<div class="row text-center d-print-none" style="margin-top: 3vh; margin-bottom: 3vh" v-if="share_uid !== 'None'">
<div class="col col-md-12">
<a :href="resolveDjangoUrl('view_report_share_abuse', share_uid)" >{{$t('Report Abuse')}}</a>
<a :href="resolveDjangoUrl('view_report_share_abuse', share_uid)">{{ $t('Report Abuse') }}</a>
</div>
</div>

View File

@ -1,7 +1,7 @@
<template>
<b-card no-body v-hover>
<b-card-header class="p-4">
<h5>{{ $t('TableOfContents') }}</h5>
<h5>{{ $t('Table_of_Contents') }}</h5>
</b-card-header>
<b-card-body class="p-4">
<ol style="max-height: 60vh;overflow-y:auto;-webkit-overflow-scrolling: touch;" class="mb-1">

View File

@ -26,12 +26,13 @@
</td>
<td v-if="detailed">
<div v-if="ingredient.note">
<span v-b-popover.hover="ingredient.note" v-if="ingredient.note.length > 15"
<span v-b-popover.hover="ingredient.note"
class="d-print-none touchable"> <i class="far fa-comment"></i>
</span>
<span v-else>
{{ ingredient.note }}
</span>
<!-- v-if="ingredient.note.length > 15" -->
<!-- <span v-else>-->
<!-- {{ ingredient.note }}-->
<!-- </span>-->
<div class="d-none d-print-block">
<i class="far fa-comment-alt d-print-none"></i> {{ ingredient.note }}

View File

@ -45,7 +45,7 @@
<div class="actionArea d-none d-sm-flex">
<span class="delete-area text-danger p-1 mr-2" @drop.prevent="onDeleteDrop($event)"
@dragenter.prevent="onDeleteDragEnter($event)" @dragleave.prevent="onDeleteDragLeave($event)" @dragover.prevent="onDeleteDragEnter"><i
class="fas fa-trash"></i> {{ $t('DragHereToDelete') }}</span>
class="fas fa-trash"></i> {{ $t('Drag_Here_To_Delete') }}</span>
</div>
</div>
</template>

View File

@ -6,12 +6,14 @@
<div class="col-6 col-lg-9">
<b-input-group>
<b-form-input id="TitleInput" v-model="entryEditing.title"
:placeholder="entryEditing.title_placeholder"></b-form-input>
:placeholder="entryEditing.title_placeholder"
@change="missing_recipe = false"></b-form-input>
<b-input-group-append class="d-none d-lg-block">
<b-button variant="primary" @click="entryEditing.title = ''"><i class="fa fa-eraser"></i></b-button>
</b-input-group-append>
</b-input-group>
<small tabindex="-1" class="form-text text-muted">{{ $t("Title") }}</small>
<span class="text-danger" v-if="missing_recipe">{{ $t('Title_or_Recipe_Required') }}</span>
<small tabindex="-1" class="form-text text-muted" v-if="!missing_recipe">{{ $t("Title") }}</small>
</div>
<div class="col-6 col-lg-3">
<input type="date" id="DateInput" class="form-control" v-model="entryEditing.date">
@ -37,10 +39,15 @@
:label="'name'"
:model="Models.MEAL_TYPE"
style="flex-grow: 1; flex-shrink: 1; flex-basis: 0"
v-bind:placeholder="$t('MealType')" :limit="10"
v-bind:placeholder="$t('Meal_Type')" :limit="10"
:multiple="false"
:initial_selection="entryEditing_initial_meal_type"></generic-multiselect>
<small tabindex="-1" class="form-text text-muted">{{ $t("MealType") }}</small>
:initial_selection="entryEditing_initial_meal_type"
:allow_create="true"
:create_placeholder="$t('Create_New_Meal_Type')"
@new="createMealType"
></generic-multiselect>
<span class="text-danger" v-if="missing_meal_type">{{ $t('Meal_Type_Required') }}</span>
<small tabindex="-1" class="form-text text-muted" v-if="!missing_meal_type">{{ $t("Meal_Type") }}</small>
</b-form-group>
<b-form-group
label-for="NoteInput"
@ -76,6 +83,10 @@ import {BootstrapVue} from "bootstrap-vue";
import GenericMultiselect from "./GenericMultiselect";
import {ApiMixin} from "../utils/utils";
const {ApiApiFactory} = require("@/utils/openapi/api");
const {StandardToasts} = require("@/utils/utils");
Vue.use(BootstrapVue)
export default {
@ -101,7 +112,9 @@ export default {
},
data() {
return {
entryEditing: {}
entryEditing: {},
missing_recipe: false,
missing_meal_type: false
}
},
watch: {
@ -114,29 +127,47 @@ export default {
},
methods: {
editEntry() {
this.missing_meal_type = false
this.missing_recipe = false
let cancel = false
if (this.entryEditing.meal_type == null) {
alert("Need Meal type")
return
this.missing_meal_type = true
cancel = true
}
if (this.entryEditing.recipe == null && this.entryEditing.title === '') {
alert("Need title or recipe")
return
this.missing_recipe = true
cancel = true
}
if (!cancel) {
this.$bvModal.hide(`edit-modal`);
this.$emit('save-entry', this.entryEditing)
}
this.$bvModal.hide(`edit-modal`);
this.$emit('save-entry', this.entryEditing)
},
deleteEntry() {
this.$bvModal.hide(`edit-modal`);
this.$emit('delete-entry', this.entryEditing)
},
selectMealType(event) {
this.missing_meal_type = false
if (event.val != null) {
this.entryEditing.meal_type = event.val;
} else {
this.entryEditing.meal_type = null;
}
},
createMealType(event) {
if (event != "") {
let apiClient = new ApiApiFactory()
apiClient.createMealType({name: event}).then(e => {
this.$emit('reload-meal-types')
}).catch(error => {
StandardToasts.makeStandardToast(StandardToasts.FAIL_UPDATE)
})
}
},
selectRecipe(event) {
this.missing_recipe = false
if (event.val != null) {
this.entryEditing.recipe = event.val;
this.entryEditing.title_placeholder = this.entryEditing.recipe.name

View File

@ -75,7 +75,7 @@
<meal-plan-edit-modal :entry="entryEditing" :entryEditing_initial_recipe="[recipe]"
:entry-editing_initial_meal_type="[]" @save-entry="saveMealPlan"
:modal_id="`modal-meal-plan_${modal_id}`" :allow_delete="false" :modal_title="$t('CreateMealPlanEntry')"></meal-plan-edit-modal>
:modal_id="`modal-meal-plan_${modal_id}`" :allow_delete="false" :modal_title="$t('Create_Meal_Plan_Entry')"></meal-plan-edit-modal>
</div>
</template>

View File

@ -71,5 +71,96 @@
"Cancel": "Abbrechen",
"success_deleting_resource": "Ressource erfolgreich gelöscht!",
"Load_More": "Mehr laden",
"Ok": "Öffnen"
"Ok": "Öffnen",
"Link": "Verknüpfung",
"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 neu",
"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": "Schlagwort zusammenführen",
"Hide_Keywords": "Schlagwort 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": "Ersetze <i>{source}</i> mit <i>{target}</i>",
"move_selection": "Wähle Elternelement {type} um {source} zu verschieben.",
"Root": "Ursprung",
"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": "Ersetze alle vorkommen von {source} mit dem ausgewählten {type}."
}

View File

@ -21,6 +21,7 @@
"Add_to_Plan": "Add to Plan",
"Step_start_time": "Step start time",
"Sort_by_new": "Sort by new",
"Table_of_Contents": "Table of Contents",
"Recipes_per_page": "Recipes per Page",
"Show_as_header": "Show as header",
"Hide_as_header": "Hide as header",
@ -50,7 +51,7 @@
"Move_Down": "Move down",
"Step_Name": "Step Name",
"Step_Type": "Step Type",
"Make_Header": "Make_Header",
"Make_header": "Make_Header",
"Make_Ingredient": "Make_Ingredient",
"Enable_Amount": "Enable Amount",
"Disable_Amount": "Disable Amount",
@ -151,13 +152,34 @@
"Create_New_Food": "Add New Food",
"Create_New_Keyword": "Add New Keyword",
"Create_New_Unit": "Add New Unit",
"Create_New_Meal_Type": "Add New Meal Type",
"and_up": "& Up",
"Instructions": "Instructions",
"Unrated": "Unrated",
"Automate": "Automate",
"Empty": "Empty",
"Key_Ctrl": "Ctrl",
"Key_Shift": "Shift",
"Time": "Time",
"Text": "Text",
"Shopping_list": "Shopping List"
"Shopping_list": "Shopping List",
"Create_Meal_Plan_Entry": "Create meal plan entry",
"Edit_Meal_Plan_Entry": "Edit meal plan entry",
"Title": "Title",
"Week": "Week",
"Month": "Month",
"Year": "Year",
"Planner": "Planner",
"Planner_Settings": "Planner settings",
"Period": "Period",
"Plan_Period_To_Show": "Show weeks, months or years",
"Periods": "Periods",
"Plan_Show_How_Many_Periods": "How many periods to show",
"Starting_Day": "Starting day of the week",
"Meal_Types": "Meal types",
"Meal_Type": "Meal type",
"Clone": "Clone",
"Drag_Here_To_Delete": "Drag here to delete",
"Meal_Type_Required": "Meal type is required",
"Title_or_Recipe_Required": "Title or recipe selection required"
}

View File

@ -155,5 +155,30 @@
"Parameter": "Parametro",
"Type": "Tipo",
"Automate": "Automatizza",
"create_rule": "e crea automazione"
"create_rule": "e crea automazione",
"Empty": "Vuoto",
"Food_Alias": "Alias Alimento",
"Unit_Alias": "Alias Unità",
"Keyword_Alias": "Alias Parola Chiave",
"Table_of_Contents": "Indice dei contenuti",
"warning_feature_beta": "Questa funzione è attualmente in BETA (non è completa). Potrebbero verificarsi delle anomalie e modifiche che in futuro potrebbero bloccare la funzionalità stessa o rimuove i dati correlati a essa.",
"Shopping_list": "Lista della spesa",
"Title": "Titolo",
"Create_New_Meal_Type": "Aggiungi nuovo tipo di pasto",
"Week": "Settimana",
"Month": "Mese",
"Year": "Anno",
"Planner": "Planner",
"Planner_Settings": "Impostazioni planner",
"Period": "Periodo",
"Plan_Period_To_Show": "Mostra settimane, mesi o anni",
"Plan_Show_How_Many_Periods": "Periodo da mostrare",
"Starting_Day": "Giorno di inizio della settimana",
"Meal_Types": "Tipi di pasto",
"Clone": "Duplica",
"Drag_Here_To_Delete": "Sposta qui per eliminare",
"Meal_Type_Required": "Il tipo di pasto è richiesto",
"Periods": "Periodi",
"Meal_Type": "Tipo di pasto",
"Title_or_Recipe_Required": "Sono richiesti titolo o ricetta"
}