mirror of
https://github.com/TandoorRecipes/recipes.git
synced 2026-01-01 04:10:06 -05:00
Merge branch 'develop' into translations_cookbook-locale-en-lc-messages-django-po--develop_nl
This commit is contained in:
@@ -8,14 +8,25 @@ ALLOWED_HOSTS=*
|
|||||||
# random secret key, use for example base64 /dev/urandom | head -c50 to generate one
|
# random secret key, use for example base64 /dev/urandom | head -c50 to generate one
|
||||||
SECRET_KEY=
|
SECRET_KEY=
|
||||||
|
|
||||||
|
# your default timezone
|
||||||
|
TIMEZONE=Europe/Berlin
|
||||||
|
|
||||||
# add only a database password if you want to run with the default postgres, otherwise change settings accordingly
|
# add only a database password if you want to run with the default postgres, otherwise change settings accordingly
|
||||||
DB_ENGINE=django.db.backends.postgresql_psycopg2
|
DB_ENGINE=django.db.backends.postgresql
|
||||||
POSTGRES_HOST=db_recipes
|
POSTGRES_HOST=db_recipes
|
||||||
POSTGRES_PORT=5432
|
POSTGRES_PORT=5432
|
||||||
POSTGRES_USER=djangodb
|
POSTGRES_USER=djangodb
|
||||||
POSTGRES_PASSWORD=
|
POSTGRES_PASSWORD=
|
||||||
POSTGRES_DB=djangodb
|
POSTGRES_DB=djangodb
|
||||||
|
|
||||||
|
# the default value for the user preference 'fractions' (enable/disable fraction support)
|
||||||
|
# when unset: 0 (disabled)
|
||||||
|
FRACTION_PREF_DEFAULT=0
|
||||||
|
|
||||||
|
# the default value for the user preference 'comments' (enable/disable commenting system)
|
||||||
|
# when unset: 1 (true)
|
||||||
|
COMMENT_PREF_DEFAULT=1
|
||||||
|
|
||||||
# Users can set a amount of time after which the shopping list is refreshed when they are in viewing mode
|
# Users can set a amount of time after which the shopping list is refreshed when they are in viewing mode
|
||||||
# This is the minimum interval users can set. Setting this to low will allow users to refresh very frequently which
|
# This is the minimum interval users can set. Setting this to low will allow users to refresh very frequently which
|
||||||
# might cause high load on the server. (Technically they can obviously refresh as often as they want with their own scripts)
|
# might cause high load on the server. (Technically they can obviously refresh as often as they want with their own scripts)
|
||||||
@@ -33,14 +44,9 @@ SHOPPING_MIN_AUTOSYNC_INTERVAL=5
|
|||||||
# when unset: 1 (true) - this is temporary until an appropriate amount of time has passed for everyone to migrate
|
# when unset: 1 (true) - this is temporary until an appropriate amount of time has passed for everyone to migrate
|
||||||
GUNICORN_MEDIA=0
|
GUNICORN_MEDIA=0
|
||||||
|
|
||||||
|
|
||||||
# allow authentication via reverse proxy (e.g. authelia), leave of if you dont know what you are doing
|
# allow authentication via reverse proxy (e.g. authelia), leave of if you dont know what you are doing
|
||||||
# docs: https://github.com/vabene1111/recipes/tree/develop/docs/docker/nginx-proxy%20with%20proxy%20authentication
|
# docs: https://github.com/vabene1111/recipes/tree/develop/docs/docker/nginx-proxy%20with%20proxy%20authentication
|
||||||
# when unset: 0 (false)
|
# when unset: 0 (false)
|
||||||
REVERSE_PROXY_AUTH=0
|
REVERSE_PROXY_AUTH=0
|
||||||
|
|
||||||
|
|
||||||
# the default value for the user preference 'comments' (enable/disable commenting system)
|
|
||||||
# when unset: 1 (true)
|
|
||||||
COMMENT_PREF_DEFAULT=1
|
|
||||||
|
|
||||||
|
|||||||
@@ -172,3 +172,10 @@ class ShareLinkAdmin(admin.ModelAdmin):
|
|||||||
|
|
||||||
|
|
||||||
admin.site.register(ShareLink, ShareLinkAdmin)
|
admin.site.register(ShareLink, ShareLinkAdmin)
|
||||||
|
|
||||||
|
|
||||||
|
class NutritionInformationAdmin(admin.ModelAdmin):
|
||||||
|
list_display = ('id',)
|
||||||
|
|
||||||
|
|
||||||
|
admin.site.register(NutritionInformation, NutritionInformationAdmin)
|
||||||
|
|||||||
@@ -31,11 +31,12 @@ class UserPreferenceForm(forms.ModelForm):
|
|||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = UserPreference
|
model = UserPreference
|
||||||
fields = ('default_unit', 'theme', 'nav_color', 'default_page', 'show_recent', 'search_style', 'plan_share', 'ingredient_decimals', 'shopping_auto_sync', 'comments')
|
fields = ('default_unit', 'use_fractions', 'theme', 'nav_color', 'default_page', 'show_recent', 'search_style', 'plan_share', 'ingredient_decimals', 'shopping_auto_sync', 'comments')
|
||||||
|
|
||||||
help_texts = {
|
help_texts = {
|
||||||
'nav_color': _('Color of the top navigation bar. Not all colors work with all themes, just try them out!'),
|
'nav_color': _('Color of the top navigation bar. Not all colors work with all themes, just try them out!'),
|
||||||
'default_unit': _('Default Unit to be used when inserting a new ingredient into a recipe.'),
|
'default_unit': _('Default Unit to be used when inserting a new ingredient into a recipe.'),
|
||||||
|
'use_fractions': _('Enables support for fractions in ingredient amounts (e.g. convert decimals to fractions automatically)'),
|
||||||
'plan_share': _('Default user to share newly created meal plan entries with.'),
|
'plan_share': _('Default user to share newly created meal plan entries with.'),
|
||||||
'show_recent': _('Show recently viewed recipes on search page.'),
|
'show_recent': _('Show recently viewed recipes on search page.'),
|
||||||
'ingredient_decimals': _('Number of decimals to round ingredients.'),
|
'ingredient_decimals': _('Number of decimals to round ingredients.'),
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import json
|
import json
|
||||||
import random
|
import random
|
||||||
import re
|
import re
|
||||||
|
import unicodedata
|
||||||
from json import JSONDecodeError
|
from json import JSONDecodeError
|
||||||
|
|
||||||
import microdata
|
import microdata
|
||||||
@@ -76,6 +77,14 @@ def find_recipe_json(ld_json, url):
|
|||||||
if len(ingredient_split) > 2:
|
if len(ingredient_split) > 2:
|
||||||
ingredient = " ".join(ingredient_split[2:])
|
ingredient = " ".join(ingredient_split[2:])
|
||||||
unit = ingredient_split[1]
|
unit = ingredient_split[1]
|
||||||
|
|
||||||
|
try:
|
||||||
|
if 'fraction' in unicodedata.decomposition(ingredient_split[0]):
|
||||||
|
frac_split = unicodedata.decomposition(ingredient_split[0]).split()
|
||||||
|
amount = round(float((frac_split[1]).replace('003', '')) / float((frac_split[3]).replace('003', '')), 3)
|
||||||
|
else:
|
||||||
|
raise TypeError
|
||||||
|
except TypeError: # raised by unicodedata.decomposition if there was no unicode character in parsed data
|
||||||
try:
|
try:
|
||||||
amount = float(ingredient_split[0].replace(',', '.'))
|
amount = float(ingredient_split[0].replace(',', '.'))
|
||||||
except ValueError:
|
except ValueError:
|
||||||
|
|||||||
BIN
cookbook/locale/ca/LC_MESSAGES/django.mo
Normal file
BIN
cookbook/locale/ca/LC_MESSAGES/django.mo
Normal file
Binary file not shown.
1889
cookbook/locale/ca/LC_MESSAGES/django.po
Normal file
1889
cookbook/locale/ca/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
Binary file not shown.
File diff suppressed because it is too large
Load Diff
1988
cookbook/locale/es/LC_MESSAGES/django.po
Normal file
1988
cookbook/locale/es/LC_MESSAGES/django.po
Normal file
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
1968
cookbook/locale/it/LC_MESSAGES/django.po
Normal file
1968
cookbook/locale/it/LC_MESSAGES/django.po
Normal file
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@@ -6,8 +6,6 @@
|
|||||||
# Translators:
|
# Translators:
|
||||||
# 31a3ead7f9b1ec8ada1a36808eee4069_988cec9 <9478557dfb8b6cd81570ee9e754f1719_904168>, 2020
|
# 31a3ead7f9b1ec8ada1a36808eee4069_988cec9 <9478557dfb8b6cd81570ee9e754f1719_904168>, 2020
|
||||||
# Jesse Kamps <jkamps@gmail.com>, 2020
|
# Jesse Kamps <jkamps@gmail.com>, 2020
|
||||||
# Frank Engbers <ikbenfrank@gmail.com>, 2020
|
|
||||||
#
|
|
||||||
#, fuzzy
|
#, fuzzy
|
||||||
msgid ""
|
msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -15,12 +13,9 @@ msgstr ""
|
|||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2020-12-15 21:39+0100\n"
|
"POT-Creation-Date: 2020-12-15 21:39+0100\n"
|
||||||
"PO-Revision-Date: 2020-06-02 19:28+0000\n"
|
"PO-Revision-Date: 2020-06-02 19:28+0000\n"
|
||||||
"Last-Translator: Frank Engbers <ikbenfrank@gmail.com>, 2020\n"
|
|
||||||
"Language-Team: Dutch (https://www.transifex.com/django-recipes/teams/110507/nl/)\n"
|
|
||||||
"MIME-Version: 1.0\n"
|
"MIME-Version: 1.0\n"
|
||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"Language: nl\n"
|
|
||||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||||
|
|
||||||
#: .\cookbook\filters.py:15 .\cookbook\templates\base.html:80
|
#: .\cookbook\filters.py:15 .\cookbook\templates\base.html:80
|
||||||
@@ -183,11 +178,11 @@ msgstr "Laat leeg voor nextcloud en vul de api token in voor dropbox."
|
|||||||
|
|
||||||
#: .\cookbook\forms.py:211
|
#: .\cookbook\forms.py:211
|
||||||
msgid ""
|
msgid ""
|
||||||
"Leave empty for dropbox and enter only base url for nextcloud "
|
"Leave empty for dropbox and enter only base url for nextcloud (<code>/remote."
|
||||||
"(<code>/remote.php/webdav/</code> is added automatically)"
|
"php/webdav/</code> is added automatically)"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Laat leeg voor dropbox en vul enkel de base url voor nextcloud in. "
|
"Laat leeg voor dropbox en vul enkel de base url voor nextcloud in. (<code>/"
|
||||||
"(<code>/remote.php/webdav/</code> wordt automatisch toegevoegd.)"
|
"remote.php/webdav/</code> wordt automatisch toegevoegd.)"
|
||||||
|
|
||||||
#: .\cookbook\forms.py:230
|
#: .\cookbook\forms.py:230
|
||||||
msgid "Search String"
|
msgid "Search String"
|
||||||
@@ -210,11 +205,11 @@ msgstr ""
|
|||||||
#: .\cookbook\forms.py:272
|
#: .\cookbook\forms.py:272
|
||||||
#: .\cookbook\templates\forms\edit_internal_recipe.html:352
|
#: .\cookbook\templates\forms\edit_internal_recipe.html:352
|
||||||
msgid ""
|
msgid ""
|
||||||
"You can use markdown to format this field. See the <a "
|
"You can use markdown to format this field. See the <a href=\"/docs/markdown/"
|
||||||
"href=\"/docs/markdown/\">docs here</a>"
|
"\">docs here</a>"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Je kunt markdown gebruiken om dit veld te op te maken. Bekijk de <a "
|
"Je kunt markdown gebruiken om dit veld te op te maken. Bekijk de <a href=\"/"
|
||||||
"href=\"/docs/markdown/\">documentatie hier</a>."
|
"docs/markdown/\">documentatie hier</a>."
|
||||||
|
|
||||||
#: .\cookbook\forms.py:273
|
#: .\cookbook\forms.py:273
|
||||||
msgid "Scaling factor for recipe."
|
msgid "Scaling factor for recipe."
|
||||||
@@ -854,14 +849,18 @@ msgstr "Ingrediënten bewerken"
|
|||||||
#: .\cookbook\templates\forms\ingredients.html:16
|
#: .\cookbook\templates\forms\ingredients.html:16
|
||||||
msgid ""
|
msgid ""
|
||||||
"\n"
|
"\n"
|
||||||
" The following form can be used if, accidentally, two (or more) units or ingredients where created that should be\n"
|
" The following form can be used if, accidentally, two (or more) units "
|
||||||
|
"or ingredients where created that should be\n"
|
||||||
" the same.\n"
|
" the same.\n"
|
||||||
" It merges two units or ingredients and updates all recipes using them.\n"
|
" It merges two units or ingredients and updates all recipes using "
|
||||||
|
"them.\n"
|
||||||
" "
|
" "
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"\n"
|
"\n"
|
||||||
"Het volgende formulier kan worden gebruikt wanneer per ongeluk twee (of meer) eenheden of ingrediënten zijn gecreëerd dat eigenlijk hetzelfde zijn.\n"
|
"Het volgende formulier kan worden gebruikt wanneer per ongeluk twee (of "
|
||||||
"Het doet de twee eenheden of ingrediënten samenvoegen en alle bijbehorende recepten updaten."
|
"meer) eenheden of ingrediënten zijn gecreëerd dat eigenlijk hetzelfde zijn.\n"
|
||||||
|
"Het doet de twee eenheden of ingrediënten samenvoegen en alle bijbehorende "
|
||||||
|
"recepten updaten."
|
||||||
|
|
||||||
#: .\cookbook\templates\forms\ingredients.html:24
|
#: .\cookbook\templates\forms\ingredients.html:24
|
||||||
#: .\cookbook\templates\stats.html:26
|
#: .\cookbook\templates\stats.html:26
|
||||||
@@ -986,16 +985,22 @@ msgstr "Veiligheidswaarschuwing"
|
|||||||
#: .\cookbook\templates\include\storage_backend_warning.html:5
|
#: .\cookbook\templates\include\storage_backend_warning.html:5
|
||||||
msgid ""
|
msgid ""
|
||||||
"\n"
|
"\n"
|
||||||
" The <b>Password and Token</b> field are stored as <b>plain text</b> inside the database.\n"
|
" The <b>Password and Token</b> field are stored as <b>plain text</b> "
|
||||||
" This is necessary because they are needed to make API requests, but it also increases the risk of\n"
|
"inside the database.\n"
|
||||||
|
" This is necessary because they are needed to make API requests, but "
|
||||||
|
"it also increases the risk of\n"
|
||||||
" someone stealing it. <br/>\n"
|
" someone stealing it. <br/>\n"
|
||||||
" To limit the possible damage tokens or accounts with limited access can be used.\n"
|
" To limit the possible damage tokens or accounts with limited access "
|
||||||
|
"can be used.\n"
|
||||||
" "
|
" "
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"\n"
|
"\n"
|
||||||
"Het <b>wachtwoord en token</b> veld worden als <b>plain text</b> in de database opgeslagen.\n"
|
"Het <b>wachtwoord en token</b> veld worden als <b>plain text</b> in de "
|
||||||
"Dit is benodigd omdat deze benodigd zijn voor de API requests, Echter verhoogd dit ook het risico van diefstal.<br/>\n"
|
"database opgeslagen.\n"
|
||||||
"Om mogelijke schade te beperken kunt u gebruik maken van account met gelimiteerde toegang."
|
"Dit is benodigd omdat deze benodigd zijn voor de API requests, Echter "
|
||||||
|
"verhoogd dit ook het risico van diefstal.<br/>\n"
|
||||||
|
"Om mogelijke schade te beperken kunt u gebruik maken van account met "
|
||||||
|
"gelimiteerde toegang."
|
||||||
|
|
||||||
#: .\cookbook\templates\index.html:29
|
#: .\cookbook\templates\index.html:29
|
||||||
msgid "Search recipe ..."
|
msgid "Search recipe ..."
|
||||||
@@ -1038,17 +1043,26 @@ msgstr "Markdown informatie"
|
|||||||
#: .\cookbook\templates\markdown_info.html:14
|
#: .\cookbook\templates\markdown_info.html:14
|
||||||
msgid ""
|
msgid ""
|
||||||
"\n"
|
"\n"
|
||||||
" Markdown is lightweight markup language that can be used to format plain text easily.\n"
|
" Markdown is lightweight markup language that can be used to format "
|
||||||
" This site uses the <a href=\"https://python-markdown.github.io/\" target=\"_blank\">Python Markdown</a> library to\n"
|
"plain text easily.\n"
|
||||||
" convert your text into nice looking html. Its full markdown documentation can be found\n"
|
" This site uses the <a href=\"https://python-markdown.github.io/\" "
|
||||||
" <a href=\"https://daringfireball.net/projects/markdown/syntax\" target=\"_blank\">here</a>.\n"
|
"target=\"_blank\">Python Markdown</a> library to\n"
|
||||||
" An incomplete but most likely sufficient documentation can be found below.\n"
|
" convert your text into nice looking html. Its full markdown "
|
||||||
|
"documentation can be found\n"
|
||||||
|
" <a href=\"https://daringfireball.net/projects/markdown/syntax\" "
|
||||||
|
"target=\"_blank\">here</a>.\n"
|
||||||
|
" An incomplete but most likely sufficient documentation can be found "
|
||||||
|
"below.\n"
|
||||||
" "
|
" "
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"\n"
|
"\n"
|
||||||
"Markdown is een lichtgewicht markup taal, waarmee gemakkelijk tekst opgemaakt kan worden.\n"
|
"Markdown is een lichtgewicht markup taal, waarmee gemakkelijk tekst "
|
||||||
"Deze site maakt gebruik van de <a href=\"https://python-markdown.github.io/\" target=\"_blank\"> Python Markdown</a> bibliotheek om \n"
|
"opgemaakt kan worden.\n"
|
||||||
"je tekst naar mooie html om te zetten. De volledige documentatie van deze bibliotheek is <a href=\"https://daringfireball.net/projects/markdown/syntax\" target=\"_blank\">hier</a> te bekijken.\n"
|
"Deze site maakt gebruik van de <a href=\"https://python-markdown.github.io/"
|
||||||
|
"\" target=\"_blank\"> Python Markdown</a> bibliotheek om \n"
|
||||||
|
"je tekst naar mooie html om te zetten. De volledige documentatie van deze "
|
||||||
|
"bibliotheek is <a href=\"https://daringfireball.net/projects/markdown/syntax"
|
||||||
|
"\" target=\"_blank\">hier</a> te bekijken.\n"
|
||||||
"Onvolledige, maar vermoedelijk voldoende uitleg, kan je hieronder bekijken."
|
"Onvolledige, maar vermoedelijk voldoende uitleg, kan je hieronder bekijken."
|
||||||
|
|
||||||
#: .\cookbook\templates\markdown_info.html:25
|
#: .\cookbook\templates\markdown_info.html:25
|
||||||
@@ -1136,8 +1150,7 @@ msgid ""
|
|||||||
"links directly into markdown fields without any formatting."
|
"links directly into markdown fields without any formatting."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Het is mogelijk om Links te formatteren met Markdown. Deze aplicatie staat "
|
"Het is mogelijk om Links te formatteren met Markdown. Deze aplicatie staat "
|
||||||
"het ook toe om links direct in het tekst velt te plakken zonder "
|
"het ook toe om links direct in het tekst velt te plakken zonder formattering."
|
||||||
"formattering."
|
|
||||||
|
|
||||||
#: .\cookbook\templates\markdown_info.html:132
|
#: .\cookbook\templates\markdown_info.html:132
|
||||||
#: .\cookbook\templates\markdown_info.html:145
|
#: .\cookbook\templates\markdown_info.html:145
|
||||||
@@ -1155,9 +1168,8 @@ msgid ""
|
|||||||
"target=\"_blank\">this</a> one."
|
"target=\"_blank\">this</a> one."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Het is lastig om markdown tabellen handmatig te creëren. Het is geadviseerd "
|
"Het is lastig om markdown tabellen handmatig te creëren. Het is geadviseerd "
|
||||||
"dat u een tabel bewerker zoals <a "
|
"dat u een tabel bewerker zoals <a href=\"https://www.tablesgenerator.com/"
|
||||||
"href=\"https://www.tablesgenerator.com/markdown_tables\" "
|
"markdown_tables\" target=\"_blank\">deze</a> gebruikt."
|
||||||
"target=\"_blank\">deze</a> gebruikt."
|
|
||||||
|
|
||||||
#: .\cookbook\templates\markdown_info.html:155
|
#: .\cookbook\templates\markdown_info.html:155
|
||||||
#: .\cookbook\templates\markdown_info.html:157
|
#: .\cookbook\templates\markdown_info.html:157
|
||||||
@@ -1196,13 +1208,11 @@ msgstr "Notitie (optioneel)"
|
|||||||
|
|
||||||
#: .\cookbook\templates\meal_plan.html:139
|
#: .\cookbook\templates\meal_plan.html:139
|
||||||
msgid ""
|
msgid ""
|
||||||
"You can use markdown to format this field. See the <a "
|
"You can use markdown to format this field. See the <a href=\"/docs/markdown/"
|
||||||
"href=\"/docs/markdown/\" target=\"_blank\" rel=\"noopener noreferrer\">docs "
|
"\" target=\"_blank\" rel=\"noopener noreferrer\">docs here</a>"
|
||||||
"here</a>"
|
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Je kan markdown gebruiken om dit veld op te maken. Zie de <a "
|
"Je kan markdown gebruiken om dit veld op te maken. Zie de <a href=\"/docs/"
|
||||||
"href=\"/docs/markdown/\" target=\"_blank\" rel=\"noopener "
|
"markdown/\" target=\"_blank\" rel=\"noopener noreferrer\">documentatie</a>"
|
||||||
"noreferrer\">documentatie</a>"
|
|
||||||
|
|
||||||
#: .\cookbook\templates\meal_plan.html:143
|
#: .\cookbook\templates\meal_plan.html:143
|
||||||
msgid "Recipe Multiplier"
|
msgid "Recipe Multiplier"
|
||||||
@@ -1286,22 +1296,35 @@ msgstr "Maaltijdplanner hulp"
|
|||||||
#: .\cookbook\templates\meal_plan.html:337
|
#: .\cookbook\templates\meal_plan.html:337
|
||||||
msgid ""
|
msgid ""
|
||||||
"\n"
|
"\n"
|
||||||
" <p>The meal plan module allows planning of meals both with recipes or just notes.</p>\n"
|
" <p>The meal plan module allows planning of meals "
|
||||||
" <p>Simply select a recipe from the list of recently viewed recipes or search the one you\n"
|
"both with recipes or just notes.</p>\n"
|
||||||
" want and drag it to the desired plan position. You can also add a note and a title and\n"
|
" <p>Simply select a recipe from the list of "
|
||||||
" then drag the recipe to create a plan entry with a custom title and note. Creating only\n"
|
"recently viewed recipes or search the one you\n"
|
||||||
" Notes is possible by dragging the create note box into the plan.</p>\n"
|
" want and drag it to the desired plan "
|
||||||
" <p>Click on a recipe in order to open the detail view. Here you can also add it to the\n"
|
"position. You can also add a note and a title and\n"
|
||||||
" shopping list. You can also add all recipes of a day to the shopping list by\n"
|
" then drag the recipe to create a plan entry "
|
||||||
" clicking the shopping cart at the top of the table.</p>\n"
|
"with a custom title and note. Creating only\n"
|
||||||
" <p>Since a common use case is to plan meals together you can define\n"
|
" Notes is possible by dragging the create "
|
||||||
" users you want to share your plan with in the settings.\n"
|
"note box into the plan.</p>\n"
|
||||||
|
" <p>Click on a recipe in order to open the detail "
|
||||||
|
"view. Here you can also add it to the\n"
|
||||||
|
" shopping list. You can also add all recipes "
|
||||||
|
"of a day to the shopping list by\n"
|
||||||
|
" clicking the shopping cart at the top of the "
|
||||||
|
"table.</p>\n"
|
||||||
|
" <p>Since a common use case is to plan meals "
|
||||||
|
"together you can define\n"
|
||||||
|
" users you want to share your plan with in "
|
||||||
|
"the settings.\n"
|
||||||
" </p>\n"
|
" </p>\n"
|
||||||
" <p>You can also edit the types of meals you want to plan. If you share your plan with\n"
|
" <p>You can also edit the types of meals you want "
|
||||||
|
"to plan. If you share your plan with\n"
|
||||||
" someone with\n"
|
" someone with\n"
|
||||||
" different meals, their meal types will appear in your list as well. To prevent\n"
|
" different meals, their meal types will "
|
||||||
|
"appear in your list as well. To prevent\n"
|
||||||
" duplicates (e.g. Other and Misc.)\n"
|
" duplicates (e.g. Other and Misc.)\n"
|
||||||
" name your meal types the same as the users you share your meals with and they will be\n"
|
" name your meal types the same as the users "
|
||||||
|
"you share your meals with and they will be\n"
|
||||||
" merged.</p>\n"
|
" merged.</p>\n"
|
||||||
" "
|
" "
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@@ -1394,17 +1417,21 @@ msgstr "Extern recept"
|
|||||||
#: .\cookbook\templates\recipe_view.html:417
|
#: .\cookbook\templates\recipe_view.html:417
|
||||||
msgid ""
|
msgid ""
|
||||||
"\n"
|
"\n"
|
||||||
" This is an external recipe, which means you can only view it by opening the link\n"
|
" This is an external recipe, which means "
|
||||||
|
"you can only view it by opening the link\n"
|
||||||
" above.\n"
|
" above.\n"
|
||||||
" You can convert this recipe to a fancy recipe by pressing the convert button. The\n"
|
" You can convert this recipe to a fancy "
|
||||||
|
"recipe by pressing the convert button. The\n"
|
||||||
" original\n"
|
" original\n"
|
||||||
" file\n"
|
" file\n"
|
||||||
" will still be accessible.\n"
|
" will still be accessible.\n"
|
||||||
" "
|
" "
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"\n"
|
"\n"
|
||||||
"Dit is een extern recept, dat betekent dat je het dient te openen met de bovenstaande link.\n"
|
"Dit is een extern recept, dat betekent dat je het dient te openen met de "
|
||||||
"Je kan dit recept naar een flitsend recept omzetten door op de converteer knop te klikken.\n"
|
"bovenstaande link.\n"
|
||||||
|
"Je kan dit recept naar een flitsend recept omzetten door op de converteer "
|
||||||
|
"knop te klikken.\n"
|
||||||
"Het originele bestand blijft beschikbaar."
|
"Het originele bestand blijft beschikbaar."
|
||||||
|
|
||||||
#: .\cookbook\templates\recipe_view.html:428
|
#: .\cookbook\templates\recipe_view.html:428
|
||||||
@@ -1611,15 +1638,19 @@ msgstr "Systeeminformatie"
|
|||||||
#: .\cookbook\templates\system.html:39
|
#: .\cookbook\templates\system.html:39
|
||||||
msgid ""
|
msgid ""
|
||||||
"\n"
|
"\n"
|
||||||
" Django Recipes is an open source free software application. It can be found on\n"
|
" Django Recipes is an open source free software application. It can "
|
||||||
|
"be found on\n"
|
||||||
" <a href=\"https://github.com/vabene1111/recipes\">GitHub</a>.\n"
|
" <a href=\"https://github.com/vabene1111/recipes\">GitHub</a>.\n"
|
||||||
" Changelogs can be found <a href=\"https://github.com/vabene1111/recipes/releases\">here</a>.\n"
|
" Changelogs can be found <a href=\"https://github.com/vabene1111/"
|
||||||
|
"recipes/releases\">here</a>.\n"
|
||||||
" "
|
" "
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"\n"
|
"\n"
|
||||||
"Django Recipes is een open source gratis software applicatie. Het kan gevonden worden op\n"
|
"Django Recipes is een open source gratis software applicatie. Het kan "
|
||||||
|
"gevonden worden op\n"
|
||||||
"<a href=\"https://github.com/vabene1111/recipes\">GitHub</a>.\n"
|
"<a href=\"https://github.com/vabene1111/recipes\">GitHub</a>.\n"
|
||||||
"Wijzigingenoverzichten kunnen <a href=\"https://github.com/vabene1111/recipes/releases\">hier</a> gevonden worden."
|
"Wijzigingenoverzichten kunnen <a href=\"https://github.com/vabene1111/"
|
||||||
|
"recipes/releases\">hier</a> gevonden worden."
|
||||||
|
|
||||||
#: .\cookbook\templates\system.html:53
|
#: .\cookbook\templates\system.html:53
|
||||||
msgid "Media Serving"
|
msgid "Media Serving"
|
||||||
@@ -1639,12 +1670,15 @@ msgstr "Ok"
|
|||||||
msgid ""
|
msgid ""
|
||||||
"Serving media files directly using gunicorn/python is <b>not recommend</b>!\n"
|
"Serving media files directly using gunicorn/python is <b>not recommend</b>!\n"
|
||||||
" Please follow the steps described\n"
|
" Please follow the steps described\n"
|
||||||
" <a href=\"https://github.com/vabene1111/recipes/releases/tag/0.8.1\">here</a> to update\n"
|
" <a href=\"https://github.com/vabene1111/recipes/releases/"
|
||||||
|
"tag/0.8.1\">here</a> to update\n"
|
||||||
" your installation.\n"
|
" your installation.\n"
|
||||||
" "
|
" "
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Mediabestanden rechtstreeks aanbieden met gunicorn/python is <b>niet aanbevolen</b>!\n"
|
"Mediabestanden rechtstreeks aanbieden met gunicorn/python is <b>niet "
|
||||||
"Volg de stappen zoals <a href=\"https://github.com/vabene1111/recipes/releases/tag/0.8.1\">hier</a> beschreven om je installatie te updaten."
|
"aanbevolen</b>!\n"
|
||||||
|
"Volg de stappen zoals <a href=\"https://github.com/vabene1111/recipes/"
|
||||||
|
"releases/tag/0.8.1\">hier</a> beschreven om je installatie te updaten."
|
||||||
|
|
||||||
#: .\cookbook\templates\system.html:62 .\cookbook\templates\system.html:78
|
#: .\cookbook\templates\system.html:62 .\cookbook\templates\system.html:78
|
||||||
#: .\cookbook\templates\system.html:93 .\cookbook\templates\system.html:107
|
#: .\cookbook\templates\system.html:93 .\cookbook\templates\system.html:107
|
||||||
@@ -1658,15 +1692,20 @@ msgstr "Geheime sleutel"
|
|||||||
#: .\cookbook\templates\system.html:71
|
#: .\cookbook\templates\system.html:71
|
||||||
msgid ""
|
msgid ""
|
||||||
"\n"
|
"\n"
|
||||||
" You do not have a <code>SECRET_KEY</code> configured in your <code>.env</code> file. Django defaulted to the\n"
|
" You do not have a <code>SECRET_KEY</code> configured in your "
|
||||||
|
"<code>.env</code> file. Django defaulted to the\n"
|
||||||
" standard key\n"
|
" standard key\n"
|
||||||
" provided with the installation which is publicly know and insecure! Please set\n"
|
" provided with the installation which is publicly know and "
|
||||||
" <code>SECRET_KEY</code> int the <code>.env</code> configuration file.\n"
|
"insecure! Please set\n"
|
||||||
|
" <code>SECRET_KEY</code> int the <code>.env</code> configuration "
|
||||||
|
"file.\n"
|
||||||
" "
|
" "
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"\n"
|
"\n"
|
||||||
"Je hebt geen <code>SECRET_KEY</code> geconfigureerd in je .env bestand.\n"
|
"Je hebt geen <code>SECRET_KEY</code> geconfigureerd in je .env bestand.\n"
|
||||||
"Django is overgegaan naar de standaard sleutel die openbaar en onveilig is! Stel alsjeblieft <code>SECRET_KEY</code>in in het <code>.env</code> configuratiebestand."
|
"Django is overgegaan naar de standaard sleutel die openbaar en onveilig is! "
|
||||||
|
"Stel alsjeblieft <code>SECRET_KEY</code>in in het <code>.env</code> "
|
||||||
|
"configuratiebestand."
|
||||||
|
|
||||||
#: .\cookbook\templates\system.html:83
|
#: .\cookbook\templates\system.html:83
|
||||||
msgid "Debug Mode"
|
msgid "Debug Mode"
|
||||||
@@ -1675,13 +1714,17 @@ msgstr "Debug modus"
|
|||||||
#: .\cookbook\templates\system.html:87
|
#: .\cookbook\templates\system.html:87
|
||||||
msgid ""
|
msgid ""
|
||||||
"\n"
|
"\n"
|
||||||
" This application is still running in debug mode. This is most likely not needed. Turn of debug mode by\n"
|
" This application is still running in debug mode. This is most "
|
||||||
|
"likely not needed. Turn of debug mode by\n"
|
||||||
" setting\n"
|
" setting\n"
|
||||||
" <code>DEBUG=0</code> int the <code>.env</code> configuration file.\n"
|
" <code>DEBUG=0</code> int the <code>.env</code> configuration "
|
||||||
|
"file.\n"
|
||||||
" "
|
" "
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"\n"
|
"\n"
|
||||||
"Deze applicatie draait in debug modus. Dit is waarschijnlijk niet nodig. Schakel debug modus uit door de instelling <code>DEBUG=0</code> in het <code>.env</code>configuratiebestand aan te passen."
|
"Deze applicatie draait in debug modus. Dit is waarschijnlijk niet nodig. "
|
||||||
|
"Schakel debug modus uit door de instelling <code>DEBUG=0</code> in het "
|
||||||
|
"<code>.env</code>configuratiebestand aan te passen."
|
||||||
|
|
||||||
#: .\cookbook\templates\system.html:98
|
#: .\cookbook\templates\system.html:98
|
||||||
msgid "Database"
|
msgid "Database"
|
||||||
@@ -1694,12 +1737,15 @@ msgstr "Info"
|
|||||||
#: .\cookbook\templates\system.html:102
|
#: .\cookbook\templates\system.html:102
|
||||||
msgid ""
|
msgid ""
|
||||||
"\n"
|
"\n"
|
||||||
" This application is not running with a Postgres database backend. This is ok but not recommended as some\n"
|
" This application is not running with a Postgres database "
|
||||||
|
"backend. This is ok but not recommended as some\n"
|
||||||
" features only work with postgres databases.\n"
|
" features only work with postgres databases.\n"
|
||||||
" "
|
" "
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"\n"
|
"\n"
|
||||||
"Deze applicatie draait niet met een Postgres database als backend. Dit is ok maar wordt niet aanbevolen omdat sommige functies alleen werken met Postgres databases."
|
"Deze applicatie draait niet met een Postgres database als backend. Dit is ok "
|
||||||
|
"maar wordt niet aanbevolen omdat sommige functies alleen werken met Postgres "
|
||||||
|
"databases."
|
||||||
|
|
||||||
#: .\cookbook\templates\url_import.html:5
|
#: .\cookbook\templates\url_import.html:5
|
||||||
msgid "URL Import"
|
msgid "URL Import"
|
||||||
@@ -1734,9 +1780,11 @@ msgstr "Informatie"
|
|||||||
#: .\cookbook\templates\url_import.html:227
|
#: .\cookbook\templates\url_import.html:227
|
||||||
msgid ""
|
msgid ""
|
||||||
" Only websites containing ld+json or microdata information can currently\n"
|
" Only websites containing ld+json or microdata information can currently\n"
|
||||||
" be imported. Most big recipe pages support this. If you site cannot be imported but\n"
|
" be imported. Most big recipe pages "
|
||||||
|
"support this. If you site cannot be imported but\n"
|
||||||
" you think\n"
|
" you think\n"
|
||||||
" it probably has some kind of structured data feel free to post an example in the\n"
|
" it probably has some kind of structured "
|
||||||
|
"data feel free to post an example in the\n"
|
||||||
" github issues."
|
" github issues."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Alleen websites die Id+json of microdata informatie bevatten kunnen op dit "
|
"Alleen websites die Id+json of microdata informatie bevatten kunnen op dit "
|
||||||
|
|||||||
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
File diff suppressed because it is too large
Load Diff
36
cookbook/migrations/0089_auto_20201117_2222.py
Normal file
36
cookbook/migrations/0089_auto_20201117_2222.py
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
# Generated by Django 3.1.1 on 2020-11-17 21:22
|
||||||
|
|
||||||
|
import datetime
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('cookbook', '0088_shoppinglist_finished'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='NutritionInformation',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('fats', models.DecimalField(decimal_places=16, default=0, max_digits=32)),
|
||||||
|
('carbohydrates', models.DecimalField(decimal_places=16, default=0, max_digits=32)),
|
||||||
|
('proteins', models.DecimalField(decimal_places=16, default=0, max_digits=32)),
|
||||||
|
('calories', models.DecimalField(decimal_places=16, default=0, max_digits=32)),
|
||||||
|
('source', models.CharField(blank=True, default='', max_length=512, null=True)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='invitelink',
|
||||||
|
name='valid_until',
|
||||||
|
field=models.DateField(default=datetime.date(2020, 12, 1)),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='recipe',
|
||||||
|
name='nutrition',
|
||||||
|
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='cookbook.nutritioninformation'),
|
||||||
|
),
|
||||||
|
]
|
||||||
24
cookbook/migrations/0090_auto_20201214_1359.py
Normal file
24
cookbook/migrations/0090_auto_20201214_1359.py
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
# Generated by Django 3.1.3 on 2020-12-14 12:59
|
||||||
|
|
||||||
|
import datetime
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('cookbook', '0089_auto_20201117_2222'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='userpreference',
|
||||||
|
name='use_fractions',
|
||||||
|
field=models.BooleanField(default=False),
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='invitelink',
|
||||||
|
name='valid_until',
|
||||||
|
field=models.DateField(default=datetime.date(2020, 12, 28)),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -9,7 +9,7 @@ from django.utils.translation import gettext as _
|
|||||||
from django.db import models
|
from django.db import models
|
||||||
from django_random_queryset import RandomManager
|
from django_random_queryset import RandomManager
|
||||||
|
|
||||||
from recipes.settings import COMMENT_PREF_DEFAULT
|
from recipes.settings import COMMENT_PREF_DEFAULT, FRACTION_PREF_DEFAULT
|
||||||
|
|
||||||
|
|
||||||
def get_user_name(self):
|
def get_user_name(self):
|
||||||
@@ -69,6 +69,7 @@ class UserPreference(models.Model):
|
|||||||
theme = models.CharField(choices=THEMES, max_length=128, default=FLATLY)
|
theme = models.CharField(choices=THEMES, max_length=128, default=FLATLY)
|
||||||
nav_color = models.CharField(choices=COLORS, max_length=128, default=PRIMARY)
|
nav_color = models.CharField(choices=COLORS, max_length=128, default=PRIMARY)
|
||||||
default_unit = models.CharField(max_length=32, default='g')
|
default_unit = models.CharField(max_length=32, default='g')
|
||||||
|
use_fractions = models.BooleanField(default=FRACTION_PREF_DEFAULT)
|
||||||
default_page = models.CharField(choices=PAGES, max_length=64, default=SEARCH)
|
default_page = models.CharField(choices=PAGES, max_length=64, default=SEARCH)
|
||||||
search_style = models.CharField(choices=SEARCH_STYLE, max_length=64, default=LARGE)
|
search_style = models.CharField(choices=SEARCH_STYLE, max_length=64, default=LARGE)
|
||||||
show_recent = models.BooleanField(default=True)
|
show_recent = models.BooleanField(default=True)
|
||||||
@@ -182,6 +183,17 @@ class Step(models.Model):
|
|||||||
ordering = ['order', 'pk']
|
ordering = ['order', 'pk']
|
||||||
|
|
||||||
|
|
||||||
|
class NutritionInformation(models.Model):
|
||||||
|
fats = models.DecimalField(default=0, decimal_places=16, max_digits=32)
|
||||||
|
carbohydrates = models.DecimalField(default=0, decimal_places=16, max_digits=32)
|
||||||
|
proteins = models.DecimalField(default=0, decimal_places=16, max_digits=32)
|
||||||
|
calories = models.DecimalField(default=0, decimal_places=16, max_digits=32)
|
||||||
|
source = models.CharField(max_length=512, default="", null=True, blank=True)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f'Nutrition'
|
||||||
|
|
||||||
|
|
||||||
class Recipe(models.Model):
|
class Recipe(models.Model):
|
||||||
name = models.CharField(max_length=128)
|
name = models.CharField(max_length=128)
|
||||||
image = models.ImageField(upload_to='recipes/', blank=True, null=True)
|
image = models.ImageField(upload_to='recipes/', blank=True, null=True)
|
||||||
@@ -195,6 +207,7 @@ class Recipe(models.Model):
|
|||||||
working_time = models.IntegerField(default=0)
|
working_time = models.IntegerField(default=0)
|
||||||
waiting_time = models.IntegerField(default=0)
|
waiting_time = models.IntegerField(default=0)
|
||||||
internal = models.BooleanField(default=False)
|
internal = models.BooleanField(default=False)
|
||||||
|
nutrition = models.ForeignKey(NutritionInformation, blank=True, null=True, on_delete=models.CASCADE)
|
||||||
created_by = models.ForeignKey(User, on_delete=models.PROTECT)
|
created_by = models.ForeignKey(User, on_delete=models.PROTECT)
|
||||||
created_at = models.DateTimeField(auto_now_add=True)
|
created_at = models.DateTimeField(auto_now_add=True)
|
||||||
updated_at = models.DateTimeField(auto_now=True)
|
updated_at = models.DateTimeField(auto_now=True)
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ from rest_framework import serializers
|
|||||||
from rest_framework.exceptions import ValidationError
|
from rest_framework.exceptions import ValidationError
|
||||||
|
|
||||||
from cookbook.models import MealPlan, MealType, Recipe, ViewLog, UserPreference, Storage, Sync, SyncLog, Keyword, Unit, Ingredient, Comment, RecipeImport, RecipeBook, RecipeBookEntry, ShareLink, CookLog, Food, Step, ShoppingList, \
|
from cookbook.models import MealPlan, MealType, Recipe, ViewLog, UserPreference, Storage, Sync, SyncLog, Keyword, Unit, Ingredient, Comment, RecipeImport, RecipeBook, RecipeBookEntry, ShareLink, CookLog, Food, Step, ShoppingList, \
|
||||||
ShoppingListEntry, ShoppingListRecipe
|
ShoppingListEntry, ShoppingListRecipe, NutritionInformation
|
||||||
from cookbook.templatetags.custom_tags import markdown
|
from cookbook.templatetags.custom_tags import markdown
|
||||||
|
|
||||||
|
|
||||||
@@ -140,13 +140,20 @@ class StepSerializer(WritableNestedModelSerializer):
|
|||||||
fields = ('id', 'name', 'type', 'instruction', 'ingredients', 'time', 'order', 'show_as_header')
|
fields = ('id', 'name', 'type', 'instruction', 'ingredients', 'time', 'order', 'show_as_header')
|
||||||
|
|
||||||
|
|
||||||
|
class NutritionInformationSerializer(serializers.ModelSerializer):
|
||||||
|
class Meta:
|
||||||
|
model = NutritionInformation
|
||||||
|
fields = ('carbohydrates', 'fats', 'proteins', 'calories', 'source')
|
||||||
|
|
||||||
|
|
||||||
class RecipeSerializer(WritableNestedModelSerializer):
|
class RecipeSerializer(WritableNestedModelSerializer):
|
||||||
|
nutrition = NutritionInformationSerializer(allow_null=True, required=False)
|
||||||
steps = StepSerializer(many=True)
|
steps = StepSerializer(many=True)
|
||||||
keywords = KeywordSerializer(many=True)
|
keywords = KeywordSerializer(many=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Recipe
|
model = Recipe
|
||||||
fields = ['id', 'name', 'image', 'keywords', 'steps', 'working_time', 'waiting_time', 'created_by', 'created_at', 'updated_at', 'internal']
|
fields = ['id', 'name', 'image', 'keywords', 'steps', 'working_time', 'waiting_time', 'created_by', 'created_at', 'updated_at', 'internal', 'nutrition']
|
||||||
read_only_fields = ['image', 'created_by', 'created_at']
|
read_only_fields = ['image', 'created_by', 'created_at']
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
43
cookbook/static/js/frac.js
Normal file
43
cookbook/static/js/frac.js
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
/* frac.js (C) 2012-present SheetJS -- http://sheetjs.com */
|
||||||
|
/*https://developer.aliyun.com/mirror/npm/package/frac/v/0.3.0 Apache license*/
|
||||||
|
var frac = function frac(x, D, mixed) {
|
||||||
|
var n1 = Math.floor(x), d1 = 1;
|
||||||
|
var n2 = n1+1, d2 = 1;
|
||||||
|
if(x !== n1) while(d1 <= D && d2 <= D) {
|
||||||
|
var m = (n1 + n2) / (d1 + d2);
|
||||||
|
if(x === m) {
|
||||||
|
if(d1 + d2 <= D) { d1+=d2; n1+=n2; d2=D+1; }
|
||||||
|
else if(d1 > d2) d2=D+1;
|
||||||
|
else d1=D+1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
else if(x < m) { n2 = n1+n2; d2 = d1+d2; }
|
||||||
|
else { n1 = n1+n2; d1 = d1+d2; }
|
||||||
|
}
|
||||||
|
if(d1 > D) { d1 = d2; n1 = n2; }
|
||||||
|
if(!mixed) return [0, n1, d1];
|
||||||
|
var q = Math.floor(n1/d1);
|
||||||
|
return [q, n1 - q*d1, d1];
|
||||||
|
};
|
||||||
|
frac.cont = function cont(x, D, mixed) {
|
||||||
|
var sgn = x < 0 ? -1 : 1;
|
||||||
|
var B = x * sgn;
|
||||||
|
var P_2 = 0, P_1 = 1, P = 0;
|
||||||
|
var Q_2 = 1, Q_1 = 0, Q = 0;
|
||||||
|
var A = Math.floor(B);
|
||||||
|
while(Q_1 < D) {
|
||||||
|
A = Math.floor(B);
|
||||||
|
P = A * P_1 + P_2;
|
||||||
|
Q = A * Q_1 + Q_2;
|
||||||
|
if((B - A) < 0.00000005) break;
|
||||||
|
B = 1 / (B - A);
|
||||||
|
P_2 = P_1; P_1 = P;
|
||||||
|
Q_2 = Q_1; Q_1 = Q;
|
||||||
|
}
|
||||||
|
if(Q > D) { if(Q_1 > D) { Q = Q_2; P = P_2; } else { Q = Q_1; P = P_1; } }
|
||||||
|
if(!mixed) return [0, sgn * P, Q];
|
||||||
|
var q = Math.floor(sgn * P/Q);
|
||||||
|
return [q, sgn*P - q*Q, Q];
|
||||||
|
};
|
||||||
|
// eslint-disable-next-line no-undef
|
||||||
|
if(typeof module !== 'undefined' && typeof DO_NOT_EXPORT_FRAC === 'undefined') module.exports = frac;
|
||||||
146
cookbook/static/js/vue-cookies.js
Normal file
146
cookbook/static/js/vue-cookies.js
Normal file
@@ -0,0 +1,146 @@
|
|||||||
|
/**
|
||||||
|
* Vue Cookies v1.7.4
|
||||||
|
* https://github.com/cmp-cc/vue-cookies
|
||||||
|
*
|
||||||
|
* Copyright 2016, cmp-cc
|
||||||
|
* Released under the MIT license
|
||||||
|
*/
|
||||||
|
|
||||||
|
(function () {
|
||||||
|
|
||||||
|
var defaultConfig = {
|
||||||
|
expires: '1d',
|
||||||
|
path: '; path=/',
|
||||||
|
domain: '',
|
||||||
|
secure: '',
|
||||||
|
sameSite: '; SameSite=Lax'
|
||||||
|
};
|
||||||
|
|
||||||
|
var VueCookies = {
|
||||||
|
// install of Vue
|
||||||
|
install: function (Vue) {
|
||||||
|
Vue.prototype.$cookies = this;
|
||||||
|
Vue.$cookies = this;
|
||||||
|
},
|
||||||
|
config: function (expireTimes, path, domain, secure, sameSite) {
|
||||||
|
defaultConfig.expires = expireTimes ? expireTimes : '1d';
|
||||||
|
defaultConfig.path = path ? '; path=' + path : '; path=/';
|
||||||
|
defaultConfig.domain = domain ? '; domain=' + domain : '';
|
||||||
|
defaultConfig.secure = secure ? '; Secure' : '';
|
||||||
|
defaultConfig.sameSite = sameSite ? '; SameSite=' + sameSite : '; SameSite=Lax';
|
||||||
|
},
|
||||||
|
get: function (key) {
|
||||||
|
var value = decodeURIComponent(document.cookie.replace(new RegExp('(?:(?:^|.*;)\\s*' + encodeURIComponent(key).replace(/[\-\.\+\*]/g, '\\$&') + '\\s*\\=\\s*([^;]*).*$)|^.*$'), '$1')) || null;
|
||||||
|
|
||||||
|
if (value && value.substring(0, 1) === '{' && value.substring(value.length - 1, value.length) === '}') {
|
||||||
|
try {
|
||||||
|
value = JSON.parse(value);
|
||||||
|
} catch (e) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
},
|
||||||
|
set: function (key, value, expireTimes, path, domain, secure, sameSite) {
|
||||||
|
if (!key) {
|
||||||
|
throw new Error('Cookie name is not find in first argument.');
|
||||||
|
} else if (/^(?:expires|max\-age|path|domain|secure|SameSite)$/i.test(key)) {
|
||||||
|
throw new Error('Cookie key name illegality, Cannot be set to ["expires","max-age","path","domain","secure","SameSite"]\t current key name: ' + key);
|
||||||
|
}
|
||||||
|
// support json object
|
||||||
|
if (value && value.constructor === Object) {
|
||||||
|
value = JSON.stringify(value);
|
||||||
|
}
|
||||||
|
var _expires = '';
|
||||||
|
expireTimes = expireTimes == undefined ? defaultConfig.expires : expireTimes;
|
||||||
|
if (expireTimes && expireTimes != 0) {
|
||||||
|
switch (expireTimes.constructor) {
|
||||||
|
case Number:
|
||||||
|
if (expireTimes === Infinity || expireTimes === -1) _expires = '; expires=Fri, 31 Dec 9999 23:59:59 GMT';
|
||||||
|
else _expires = '; max-age=' + expireTimes;
|
||||||
|
break;
|
||||||
|
case String:
|
||||||
|
if (/^(?:\d+(y|m|d|h|min|s))$/i.test(expireTimes)) {
|
||||||
|
// get capture number group
|
||||||
|
var _expireTime = expireTimes.replace(/^(\d+)(?:y|m|d|h|min|s)$/i, '$1');
|
||||||
|
// get capture type group , to lower case
|
||||||
|
switch (expireTimes.replace(/^(?:\d+)(y|m|d|h|min|s)$/i, '$1').toLowerCase()) {
|
||||||
|
// Frequency sorting
|
||||||
|
case 'm':
|
||||||
|
_expires = '; max-age=' + +_expireTime * 2592000;
|
||||||
|
break; // 60 * 60 * 24 * 30
|
||||||
|
case 'd':
|
||||||
|
_expires = '; max-age=' + +_expireTime * 86400;
|
||||||
|
break; // 60 * 60 * 24
|
||||||
|
case 'h':
|
||||||
|
_expires = '; max-age=' + +_expireTime * 3600;
|
||||||
|
break; // 60 * 60
|
||||||
|
case 'min':
|
||||||
|
_expires = '; max-age=' + +_expireTime * 60;
|
||||||
|
break; // 60
|
||||||
|
case 's':
|
||||||
|
_expires = '; max-age=' + _expireTime;
|
||||||
|
break;
|
||||||
|
case 'y':
|
||||||
|
_expires = '; max-age=' + +_expireTime * 31104000;
|
||||||
|
break; // 60 * 60 * 24 * 30 * 12
|
||||||
|
default:
|
||||||
|
new Error('unknown exception of "set operation"');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
_expires = '; expires=' + expireTimes;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case Date:
|
||||||
|
_expires = '; expires=' + expireTimes.toUTCString();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
document.cookie =
|
||||||
|
encodeURIComponent(key) + '=' + encodeURIComponent(value) +
|
||||||
|
_expires +
|
||||||
|
(domain ? '; domain=' + domain : defaultConfig.domain) +
|
||||||
|
(path ? '; path=' + path : defaultConfig.path) +
|
||||||
|
(secure == undefined ? defaultConfig.secure : secure ? '; Secure' : '') +
|
||||||
|
(sameSite == undefined ? defaultConfig.sameSite : (sameSite ? '; SameSite=' + sameSite : ''));
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
remove: function (key, path, domain) {
|
||||||
|
if (!key || !this.isKey(key)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
document.cookie = encodeURIComponent(key) +
|
||||||
|
'=; expires=Thu, 01 Jan 1970 00:00:00 GMT' +
|
||||||
|
(domain ? '; domain=' + domain : defaultConfig.domain) +
|
||||||
|
(path ? '; path=' + path : defaultConfig.path) +
|
||||||
|
'; SameSite=Lax';
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
isKey: function (key) {
|
||||||
|
return (new RegExp('(?:^|;\\s*)' + encodeURIComponent(key).replace(/[\-\.\+\*]/g, '\\$&') + '\\s*\\=')).test(document.cookie);
|
||||||
|
},
|
||||||
|
keys: function () {
|
||||||
|
if (!document.cookie) return [];
|
||||||
|
var _keys = document.cookie.replace(/((?:^|\s*;)[^\=]+)(?=;|$)|^\s*|\s*(?:\=[^;]*)?(?:\1|$)/g, '').split(/\s*(?:\=[^;]*)?;\s*/);
|
||||||
|
for (var _index = 0; _index < _keys.length; _index++) {
|
||||||
|
_keys[_index] = decodeURIComponent(_keys[_index]);
|
||||||
|
}
|
||||||
|
return _keys;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (typeof exports == 'object') {
|
||||||
|
module.exports = VueCookies;
|
||||||
|
} else if (typeof define == 'function' && define.amd) {
|
||||||
|
define([], function () {
|
||||||
|
return VueCookies;
|
||||||
|
});
|
||||||
|
} else if (window.Vue) {
|
||||||
|
Vue.use(VueCookies);
|
||||||
|
}
|
||||||
|
// vue-cookies can exist independently,no dependencies library
|
||||||
|
if (typeof window !== 'undefined') {
|
||||||
|
window.$cookies = VueCookies;
|
||||||
|
}
|
||||||
|
|
||||||
|
})();
|
||||||
@@ -56,7 +56,7 @@
|
|||||||
<input type="file" @change="imageChanged">
|
<input type="file" @change="imageChanged">
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-6">
|
<div class="col-md-6">
|
||||||
<label for="id_name"> {% trans 'Preperation Time' %}</label>
|
<label for="id_name"> {% trans 'Preparation Time' %}</label>
|
||||||
<input class="form-control" id="id_prep_time" v-model="recipe.working_time">
|
<input class="form-control" id="id_prep_time" v-model="recipe.working_time">
|
||||||
<br/>
|
<br/>
|
||||||
<label for="id_name"> {% trans 'Waiting Time' %}</label>
|
<label for="id_name"> {% trans 'Waiting Time' %}</label>
|
||||||
@@ -80,6 +80,35 @@
|
|||||||
</multiselect>
|
</multiselect>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<template v-if="recipe !== undefined">
|
||||||
|
<div class="row" v-if="recipe.nutrition" style="margin-top: 1vh">
|
||||||
|
<div class="col-md-12">
|
||||||
|
<div class="card border-grey">
|
||||||
|
<div class="card-body">
|
||||||
|
<h4 class="card-title">{% trans 'Nutrition' %}</h4>
|
||||||
|
<div class="dropdown-menu dropdown-menu-right"
|
||||||
|
aria-labelledby="dropdownMenuLink">
|
||||||
|
<button class="dropdown-item" @click="removeStep(step)"><i
|
||||||
|
class="fa fa-trash fa-fw"></i> {% trans 'Delete Step' %}</button>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<label for="id_name"> {% trans 'Calories' %}</label>
|
||||||
|
<input class="form-control" id="id_calories" v-model="recipe.nutrition.calories">
|
||||||
|
|
||||||
|
<label for="id_name"> {% trans 'Carbohydrates' %}</label>
|
||||||
|
<input class="form-control" id="id_carbohydrates" v-model="recipe.nutrition.carbohydrates">
|
||||||
|
|
||||||
|
<label for="id_name"> {% trans 'Fats' %}</label>
|
||||||
|
<input class="form-control" id="id_fats" v-model="recipe.nutrition.fats">
|
||||||
|
<label for="id_name"> {% trans 'Proteins' %}</label>
|
||||||
|
<input class="form-control" id="id_proteins" v-model="recipe.nutrition.proteins">
|
||||||
|
<br/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
|
||||||
<draggable :list="recipe.steps" group="steps"
|
<draggable :list="recipe.steps" group="steps"
|
||||||
@@ -327,7 +356,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</draggable>
|
</draggable>
|
||||||
|
|
||||||
<div class="row" style="margin-top: 1vh; margin-bottom: 8vh">
|
<div class="row" style="margin-top: 1vh; margin-bottom: 8vh" v-if="recipe !== undefined">
|
||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<button type="button" @click="updateRecipe(true)"
|
<button type="button" @click="updateRecipe(true)"
|
||||||
class="btn btn-success shadow-none">{% trans 'Save & View' %}</button>
|
class="btn btn-success shadow-none">{% trans 'Save & View' %}</button>
|
||||||
@@ -335,6 +364,11 @@
|
|||||||
class="btn btn-info shadow-none">{% trans 'Save' %}</button>
|
class="btn btn-info shadow-none">{% trans 'Save' %}</button>
|
||||||
<button type="button" @click="addStep()"
|
<button type="button" @click="addStep()"
|
||||||
class="btn btn-primary shadow-none">{% trans 'Add Step' %}</button>
|
class="btn btn-primary shadow-none">{% trans 'Add Step' %}</button>
|
||||||
|
<button type="button" @click="addNutrition()"
|
||||||
|
class="btn btn-primary shadow-none"
|
||||||
|
v-if="recipe.nutrition === null">{% trans 'Add Nutrition' %}</button>
|
||||||
|
<button type="button" @click="removeNutrition()" v-if="recipe.nutrition !== null"
|
||||||
|
class="btn btn-warning shadow-none">{% trans 'Remove Nutrition' %}</button>
|
||||||
<a href="{% url 'view_recipe' recipe.pk %}" @click="addStep()"
|
<a href="{% url 'view_recipe' recipe.pk %}" @click="addStep()"
|
||||||
class="btn btn-secondary shadow-none">{% trans 'View Recipe' %}</a>
|
class="btn btn-secondary shadow-none">{% trans 'View Recipe' %}</a>
|
||||||
<a href="{% url 'delete_recipe' recipe.pk %}"
|
<a href="{% url 'delete_recipe' recipe.pk %}"
|
||||||
@@ -349,7 +383,7 @@
|
|||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block content_xl_right %}
|
{% block content_xl_right %}
|
||||||
<div class="sticky-top" style="top: 2vh; z-index: 100;">
|
<div class="sticky-top" style="top: 2vh; z-index: 100;" v-if="recipe !== undefined">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-11">
|
<div class="col-md-11">
|
||||||
<button type="button" @click="updateRecipe(true)"
|
<button type="button" @click="updateRecipe(true)"
|
||||||
@@ -361,6 +395,12 @@
|
|||||||
<button type="button" @click="addStep()"
|
<button type="button" @click="addStep()"
|
||||||
class="btn btn-primary btn-block shadow-none">{% trans 'Add Step' %}</button>
|
class="btn btn-primary btn-block shadow-none">{% trans 'Add Step' %}</button>
|
||||||
|
|
||||||
|
<button type="button" @click="addNutrition()"
|
||||||
|
class="btn btn-primary btn-block shadow-none"
|
||||||
|
v-if="recipe.nutrition === null">{% trans 'Add Nutrition' %}</button>
|
||||||
|
<button type="button" @click="removeNutrition()" v-if="recipe.nutrition !== null"
|
||||||
|
class="btn btn-warning btn-block shadow-none">{% trans 'Remove Nutrition' %}</button>
|
||||||
|
|
||||||
<a href="{% url 'view_recipe' recipe.pk %}"
|
<a href="{% url 'view_recipe' recipe.pk %}"
|
||||||
class="btn btn-secondary btn-block shadow-none">{% trans 'View Recipe' %}</a>
|
class="btn btn-secondary btn-block shadow-none">{% trans 'View Recipe' %}</a>
|
||||||
<a href="{% url 'delete_recipe' recipe.pk %}"
|
<a href="{% url 'delete_recipe' recipe.pk %}"
|
||||||
@@ -649,6 +689,12 @@
|
|||||||
scrollToStep: function (step_index) {
|
scrollToStep: function (step_index) {
|
||||||
document.getElementById('id_step_' + step_index).scrollIntoView({behavior: 'smooth'});
|
document.getElementById('id_step_' + step_index).scrollIntoView({behavior: 'smooth'});
|
||||||
},
|
},
|
||||||
|
addNutrition: function () {
|
||||||
|
this.recipe.nutrition = {}
|
||||||
|
},
|
||||||
|
removeNutrition: function () {
|
||||||
|
this.recipe.nutrition = null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
|
|
||||||
<script src="{% static 'js/Sortable.min.js' %}"></script>
|
<script src="{% static 'js/Sortable.min.js' %}"></script>
|
||||||
<script src="{% static 'js/vuedraggable.umd.min.js' %}"></script>
|
<script src="{% static 'js/vuedraggable.umd.min.js' %}"></script>
|
||||||
|
<script src="{% static 'js/vue-cookies.js' %}"></script>
|
||||||
|
|
||||||
<script src="{% static 'js/js.cookie.min.js' %}"></script>
|
<script src="{% static 'js/js.cookie.min.js' %}"></script>
|
||||||
|
|
||||||
@@ -24,14 +25,15 @@
|
|||||||
<div class="col-md-4 offset-md-4">
|
<div class="col-md-4 offset-md-4">
|
||||||
<div class="input-group" style="margin-top: 8px; margin-bottom: 8px">
|
<div class="input-group" style="margin-top: 8px; margin-bottom: 8px">
|
||||||
<div class="input-group-prepend">
|
<div class="input-group-prepend">
|
||||||
<button class="btn btn-outline-secondary shadow-none" @click="changeWeek(-1)">
|
<button class="btn btn-outline-secondary shadow-none"
|
||||||
|
@click="changeStartDate(number_of_days * -1)">
|
||||||
<i class="fas fa-arrow-left"></i>
|
<i class="fas fa-arrow-left"></i>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<input name="week" id="id_week" class="form-control" type="week" v-model="week"
|
<input name="date" id="id_date" class="form-control" type="date" v-model="start_date"
|
||||||
@change="updatePlan()">
|
@change="updatePlan()">
|
||||||
<div class="input-group-append">
|
<div class="input-group-append">
|
||||||
<button class="btn btn-outline-secondary shadow-none" @click="changeWeek(1)">
|
<button class="btn btn-outline-secondary shadow-none" @click="changeStartDate(number_of_days)">
|
||||||
<i class="fas fa-arrow-right"></i>
|
<i class="fas fa-arrow-right"></i>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -41,10 +43,10 @@
|
|||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-12">
|
<div class="col-md-12">
|
||||||
<table class="table table-sm table-striped table-responsive-sm">
|
<table class="table table-sm table-striped table-responsive-sm" style=" table-layout:fixed;">
|
||||||
<thead class="thead-dark">
|
<thead class="thead-dark">
|
||||||
<tr>
|
<tr>
|
||||||
<th v-for="d in days" style="width: 14.2%; text-align: center">[[d]]<br/>[[formatDateDay(d)]].
|
<th v-for="d in dates" style="width: 14.2%; text-align: center">[[formatDateDayname(d)]]<br/>[[formatDateDay(d)]].
|
||||||
<button class="btn btn-sm btn-outline-secondary shadow-none" @click="addDayToShopping(d)"><i
|
<button class="btn btn-sm btn-outline-secondary shadow-none" @click="addDayToShopping(d)"><i
|
||||||
class="fas fa-cart-plus fa-sm"></i></button>
|
class="fas fa-cart-plus fa-sm"></i></button>
|
||||||
</th>
|
</th>
|
||||||
@@ -52,7 +54,7 @@
|
|||||||
</thead>
|
</thead>
|
||||||
<tbody v-for="t in meal_types">
|
<tbody v-for="t in meal_types">
|
||||||
<tr v-if="meal_plan[t.name] !== undefined">
|
<tr v-if="meal_plan[t.name] !== undefined">
|
||||||
<td colspan="7" style="text-align: center">
|
<td :colspan="number_of_days" style="text-align: center">
|
||||||
[[ meal_plan[t.name].name]]
|
[[ meal_plan[t.name].name]]
|
||||||
<template
|
<template
|
||||||
v-if="t.created_by !== {{ request.user.pk }} && user_names[t.created_by] !== undefined">
|
v-if="t.created_by !== {{ request.user.pk }} && user_names[t.created_by] !== undefined">
|
||||||
@@ -66,18 +68,21 @@
|
|||||||
@change="dragChanged(d.date, t, $event)"
|
@change="dragChanged(d.date, t, $event)"
|
||||||
:empty-insert-threshold="10" handle=".handle">
|
:empty-insert-threshold="10" handle=".handle">
|
||||||
<div class="" v-for="(element, index) in d.items" :key="element.id">
|
<div class="" v-for="(element, index) in d.items" :key="element.id">
|
||||||
|
<!-- small layout with handle -->
|
||||||
<div class="d-block d-md-none">
|
<div class="d-block d-md-none">
|
||||||
<div class="col-">
|
<div class="col-">
|
||||||
<i class="fas fa-arrows-alt handle input-group-text"
|
<i class="fas fa-arrows-alt handle input-group-text"
|
||||||
style="width: 100%"></i>
|
style="width: 100%"></i>
|
||||||
</div>
|
</div>
|
||||||
<div class="list-group-item">
|
<div class="list-group-item" style="word-wrap: break-word;">
|
||||||
<a href="#" @click="plan_detail = element" data-toggle="modal"
|
<a href="#" @click="plan_detail = element" data-toggle="modal"
|
||||||
data-target="#id_plan_detail_modal">[[ planElementName(element)]]</a>
|
data-target="#id_plan_detail_modal">[[ planElementName(element)]]</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="list-group-item handle d-md-block d-none">
|
<!-- big layout -->
|
||||||
<div class="col-md-12">
|
<div class="list-group-item handle d-md-block d-none"
|
||||||
|
style="word-wrap: break-word; padding: 2;margin-bottom: 4">
|
||||||
|
<div class="col-md-12" style="padding: 0">
|
||||||
<a href="#" @click="plan_detail = element" data-toggle="modal"
|
<a href="#" @click="plan_detail = element" data-toggle="modal"
|
||||||
data-target="#id_plan_detail_modal">[[ planElementName(element)]]</a>
|
data-target="#id_plan_detail_modal">[[ planElementName(element)]]</a>
|
||||||
</div>
|
</div>
|
||||||
@@ -107,7 +112,8 @@
|
|||||||
<input type="text" class="form-control" v-model="recipe_query" @keyup="getRecipes"
|
<input type="text" class="form-control" v-model="recipe_query" @keyup="getRecipes"
|
||||||
placeholder="{% trans 'Search Recipe' %}">
|
placeholder="{% trans 'Search Recipe' %}">
|
||||||
<div class="input-group-append">
|
<div class="input-group-append">
|
||||||
<button class="btn btn-outline-secondary" type="button" @click="getRandomRecipes">
|
<button class="btn btn-outline-secondary" type="button"
|
||||||
|
@click="getRandomRecipes">
|
||||||
<i class="fas fa-dice"></i>
|
<i class="fas fa-dice"></i>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -180,6 +186,29 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col">
|
||||||
|
<label>
|
||||||
|
{% trans 'Number of Days' %}
|
||||||
|
<input class="form-control" type="number" v-model="number_of_days"
|
||||||
|
@change="updatePlan(); $cookies.set('number_of_days',number_of_days)">
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col">
|
||||||
|
<label>
|
||||||
|
{% trans 'Weekday offset' %}
|
||||||
|
<input class="form-control" type="number" v-model="start_offset"
|
||||||
|
@change="updatePlan(); $cookies.set('start_offset',start_offset)">
|
||||||
|
<small class="text-muted">{% trans 'Number of days starting from the first day of the week to offset the default view.' %}</small>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<a href="#" data-toggle="modal"
|
<a href="#" data-toggle="modal"
|
||||||
data-target="#id_plan_types_modal">{% trans 'Edit plan types' %}</a> <br/>
|
data-target="#id_plan_types_modal">{% trans 'Edit plan types' %}</a> <br/>
|
||||||
<a href="#" data-toggle="modal"
|
<a href="#" data-toggle="modal"
|
||||||
@@ -345,8 +374,10 @@
|
|||||||
delimiters: ['[[', ']]'],
|
delimiters: ['[[', ']]'],
|
||||||
el: '#app',
|
el: '#app',
|
||||||
data: {
|
data: {
|
||||||
week: moment().format('YYYY-[W]WW'),
|
start_date: undefined,
|
||||||
days: moment.weekdays(true),
|
start_offset: 0,
|
||||||
|
dates: [],
|
||||||
|
number_of_days: $cookies.isKey('number_of_days') ? $cookies.get('number_of_days') : 7,
|
||||||
plan_entries: [],
|
plan_entries: [],
|
||||||
meal_types: [],
|
meal_types: [],
|
||||||
meal_types_edit: [],
|
meal_types_edit: [],
|
||||||
@@ -374,6 +405,9 @@
|
|||||||
this.$set(this.user_names, {{ request.user.pk }}, '{{ request.user.get_user_name }}')
|
this.$set(this.user_names, {{ request.user.pk }}, '{{ request.user.get_user_name }}')
|
||||||
this.user_id_update = Array.from(this.default_shared_users)
|
this.user_id_update = Array.from(this.default_shared_users)
|
||||||
|
|
||||||
|
this.start_offset = $cookies.isKey('start_offset') ? $cookies.get('start_offset') : 0;
|
||||||
|
this.start_date = moment().weekday(0).add(this.start_offset, 'days').format('YYYY-MM-DD')
|
||||||
|
|
||||||
this.updatePlan();
|
this.updatePlan();
|
||||||
this.getRecipes();
|
this.getRecipes();
|
||||||
},
|
},
|
||||||
@@ -388,6 +422,11 @@
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
updatePlan: function () {
|
updatePlan: function () {
|
||||||
|
this.dates = [];
|
||||||
|
for (var i = 0; i <= (this.number_of_days - 1); i++) {
|
||||||
|
this.dates.push(moment(this.start_date).add(i, 'days'));
|
||||||
|
}
|
||||||
|
|
||||||
let planEntryPromise = this.getPlanEntries();
|
let planEntryPromise = this.getPlanEntries();
|
||||||
let planTypePromise = this.getPlanTypes();
|
let planTypePromise = this.getPlanTypes();
|
||||||
|
|
||||||
@@ -396,7 +435,7 @@
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
getPlanEntries: function () {
|
getPlanEntries: function () {
|
||||||
return this.$http.get("{% url 'api:mealplan-list' %}?html_week=" + this.week).then((response) => {
|
return this.$http.get("{% url 'api:mealplan-list' %}?from_date=" + this.dates[0].format('YYYY-MM-DD') + "&to_date=" + this.dates[this.dates.length - 1].format('YYYY-MM-DD')).then((response) => {
|
||||||
this.plan_entries = response.data;
|
this.plan_entries = response.data;
|
||||||
}).catch((err) => {
|
}).catch((err) => {
|
||||||
console.log("getPlanEntries error: ", err);
|
console.log("getPlanEntries error: ", err);
|
||||||
@@ -431,11 +470,10 @@
|
|||||||
meal_type: t.id,
|
meal_type: t.id,
|
||||||
days: {}
|
days: {}
|
||||||
})
|
})
|
||||||
for (let d of this.days) {
|
for (let d of this.dates) {
|
||||||
let date = moment(this.week).weekday(this.days.indexOf(d)).format('YYYY-MM-DD')
|
this.$set(this.meal_plan[t.name].days, d.format('YYYY-MM-DD'), {
|
||||||
this.$set(this.meal_plan[t.name].days, date, {
|
name: this.formatDateDayname(d),
|
||||||
name: d,
|
date: d.format('YYYY-MM-DD'),
|
||||||
date: date,
|
|
||||||
items: []
|
items: []
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -632,11 +670,14 @@
|
|||||||
formatLocalDate: function (date) {
|
formatLocalDate: function (date) {
|
||||||
return moment(date).format('LL')
|
return moment(date).format('LL')
|
||||||
},
|
},
|
||||||
formatDateDay: function (day) {
|
formatDateDay: function (date) {
|
||||||
return moment(this.week).weekday(this.days.indexOf(day)).format('D')
|
return moment(date).format('D')
|
||||||
},
|
},
|
||||||
changeWeek: function (change) {
|
formatDateDayname: function (date) {
|
||||||
this.week = moment(this.week).add(change, 'w').format('YYYY-[W]WW')
|
return moment(date).format('dddd')
|
||||||
|
},
|
||||||
|
changeStartDate: function (change) {
|
||||||
|
this.start_date = moment(this.start_date).add(change, 'days').format('YYYY-MM-DD')
|
||||||
this.updatePlan();
|
this.updatePlan();
|
||||||
},
|
},
|
||||||
getShoppingUrl: function () {
|
getShoppingUrl: function () {
|
||||||
@@ -653,13 +694,14 @@
|
|||||||
return url
|
return url
|
||||||
},
|
},
|
||||||
getIcalUrl: function () {
|
getIcalUrl: function () {
|
||||||
return "{% url 'api_get_plan_ical' 12345 %}".replace(/12345/, this.week);
|
if (this.dates.length === 0) {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return "{% url 'api_get_plan_ical' 12345 6789 %}".replace(/12345/, this.dates[0].format('YYYY-MM-DD')).replace(/6789/, this.dates[this.dates.length - 1].format('YYYY-MM-DD'));
|
||||||
},
|
},
|
||||||
addDayToShopping: function (day) {
|
addDayToShopping: function (date) {
|
||||||
let date = moment(this.week).weekday(this.days.indexOf(day)).format('YYYY-MM-DD')
|
|
||||||
|
|
||||||
for (let t of this.meal_types) {
|
for (let t of this.meal_types) {
|
||||||
for (let i of this.meal_plan[t.name].days[date].items) {
|
for (let i of this.meal_plan[t.name].days[date.format('YYYY-MM-DD')].items) {
|
||||||
if (!this.shopping_list.includes(i)) {
|
if (!this.shopping_list.includes(i)) {
|
||||||
this.shopping_list.push(i)
|
this.shopping_list.push(i)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,6 +12,8 @@
|
|||||||
{% include 'include/vue_base.html' %}
|
{% include 'include/vue_base.html' %}
|
||||||
<script src="{% static 'js/moment-with-locales.min.js' %}"></script>
|
<script src="{% static 'js/moment-with-locales.min.js' %}"></script>
|
||||||
|
|
||||||
|
<script src="{% static 'js/frac.js' %}"></script>
|
||||||
|
|
||||||
<link rel="stylesheet" href="{% static 'css/pretty-checkbox.min.css' %}">
|
<link rel="stylesheet" href="{% static 'css/pretty-checkbox.min.css' %}">
|
||||||
<link rel="stylesheet" href="{% static 'custom/css/markdown_blockquote.css' %}">
|
<link rel="stylesheet" href="{% static 'custom/css/markdown_blockquote.css' %}">
|
||||||
|
|
||||||
@@ -77,13 +79,13 @@
|
|||||||
|
|
||||||
{% if recipe.working_time and recipe.working_time != 0 %}
|
{% if recipe.working_time and recipe.working_time != 0 %}
|
||||||
<span class="badge badge-secondary"><i
|
<span class="badge badge-secondary"><i
|
||||||
class="fas fa-user-clock"></i> {% trans 'Preparation time ca.' %} {{ recipe.working_time }} min </span>
|
class="fas fa-user-clock"></i> {% trans 'Preparation time ~' %} {{ recipe.working_time }} min </span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% if recipe.waiting_time and recipe.waiting_time != 0 %}
|
{% if recipe.waiting_time and recipe.waiting_time != 0 %}
|
||||||
<span
|
<span
|
||||||
class="badge badge-secondary"><i
|
class="badge badge-secondary"><i
|
||||||
class="far fa-clock"></i> {% trans 'Waiting time ca.' %} {{ recipe.waiting_time }} min </span>
|
class="far fa-clock"></i> {% trans 'Waiting time ~' %} {{ recipe.waiting_time }} min </span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% recipe_last recipe request.user as last_cooked %}
|
{% recipe_last recipe request.user as last_cooked %}
|
||||||
{% if last_cooked %}
|
{% if last_cooked %}
|
||||||
@@ -150,7 +152,7 @@
|
|||||||
<span>⁣</span>
|
<span>⁣</span>
|
||||||
</template>
|
</template>
|
||||||
<template v-if="!i.no_amount">
|
<template v-if="!i.no_amount">
|
||||||
<span>[[roundDecimals(i.amount * ingredient_factor)]]</span>
|
<span v-html="calculateAmount(i.amount)"></span>
|
||||||
{# Allow for amounts without units, such as "2 eggs" #}
|
{# Allow for amounts without units, such as "2 eggs" #}
|
||||||
<template v-if="i.unit">
|
<template v-if="i.unit">
|
||||||
[[i.unit.name]]
|
[[i.unit.name]]
|
||||||
@@ -208,6 +210,60 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{% if recipe.nutrition %}
|
||||||
|
<div class="row mt-5">
|
||||||
|
<div class="col-md-6 order-md-1 col-sm-12 order-sm-2 col-12 order-2">
|
||||||
|
<div class="card border-primary">
|
||||||
|
<div class="card-body">
|
||||||
|
<h4 class="card-title">{% trans 'Nutrition' %}</h4>
|
||||||
|
<table class="table table-sm">
|
||||||
|
<tr>
|
||||||
|
<td style="padding-top: 8px!important; ">
|
||||||
|
<b>{% trans 'Calories' %}</b>
|
||||||
|
</td>
|
||||||
|
<td style="text-align: right">{{ recipe.nutrition.calories|floatformat:2 }}</td>
|
||||||
|
<td>kcal</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<tr>
|
||||||
|
<td style="padding-top: 8px!important; ">
|
||||||
|
<b>{% trans 'Carbohydrates' %}</b>
|
||||||
|
</td>
|
||||||
|
<td style="text-align: right">{{ recipe.nutrition.carbohydrates|floatformat:2 }}</td>
|
||||||
|
<td>g</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<tr>
|
||||||
|
<td style="padding-top: 8px!important; ">
|
||||||
|
<b>{% trans 'Fats' %}</b>
|
||||||
|
</td>
|
||||||
|
<td style="text-align: right">{{ recipe.nutrition.fats|floatformat:2 }}</td>
|
||||||
|
<td>g</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<tr>
|
||||||
|
<td style="padding-top: 8px!important; ">
|
||||||
|
<b>{% trans 'Proteins' %}</b>
|
||||||
|
</td>
|
||||||
|
<td style="text-align: right">{{ recipe.nutrition.proteins|floatformat:2 }}</td>
|
||||||
|
<td>g</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td></td>
|
||||||
|
<td></td>
|
||||||
|
<td></td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
{% if recipe.nutrition.source %}
|
||||||
|
Source: {{ recipe.nutrition.source }}
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
|
||||||
<div v-if="recipe !== undefined && recipe.steps.length > 0">
|
<div v-if="recipe !== undefined && recipe.steps.length > 0">
|
||||||
<hr>
|
<hr>
|
||||||
<h3>{% trans 'Instructions' %}</h3>
|
<h3>{% trans 'Instructions' %}</h3>
|
||||||
@@ -271,7 +327,7 @@
|
|||||||
<span>⁣</span>
|
<span>⁣</span>
|
||||||
</template>
|
</template>
|
||||||
<template v-if="!i.no_amount">
|
<template v-if="!i.no_amount">
|
||||||
<span>[[roundDecimals(i.amount * ingredient_factor)]]</span>
|
<span v-html="calculateAmount(i.amount)"></span>
|
||||||
{# Allow for amounts without units, such as "2 eggs" #}
|
{# Allow for amounts without units, such as "2 eggs" #}
|
||||||
<template v-if="i.unit">
|
<template v-if="i.unit">
|
||||||
[[i.unit.name]]
|
[[i.unit.name]]
|
||||||
@@ -478,7 +534,7 @@
|
|||||||
this.$http.get("{% url 'api:recipe-detail' recipe.pk %}" {% if share %}
|
this.$http.get("{% url 'api:recipe-detail' recipe.pk %}" {% if share %}
|
||||||
+ "?share={{ share }}"{% endif %}).then((response) => {
|
+ "?share={{ share }}"{% endif %}).then((response) => {
|
||||||
this.recipe = response.data;
|
this.recipe = response.data;
|
||||||
this.loading = false
|
this.loading = false;
|
||||||
|
|
||||||
for (let step of this.recipe.steps) {
|
for (let step of this.recipe.steps) {
|
||||||
if (step.ingredients.length > 0) {
|
if (step.ingredients.length > 0) {
|
||||||
@@ -487,25 +543,25 @@
|
|||||||
if (step.time !== 0) {
|
if (step.time !== 0) {
|
||||||
this.has_times = true
|
this.has_times = true
|
||||||
}
|
}
|
||||||
this.$set(step, 'time_finished', undefined)
|
this.$set(step, 'time_finished', undefined);
|
||||||
for (let i of step.ingredients) {
|
for (let i of step.ingredients) {
|
||||||
this.$set(i, 'checked', false)
|
this.$set(i, 'checked', false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}).catch((err) => {
|
}).catch((err) => {
|
||||||
this.error = err.data
|
this.error = err.data;
|
||||||
this.loading = false
|
this.loading = false;
|
||||||
console.log(err)
|
console.log(err)
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
roundDecimals: function (num) {
|
roundDecimals: function (num) {
|
||||||
let decimals = {% if request.user.userpreference.ingredient_decimals %}
|
let decimals = {% if request.user.userpreference.ingredient_decimals %}
|
||||||
{{ request.user.userpreference.ingredient_decimals }} {% else %} 2 {% endif %}
|
{{ request.user.userpreference.ingredient_decimals }} {% else %} 2; {% endif %}
|
||||||
return +(Math.round(num + `e+${decimals}`) + `e-${decimals}`);
|
return +(Math.round(num + `e+${decimals}`) + `e-${decimals}`);
|
||||||
},
|
},
|
||||||
updateTimes: function (step) {
|
updateTimes: function (step) {
|
||||||
let time_diff_first = 0
|
let time_diff_first = 0;
|
||||||
for (let s of this.recipe.steps) {
|
for (let s of this.recipe.steps) {
|
||||||
if (this.recipe.steps.indexOf(s) < this.recipe.steps.indexOf(step)) {
|
if (this.recipe.steps.indexOf(s) < this.recipe.steps.indexOf(step)) {
|
||||||
time_diff_first += s.time
|
time_diff_first += s.time
|
||||||
@@ -514,7 +570,7 @@
|
|||||||
|
|
||||||
this.recipe.steps[0].time_finished = moment(step.time_finished).subtract(time_diff_first, 'minutes').format(moment.HTML5_FMT.DATETIME_LOCAL);
|
this.recipe.steps[0].time_finished = moment(step.time_finished).subtract(time_diff_first, 'minutes').format(moment.HTML5_FMT.DATETIME_LOCAL);
|
||||||
|
|
||||||
let time_diff = 0
|
let time_diff = 0;
|
||||||
for (let s of this.recipe.steps) {
|
for (let s of this.recipe.steps) {
|
||||||
s.time_finished = moment(this.recipe.steps[0].time_finished).add(time_diff, 'minutes').format(moment.HTML5_FMT.DATETIME_LOCAL);
|
s.time_finished = moment(this.recipe.steps[0].time_finished).add(time_diff, 'minutes').format(moment.HTML5_FMT.DATETIME_LOCAL);
|
||||||
time_diff += s.time
|
time_diff += s.time
|
||||||
@@ -523,8 +579,28 @@
|
|||||||
},
|
},
|
||||||
getShoppingUrl: function () {
|
getShoppingUrl: function () {
|
||||||
return `{% url 'view_shopping' %}?r=[${this.recipe.id},${this.ingredient_factor}]`
|
return `{% url 'view_shopping' %}?r=[${this.recipe.id},${this.ingredient_factor}]`
|
||||||
|
},
|
||||||
|
calculateAmount: function (amount) {
|
||||||
|
{% if request.user.userpreference.use_fractions %}
|
||||||
|
let return_string = ''
|
||||||
|
let fraction = frac.cont((amount * this.ingredient_factor), 9, true)
|
||||||
|
|
||||||
|
if (fraction[0] > 0) {
|
||||||
|
return_string += fraction[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (fraction[1] > 0) {
|
||||||
|
return_string += ` <sup>${(fraction[1])}</sup>⁄<sub>${(fraction[2])}</sub>`
|
||||||
|
}
|
||||||
|
|
||||||
|
return return_string
|
||||||
|
{% else %}
|
||||||
|
return this.roundDecimals(amount * this.ingredient_factor)
|
||||||
|
{% endif %}
|
||||||
|
},
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
@@ -73,7 +73,7 @@ urlpatterns = [
|
|||||||
path('api/get_recipe_file/<int:recipe_id>/', api.get_recipe_file, name='api_get_recipe_file'),
|
path('api/get_recipe_file/<int:recipe_id>/', api.get_recipe_file, name='api_get_recipe_file'),
|
||||||
path('api/sync_all/', api.sync_all, name='api_sync'),
|
path('api/sync_all/', api.sync_all, name='api_sync'),
|
||||||
path('api/log_cooking/<int:recipe_id>/', api.log_cooking, name='api_log_cooking'),
|
path('api/log_cooking/<int:recipe_id>/', api.log_cooking, name='api_log_cooking'),
|
||||||
path('api/plan-ical/<slug:html_week>/', api.get_plan_ical, name='api_get_plan_ical'),
|
path('api/plan-ical/<slug:from_date>/<slug:to_date>/', api.get_plan_ical, name='api_get_plan_ical'),
|
||||||
path('api/recipe-from-url/', api.recipe_from_url, name='api_recipe_from_url'),
|
path('api/recipe-from-url/', api.recipe_from_url, name='api_recipe_from_url'),
|
||||||
path('api/backup/', api.get_backup, name='api_backup'),
|
path('api/backup/', api.get_backup, name='api_backup'),
|
||||||
|
|
||||||
|
|||||||
@@ -104,7 +104,11 @@ class StandardFilterMixin(ViewSetMixin):
|
|||||||
queryset = queryset.filter(name__icontains=query)
|
queryset = queryset.filter(name__icontains=query)
|
||||||
|
|
||||||
limit = self.request.query_params.get('limit', None)
|
limit = self.request.query_params.get('limit', None)
|
||||||
|
random = self.request.query_params.get('random', False)
|
||||||
if limit is not None:
|
if limit is not None:
|
||||||
|
if random:
|
||||||
|
queryset = queryset.random(int(limit))
|
||||||
|
else:
|
||||||
queryset = queryset[:int(limit)]
|
queryset = queryset[:int(limit)]
|
||||||
return queryset
|
return queryset
|
||||||
|
|
||||||
@@ -150,7 +154,8 @@ class MealPlanViewSet(viewsets.ModelViewSet):
|
|||||||
list:
|
list:
|
||||||
optional parameters
|
optional parameters
|
||||||
|
|
||||||
- **html_week**: filter for a calendar week (format 2020-W24 as html input type week)
|
- **from_date**: filter from (inclusive) a certain date onward
|
||||||
|
- **to_date**: filter upward to (inclusive) certain date
|
||||||
|
|
||||||
"""
|
"""
|
||||||
queryset = MealPlan.objects.all()
|
queryset = MealPlan.objects.all()
|
||||||
@@ -159,10 +164,14 @@ class MealPlanViewSet(viewsets.ModelViewSet):
|
|||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
queryset = MealPlan.objects.filter(Q(created_by=self.request.user) | Q(shared=self.request.user)).distinct().all()
|
queryset = MealPlan.objects.filter(Q(created_by=self.request.user) | Q(shared=self.request.user)).distinct().all()
|
||||||
week = self.request.query_params.get('html_week', None)
|
|
||||||
if week is not None:
|
from_date = self.request.query_params.get('from_date', None)
|
||||||
y, w = week.replace('-W', ' ').split()
|
if from_date is not None:
|
||||||
queryset = queryset.filter(date__week=w, date__year=y)
|
queryset = queryset.filter(date__gte=from_date)
|
||||||
|
|
||||||
|
to_date = self.request.query_params.get('to_date', None)
|
||||||
|
if to_date is not None:
|
||||||
|
queryset = queryset.filter(date__lte=to_date)
|
||||||
return queryset
|
return queryset
|
||||||
|
|
||||||
|
|
||||||
@@ -205,16 +214,12 @@ class RecipeViewSet(viewsets.ModelViewSet, StandardFilterMixin):
|
|||||||
permission_classes = [CustomIsShare | CustomIsGuest] # TODO split read and write permission for meal plan guest
|
permission_classes = [CustomIsShare | CustomIsGuest] # TODO split read and write permission for meal plan guest
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
queryset = super().get_queryset()
|
|
||||||
|
|
||||||
internal = self.request.query_params.get('internal', None)
|
internal = self.request.query_params.get('internal', None)
|
||||||
if internal:
|
if internal:
|
||||||
queryset = queryset.filter(internal=True)
|
self.queryset = self.queryset.filter(internal=True)
|
||||||
random = self.request.query_params.get('random', False)
|
|
||||||
if random:
|
|
||||||
queryset = queryset.random(5)
|
|
||||||
|
|
||||||
return queryset
|
return super().get_queryset()
|
||||||
|
|
||||||
# TODO write extensive tests for permissions
|
# TODO write extensive tests for permissions
|
||||||
|
|
||||||
@@ -368,11 +373,14 @@ def log_cooking(request, recipe_id):
|
|||||||
|
|
||||||
|
|
||||||
@group_required('user')
|
@group_required('user')
|
||||||
def get_plan_ical(request, html_week):
|
def get_plan_ical(request, from_date, to_date):
|
||||||
queryset = MealPlan.objects.filter(Q(created_by=request.user) | Q(shared=request.user)).distinct().all()
|
queryset = MealPlan.objects.filter(Q(created_by=request.user) | Q(shared=request.user)).distinct().all()
|
||||||
|
|
||||||
y, w = html_week.replace('-W', ' ').split()
|
if from_date is not None:
|
||||||
queryset = queryset.filter(date__week=w, date__year=y)
|
queryset = queryset.filter(date__gte=from_date)
|
||||||
|
|
||||||
|
if to_date is not None:
|
||||||
|
queryset = queryset.filter(date__lte=to_date)
|
||||||
|
|
||||||
cal = Calendar()
|
cal = Calendar()
|
||||||
|
|
||||||
@@ -386,7 +394,7 @@ def get_plan_ical(request, html_week):
|
|||||||
cal.add_component(event)
|
cal.add_component(event)
|
||||||
|
|
||||||
response = FileResponse(io.BytesIO(cal.to_ical()))
|
response = FileResponse(io.BytesIO(cal.to_ical()))
|
||||||
response["Content-Disposition"] = f'attachment; filename=meal_plan_{html_week}.ics'
|
response["Content-Disposition"] = f'attachment; filename=meal_plan_{from_date}-{to_date}.ics'
|
||||||
|
|
||||||
return response
|
return response
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ from datetime import datetime
|
|||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
from PIL import Image
|
from PIL import Image, UnidentifiedImageError
|
||||||
from django.contrib import messages
|
from django.contrib import messages
|
||||||
from django.core.files import File
|
from django.core.files import File
|
||||||
from django.db.transaction import atomic
|
from django.db.transaction import atomic
|
||||||
@@ -144,6 +144,7 @@ def import_url(request):
|
|||||||
print(ingredient)
|
print(ingredient)
|
||||||
|
|
||||||
if data['image'] != '':
|
if data['image'] != '':
|
||||||
|
try:
|
||||||
response = requests.get(data['image'])
|
response = requests.get(data['image'])
|
||||||
img = Image.open(BytesIO(response.content))
|
img = Image.open(BytesIO(response.content))
|
||||||
|
|
||||||
@@ -157,6 +158,8 @@ def import_url(request):
|
|||||||
img.save(im_io, 'PNG', quality=70)
|
img.save(im_io, 'PNG', quality=70)
|
||||||
recipe.image = File(im_io, name=f'{uuid.uuid4()}_{recipe.pk}.png')
|
recipe.image = File(im_io, name=f'{uuid.uuid4()}_{recipe.pk}.png')
|
||||||
recipe.save()
|
recipe.save()
|
||||||
|
except UnidentifiedImageError:
|
||||||
|
pass
|
||||||
|
|
||||||
return HttpResponse(reverse('view_recipe', args=[recipe.pk]))
|
return HttpResponse(reverse('view_recipe', args=[recipe.pk]))
|
||||||
|
|
||||||
|
|||||||
@@ -202,6 +202,7 @@ def user_settings(request):
|
|||||||
up.plan_share.set(form.cleaned_data['plan_share'])
|
up.plan_share.set(form.cleaned_data['plan_share'])
|
||||||
up.ingredient_decimals = form.cleaned_data['ingredient_decimals']
|
up.ingredient_decimals = form.cleaned_data['ingredient_decimals']
|
||||||
up.comments = form.cleaned_data['comments']
|
up.comments = form.cleaned_data['comments']
|
||||||
|
up.use_fractions = form.cleaned_data['use_fractions']
|
||||||
|
|
||||||
up.shopping_auto_sync = form.cleaned_data['shopping_auto_sync']
|
up.shopping_auto_sync = form.cleaned_data['shopping_auto_sync']
|
||||||
if up.shopping_auto_sync < settings.SHOPPING_MIN_AUTOSYNC_INTERVAL:
|
if up.shopping_auto_sync < settings.SHOPPING_MIN_AUTOSYNC_INTERVAL:
|
||||||
@@ -242,7 +243,7 @@ def history(request):
|
|||||||
|
|
||||||
@group_required('admin')
|
@group_required('admin')
|
||||||
def system(request):
|
def system(request):
|
||||||
postgres = False if settings.DATABASES['default']['ENGINE'] == 'django.db.backends.postgresql_psycopg2' else True
|
postgres = False if (settings.DATABASES['default']['ENGINE'] == 'django.db.backends.postgresql_psycopg2' or settings.DATABASES['default']['ENGINE'] == 'django.db.backends.postgresql') else True
|
||||||
|
|
||||||
secret_key = False if os.getenv('SECRET_KEY') else True
|
secret_key = False if os.getenv('SECRET_KEY') else True
|
||||||
|
|
||||||
|
|||||||
@@ -24,7 +24,8 @@ Basic guide to setup vabenee1111/recipes docker container on Synology NAS
|
|||||||
- Open https://github.com/vabene1111/recipes/tree/develop/docs/docker/plain/nginx/conf.d
|
- Open https://github.com/vabene1111/recipes/tree/develop/docs/docker/plain/nginx/conf.d
|
||||||
- Download Recipes.conf to your conf.d folder
|
- Download Recipes.conf to your conf.d folder
|
||||||
- Open https://github.com/vabene1111/recipes/blob/develop/.env.template
|
- Open https://github.com/vabene1111/recipes/blob/develop/.env.template
|
||||||
- Copy the text and save it as 'env' to your recipes folder (no filename extension!)
|
- Copy the text and save it as '.env' to your recipes folder (no filename extension!)
|
||||||
|
- Add a POSTGRES_PASSWORD
|
||||||
- Once done, it should look like this:
|
- Once done, it should look like this:
|
||||||
|
|
||||||

|

|
||||||
@@ -50,3 +51,24 @@ Creating recipes_web_recipes_1 ... done
|
|||||||
```
|
```
|
||||||
- Browse to 192.168.1.1:2000 or whatever your IP and port are
|
- Browse to 192.168.1.1:2000 or whatever your IP and port are
|
||||||
- While the containers are starting and doing whatever they need to do, you might still get HTTP errors e.g. 500 or 502. Just be patient and try again in a moment
|
- While the containers are starting and doing whatever they need to do, you might still get HTTP errors e.g. 500 or 502. Just be patient and try again in a moment
|
||||||
|
|
||||||
|
5. Additional SSL Setup
|
||||||
|
- create foler `ssl` inside `nginx` folder
|
||||||
|
- download your ssl certificate from `security` tab in dsm `control panel`
|
||||||
|
- or create a task in `task manager` because Synology will update the certificate every few months
|
||||||
|
- set task to repeat every day
|
||||||
|
- in the script write:
|
||||||
|
```
|
||||||
|
SRC="/usr/syno/etc/certificate/system/default"
|
||||||
|
DEST="/volume1/docker/recipes/nginx/ssl/"
|
||||||
|
if [ ! -f "$DEST/fullchain.pem" ] || [ "$SRC/fullchain.pem" -nt "$DEST/fullchain.pem" ]; then
|
||||||
|
cp "$SRC/fullchain.pem" "$DEST/"
|
||||||
|
cp "$SRC/privkey.pem" "$DEST/"
|
||||||
|
chown root:root "$DEST/fullchain.pem" "$DEST/privkey.pem"
|
||||||
|
chmod 600 "$DEST/fullchain.pem" "$DEST/privkey.pem"
|
||||||
|
/usr/syno/bin/synowebapi --exec api=SYNO.Docker.Container version=1 method=restart name=recipes_nginx_recipes_1
|
||||||
|
fi
|
||||||
|
```
|
||||||
|
- change `docker-compose.yml`
|
||||||
|
add `- ./nginx/ssl:/etc/nginx/certs` to the `volumes` of `nginx_recipes`
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
@@ -8,7 +8,7 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: PACKAGE VERSION\n"
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2020-09-29 19:44+0200\n"
|
"POT-Creation-Date: 2020-12-15 21:39+0100\n"
|
||||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||||
@@ -18,18 +18,18 @@ msgstr ""
|
|||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||||
|
|
||||||
#: .\recipes\settings.py:174
|
#: .\recipes\settings.py:175
|
||||||
msgid "English"
|
msgid "English"
|
||||||
msgstr "Englisch"
|
msgstr "Englisch"
|
||||||
|
|
||||||
#: .\recipes\settings.py:175
|
#: .\recipes\settings.py:176
|
||||||
msgid "German"
|
msgid "German"
|
||||||
msgstr "Deutsch"
|
msgstr "Deutsch"
|
||||||
|
|
||||||
#: .\recipes\settings.py:176
|
#: .\recipes\settings.py:177
|
||||||
msgid "Dutch"
|
msgid "Dutch"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: .\recipes\settings.py:177
|
#: .\recipes\settings.py:178
|
||||||
msgid "French"
|
msgid "French"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|||||||
Binary file not shown.
@@ -8,7 +8,7 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: PACKAGE VERSION\n"
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2020-09-29 19:44+0200\n"
|
"POT-Creation-Date: 2020-12-15 21:39+0100\n"
|
||||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||||
@@ -18,18 +18,18 @@ msgstr ""
|
|||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||||
|
|
||||||
#: .\recipes\settings.py:174
|
#: .\recipes\settings.py:175
|
||||||
msgid "English"
|
msgid "English"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: .\recipes\settings.py:175
|
#: .\recipes\settings.py:176
|
||||||
msgid "German"
|
msgid "German"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: .\recipes\settings.py:176
|
#: .\recipes\settings.py:177
|
||||||
msgid "Dutch"
|
msgid "Dutch"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: .\recipes\settings.py:177
|
#: .\recipes\settings.py:178
|
||||||
msgid "French"
|
msgid "French"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|||||||
Binary file not shown.
@@ -8,7 +8,7 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: PACKAGE VERSION\n"
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2020-09-29 19:44+0200\n"
|
"POT-Creation-Date: 2020-12-15 21:39+0100\n"
|
||||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||||
@@ -18,18 +18,18 @@ msgstr ""
|
|||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
|
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
|
||||||
|
|
||||||
#: .\recipes\settings.py:174
|
#: .\recipes\settings.py:175
|
||||||
msgid "English"
|
msgid "English"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: .\recipes\settings.py:175
|
#: .\recipes\settings.py:176
|
||||||
msgid "German"
|
msgid "German"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: .\recipes\settings.py:176
|
#: .\recipes\settings.py:177
|
||||||
msgid "Dutch"
|
msgid "Dutch"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: .\recipes\settings.py:177
|
#: .\recipes\settings.py:178
|
||||||
msgid "French"
|
msgid "French"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|||||||
Binary file not shown.
@@ -8,7 +8,7 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: PACKAGE VERSION\n"
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2020-09-29 19:44+0200\n"
|
"POT-Creation-Date: 2020-12-15 21:39+0100\n"
|
||||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||||
@@ -18,18 +18,18 @@ msgstr ""
|
|||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||||
|
|
||||||
#: .\recipes\settings.py:174
|
#: .\recipes\settings.py:175
|
||||||
msgid "English"
|
msgid "English"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: .\recipes\settings.py:175
|
#: .\recipes\settings.py:176
|
||||||
msgid "German"
|
msgid "German"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: .\recipes\settings.py:176
|
#: .\recipes\settings.py:177
|
||||||
msgid "Dutch"
|
msgid "Dutch"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: .\recipes\settings.py:177
|
#: .\recipes\settings.py:178
|
||||||
msgid "French"
|
msgid "French"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|||||||
Binary file not shown.
@@ -8,7 +8,7 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: PACKAGE VERSION\n"
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2020-09-29 19:44+0200\n"
|
"POT-Creation-Date: 2020-12-15 21:39+0100\n"
|
||||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||||
@@ -18,18 +18,18 @@ msgstr ""
|
|||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||||
|
|
||||||
#: .\recipes\settings.py:174
|
#: .\recipes\settings.py:175
|
||||||
msgid "English"
|
msgid "English"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: .\recipes\settings.py:175
|
#: .\recipes\settings.py:176
|
||||||
msgid "German"
|
msgid "German"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: .\recipes\settings.py:176
|
#: .\recipes\settings.py:177
|
||||||
msgid "Dutch"
|
msgid "Dutch"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: .\recipes\settings.py:177
|
#: .\recipes\settings.py:178
|
||||||
msgid "French"
|
msgid "French"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|||||||
Binary file not shown.
@@ -8,7 +8,7 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: PACKAGE VERSION\n"
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2020-09-29 19:44+0200\n"
|
"POT-Creation-Date: 2020-12-15 21:39+0100\n"
|
||||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||||
@@ -17,18 +17,18 @@ msgstr ""
|
|||||||
"Content-Type: text/plain; charset=UTF-8\n"
|
"Content-Type: text/plain; charset=UTF-8\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
|
||||||
#: .\recipes\settings.py:174
|
#: .\recipes\settings.py:175
|
||||||
msgid "English"
|
msgid "English"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: .\recipes\settings.py:175
|
#: .\recipes\settings.py:176
|
||||||
msgid "German"
|
msgid "German"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: .\recipes\settings.py:176
|
#: .\recipes\settings.py:177
|
||||||
msgid "Dutch"
|
msgid "Dutch"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: .\recipes\settings.py:177
|
#: .\recipes\settings.py:178
|
||||||
msgid "French"
|
msgid "French"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|||||||
Binary file not shown.
@@ -8,7 +8,7 @@ msgid ""
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: PACKAGE VERSION\n"
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2020-09-29 19:44+0200\n"
|
"POT-Creation-Date: 2020-12-15 21:39+0100\n"
|
||||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||||
@@ -18,18 +18,18 @@ msgstr ""
|
|||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
|
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
|
||||||
|
|
||||||
#: .\recipes\settings.py:174
|
#: .\recipes\settings.py:175
|
||||||
msgid "English"
|
msgid "English"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: .\recipes\settings.py:175
|
#: .\recipes\settings.py:176
|
||||||
msgid "German"
|
msgid "German"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: .\recipes\settings.py:176
|
#: .\recipes\settings.py:177
|
||||||
msgid "Dutch"
|
msgid "Dutch"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: .\recipes\settings.py:177
|
#: .\recipes\settings.py:178
|
||||||
msgid "French"
|
msgid "French"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ REVERSE_PROXY_AUTH = bool(int(os.getenv('REVERSE_PROXY_AUTH', False)))
|
|||||||
|
|
||||||
# default value for user preference 'comment'
|
# default value for user preference 'comment'
|
||||||
COMMENT_PREF_DEFAULT = bool(int(os.getenv('COMMENT_PREF_DEFAULT', True)))
|
COMMENT_PREF_DEFAULT = bool(int(os.getenv('COMMENT_PREF_DEFAULT', True)))
|
||||||
|
FRACTION_PREF_DEFAULT = bool(int(os.getenv('FRACTION_PREF_DEFAULT', False)))
|
||||||
|
|
||||||
# minimum interval that users can set for automatic sync of shopping lists
|
# minimum interval that users can set for automatic sync of shopping lists
|
||||||
SHOPPING_MIN_AUTOSYNC_INTERVAL = int(os.getenv('SHOPPING_MIN_AUTOSYNC_INTERVAL', 5))
|
SHOPPING_MIN_AUTOSYNC_INTERVAL = int(os.getenv('SHOPPING_MIN_AUTOSYNC_INTERVAL', 5))
|
||||||
@@ -162,7 +163,7 @@ AUTH_PASSWORD_VALIDATORS = [
|
|||||||
|
|
||||||
LANGUAGE_CODE = 'en'
|
LANGUAGE_CODE = 'en'
|
||||||
|
|
||||||
TIME_ZONE = 'Europe/Berlin'
|
TIME_ZONE = os.getenv('TIMEZONE') if os.getenv('TIMEZONE') else 'Europe/Berlin'
|
||||||
|
|
||||||
USE_I18N = True
|
USE_I18N = True
|
||||||
|
|
||||||
@@ -175,6 +176,7 @@ LANGUAGES = [
|
|||||||
('de', _('German')),
|
('de', _('German')),
|
||||||
('nl', _('Dutch')),
|
('nl', _('Dutch')),
|
||||||
('fr', _('French')),
|
('fr', _('French')),
|
||||||
|
('ca', _('Catalan')),
|
||||||
]
|
]
|
||||||
|
|
||||||
# Static files (CSS, JavaScript, Images)
|
# Static files (CSS, JavaScript, Images)
|
||||||
|
|||||||
@@ -1,29 +1,29 @@
|
|||||||
bleach==3.2.1
|
bleach==3.2.1
|
||||||
bleach-whitelist==0.0.11
|
bleach-whitelist==0.0.11
|
||||||
Django==3.1.1
|
Django==3.1.4
|
||||||
django-annoying==0.10.6
|
django-annoying==0.10.6
|
||||||
django-autocomplete-light==3.5.1
|
django-autocomplete-light==3.8.1
|
||||||
django-cleanup==4.0.0
|
django-cleanup==5.1.0
|
||||||
django-crispy-forms==1.9.1
|
django-crispy-forms==1.10.0
|
||||||
django-emoji-picker==0.0.6
|
django-emoji-picker==0.0.6
|
||||||
django-filter==2.4.0
|
django-filter==2.4.0
|
||||||
django-tables2==2.3.1
|
django-tables2==2.3.3
|
||||||
djangorestframework==3.11.0
|
djangorestframework==3.12.2
|
||||||
drf-writable-nested==0.6.1
|
drf-writable-nested==0.6.2
|
||||||
gunicorn==20.0.4
|
gunicorn==20.0.4
|
||||||
lxml==4.5.1
|
lxml==4.6.2
|
||||||
Markdown==3.2.2
|
Markdown==3.3.3
|
||||||
Pillow==7.1.2
|
Pillow==8.0.1
|
||||||
psycopg2-binary==2.8.6
|
psycopg2-binary==2.8.6
|
||||||
python-dotenv==0.15.0
|
python-dotenv==0.15.0
|
||||||
requests==2.23.0
|
requests==2.25.1
|
||||||
simplejson==3.17.2
|
simplejson==3.17.2
|
||||||
six==1.15.0
|
six==1.15.0
|
||||||
webdavclient3==3.14.4
|
webdavclient3==3.14.5
|
||||||
whitenoise==5.2.0
|
whitenoise==5.2.0
|
||||||
icalendar==4.0.6
|
icalendar==4.0.7
|
||||||
pyyaml==5.3.1
|
pyyaml==5.3.1
|
||||||
uritemplate==3.0.1
|
uritemplate==3.0.1
|
||||||
beautifulsoup4==4.9.2
|
beautifulsoup4==4.9.3
|
||||||
microdata==0.7.1
|
microdata==0.7.1
|
||||||
django-random-queryset==0.1.3
|
django-random-queryset==0.1.3
|
||||||
Reference in New Issue
Block a user