Compare commits

..

18 Commits
0.3.0 ... 0.3.2

Author SHA1 Message Date
vabene1111
b8f16b50a7 simplified viewer + fixed characters escaping 2020-02-19 20:19:08 +01:00
vabene1111
752df5a1d2 cleaned up viewer 2020-02-19 19:39:32 +01:00
vabene1111
fe6e351349 testing pdf viewerr 2020-02-19 19:12:11 +01:00
vabene1111
8cc9273268 basic viewer working 2020-02-19 19:02:47 +01:00
vabene1111
0c1763b347 pdf display working 2020-02-19 18:13:11 +01:00
vabene1111
88dc713683 WIP pdf embedding 2020-02-19 16:55:13 +01:00
vabene1111
fc1cc70870 basic pdf embedding 2020-02-19 00:08:32 +01:00
vabene1111
42e09fcae9 updated translations 2020-02-18 23:26:14 +01:00
vabene1111
4843568d10 added searching for ingredients 2020-02-18 23:16:05 +01:00
vabene1111
2e7e4b23dd highlight active tab 2020-02-18 23:03:37 +01:00
vabene1111
8192a8dc8f improved shopping list ui 2020-02-18 23:00:23 +01:00
vabene1111
c98dbd065e allow choosing output format of shopping list 2020-02-18 22:41:37 +01:00
vabene1111
43d03ed17d group ingredients in shopping list 2020-02-18 22:33:31 +01:00
vabene1111
8ba34414a1 added shoping to nav 2020-02-18 22:28:22 +01:00
vabene1111
46dffe2f63 added ingredient merging 2020-02-18 22:25:21 +01:00
vabene1111
04cbe6cb2c removed empty script tag when no default is given 2020-02-18 22:17:27 +01:00
vabene1111
8fd6dcc81c updated translations 2020-02-17 23:34:17 +01:00
vabene1111
b89e96476a fixed status badge
since GH actions currently dont triggert on fast forward merge pushs the badge is changed to show passing status of develop
2020-02-17 00:33:21 +01:00
27 changed files with 652 additions and 254 deletions

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="JavaScriptLibraryMappings">
<file url="file://$PROJECT_DIR$" libraries="{jquery-3.4.1}" />
<file url="file://$PROJECT_DIR$" libraries="{jquery-3.4.1, pdf, pdf_viewer, pretty-checkbox}" />
</component>
</project>

View File

@@ -1,4 +1,4 @@
# Recipes ![CI](https://github.com/vabene1111/recipes/workflows/Continous%20Integration/badge.svg?branch=master)
# Recipes ![Continous Integration](https://github.com/vabene1111/recipes/workflows/Continous%20Integration/badge.svg?branch=develop)
Recipes is a Django application to manage, tag and search recipes using either built in models or external storage providers hosting PDF's, Images or other files.
![Preview](preview.png)

View File

@@ -2,14 +2,17 @@ import django_filters
from django.contrib.postgres.search import TrigramSimilarity
from django.db.models import Q
from cookbook.forms import MultiSelectWidget
from cookbook.models import Recipe, Keyword
from cookbook.models import Recipe, Keyword, Ingredient
from django.conf import settings
from django.utils.translation import gettext as _
class RecipeFilter(django_filters.FilterSet):
name = django_filters.CharFilter(method='filter_name')
keywords = django_filters.ModelMultipleChoiceFilter(queryset=Keyword.objects.all(), widget=MultiSelectWidget,
method='filter_keywords')
ingredients = django_filters.ModelMultipleChoiceFilter(queryset=Ingredient.objects.all(), widget=MultiSelectWidget,
method='filter_ingredients', label=_('Ingredients'))
@staticmethod
def filter_keywords(queryset, name, value):
@@ -19,6 +22,14 @@ class RecipeFilter(django_filters.FilterSet):
queryset = queryset.filter(keywords=x)
return queryset
@staticmethod
def filter_ingredients(queryset, name, value):
if not name == 'ingredients':
return queryset
for x in value:
queryset = queryset.filter(recipeingredient__ingredient=x).distinct()
return queryset
@staticmethod
def filter_name(queryset, name, value):
if not name == 'name':
@@ -32,14 +43,4 @@ class RecipeFilter(django_filters.FilterSet):
class Meta:
model = Recipe
fields = ['name', 'keywords']
class QuickRecipeFilter(django_filters.FilterSet):
name = django_filters.CharFilter(lookup_expr='icontains')
keywords = django_filters.ModelMultipleChoiceFilter(queryset=Keyword.objects.all(), widget=MultiSelectWidget,
method='filter_keywords')
class Meta:
model = Recipe
fields = ['name', 'keywords']
fields = ['name', 'keywords', 'ingredients']

View File

@@ -73,11 +73,16 @@ class InternalRecipeForm(forms.ModelForm):
widgets = {'keywords': MultiSelectWidget}
class RecipeForm(forms.Form):
class ShoppingForm(forms.Form):
recipe = forms.ModelMultipleChoiceField(
queryset=Recipe.objects.all(),
widget=MultiSelectWidget
)
markdown_format = forms.BooleanField(
help_text=_('Include <code>- [ ]</code> in list for easier usage in markdown based documents.'),
required=False,
initial=False
)
class UnitMergeForm(forms.Form):
@@ -97,6 +102,23 @@ class UnitMergeForm(forms.Form):
)
class IngredientMergeForm(forms.Form):
prefix = 'ingredient'
new_ingredient = forms.ModelChoiceField(
queryset=Ingredient.objects.all(),
widget=SelectWidget,
label=_('New Ingredient'),
help_text=_('New ingredient that other gets replaced by.'),
)
old_ingredient = forms.ModelChoiceField(
queryset=Ingredient.objects.all(),
widget=SelectWidget,
label=_('Old Ingredient'),
help_text=_('Ingredient that should be replaced.'),
)
class CommentForm(forms.ModelForm):
prefix = 'comment'

View File

@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2020-01-13 12:08+0100\n"
"POT-Creation-Date: 2020-02-18 23:20+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -18,152 +18,247 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: cookbook/forms.py:24 cookbook/forms.py:40 cookbook/forms.py:122
#: .\cookbook\filters.py:15
#: .\cookbook\templates\forms\edit_internal_recipe.html:28
#: .\cookbook\templates\forms\ingredients.html:33
#: .\cookbook\templates\recipe_view.html:67
msgid "Ingredients"
msgstr "Zutaten"
#: .\cookbook\forms.py:35
msgid ""
"Color of the top navigation bar. Not all colors work with all themes, just "
"try them out!"
msgstr ""
"Farbe der oberen Navigationsleiste. Nicht alle Farben passen, daher einfach mal ausprobieren!"
#: .\cookbook\forms.py:49 .\cookbook\forms.py:67 .\cookbook\forms.py:189
msgid "Name"
msgstr "Name"
#: cookbook/forms.py:25 cookbook/forms.py:41 cookbook/forms.py:123
#: cookbook/templates/stats.html:22
#: .\cookbook\forms.py:50 .\cookbook\forms.py:68 .\cookbook\forms.py:190
#: .\cookbook\templates\stats.html:22
msgid "Keywords"
msgstr "Schlagwörter"
#: cookbook/forms.py:26 cookbook/forms.py:43
#: .\cookbook\forms.py:51 .\cookbook\forms.py:70
msgid "Preparation time in minutes"
msgstr "Zubereitungszeit in Minuten"
#: cookbook/forms.py:27 cookbook/forms.py:44
#: .\cookbook\forms.py:52 .\cookbook\forms.py:71
msgid "Waiting time (cooking/baking) in minutes"
msgstr "Wartezeit (kochen/backen) in Minuten"
#: cookbook/forms.py:28 cookbook/forms.py:124
#: .\cookbook\forms.py:53 .\cookbook\forms.py:191
msgid "Path"
msgstr "Pfad"
#: cookbook/forms.py:29
#: .\cookbook\forms.py:54
msgid "Storage UID"
msgstr "Speicher ID"
#: cookbook/forms.py:42
#: .\cookbook\forms.py:69
msgid "Instructions"
msgstr "Anleitung"
#: cookbook/forms.py:57
#: .\cookbook\forms.py:82
msgid ""
"Include <code>- [ ]</code> in list for easier usage in markdown based "
"documents."
msgstr ""
"Füge <code>- [ ]</code> vor den Zutaten ein um sie besser in einem Markdown Dokument "
"zu verwenden."
#: .\cookbook\forms.py:94
msgid "New Unit"
msgstr "Neue Einheit"
#: .\cookbook\forms.py:95
msgid "New unit that other gets replaced by."
msgstr "Neue Einheit die die alte ersetzt."
#: .\cookbook\forms.py:100
msgid "Old Unit"
msgstr "Alte Einheit"
#: .\cookbook\forms.py:101
msgid "Unit that should be replaced."
msgstr "Einheit die ersetzt werden soll."
#: .\cookbook\forms.py:111
msgid "New Ingredient"
msgstr "Neue Zutat"
#: .\cookbook\forms.py:112
msgid "New ingredient that other gets replaced by."
msgstr "Neue Zutat die die alte ersetzt."
#: .\cookbook\forms.py:117
msgid "Old Ingredient"
msgstr "Alte Zutat"
#: .\cookbook\forms.py:118
msgid "Ingredient that should be replaced."
msgstr "Zutat die ersetzt werden soll."
#: .\cookbook\forms.py:130
msgid "Add your comment: "
msgstr "Schreibe einen Kommentar:"
#: cookbook/forms.py:75
#: .\cookbook\forms.py:148
msgid "Leave empty for dropbox and enter app password for nextcloud."
msgstr "Für Dropbox leer lassen, bei Nextcloud App-Passwort eingeben."
#: cookbook/forms.py:78
#: .\cookbook\forms.py:151
msgid "Leave empty for nextcloud and enter api token for dropbox."
msgstr "Bei Nextcloud leer lassen, bei Dropbox API Token eingeben"
#: cookbook/forms.py:86
#: .\cookbook\forms.py:159
msgid ""
"Leave empty for dropbox and enter only base url for nextcloud (<code>/remote."
"php/webdav/</code> is added automatically)"
msgstr "Bei Dropbox leer lassen, bei Nextcloud Server URL angeben (<code>/remote."
"php/webdav/</code> wird automatisch hinzugefügt)"
msgstr ""
"Bei Dropbox leer lassen, bei Nextcloud Server URL angeben (<code>/remote.php/"
"webdav/</code> wird automatisch hinzugefügt)"
#: cookbook/forms.py:111
#: .\cookbook\forms.py:178
msgid "Search String"
msgstr "Such Wort"
#: cookbook/forms.py:125
#: .\cookbook\forms.py:192
msgid "File ID"
msgstr "Datei ID"
#: cookbook/tables.py:75 cookbook/templates/forms/edit_internal_recipe.html:39
#: cookbook/templates/forms/edit_internal_recipe.html:98
#: cookbook/templates/generic/delete_template.html:5
#: cookbook/templates/generic/delete_template.html:13
#: cookbook/templates/generic/edit_template.html:25
#: .\cookbook\models.py:172
msgid "Breakfast"
msgstr "Frühstück"
#: .\cookbook\models.py:172
msgid "Lunch"
msgstr "Mittagessen"
#: .\cookbook\models.py:172
msgid "Dinner"
msgstr "Abendessen"
#: .\cookbook\models.py:172
msgid "Other"
msgstr "Andere"
#: .\cookbook\tables.py:75
#: .\cookbook\templates\forms\edit_internal_recipe.html:49
#: .\cookbook\templates\forms\edit_internal_recipe.html:176
#: .\cookbook\templates\generic\delete_template.html:5
#: .\cookbook\templates\generic\delete_template.html:13
#: .\cookbook\templates\generic\edit_template.html:25
msgid "Delete"
msgstr "Löschen"
#: cookbook/templates/base.html:64 cookbook/templates/base.html:72
#: cookbook/templates/index.html:7
#: .\cookbook\templates\base.html:70 .\cookbook\templates\base.html:78
#: .\cookbook\templates\forms\ingredients.html:7
#: .\cookbook\templates\index.html:7 .\cookbook\templates\shopping_list.html:7
msgid "Cookbook"
msgstr "Kochbuch"
#: cookbook/templates/base.html:76
#: .\cookbook\templates\base.html:82
msgid "Books"
msgstr "Bücher"
#: cookbook/templates/base.html:81
#: .\cookbook\templates\base.html:86 .\cookbook\templates\meal_plan.html:4
#: .\cookbook\templates\meal_plan.html:13 .\cookbook\views\edit.py:261
#: .\cookbook\views\edit.py:462 .\cookbook\views\new.py:130
msgid "Meal-Plan"
msgstr "Plan"
#: .\cookbook\templates\base.html:90
#, fuzzy
#| msgid "Shopping List"
msgid "Shopping"
msgstr "Einkaufsliste"
#: .\cookbook\templates\base.html:96
msgid "Tags"
msgstr "Schlagwörter"
#: cookbook/templates/base.html:85 cookbook/views/edit.py:130
#: cookbook/views/edit.py:331 cookbook/views/lists.py:17
#: cookbook/views/new.py:44
#: .\cookbook\templates\base.html:100 .\cookbook\views\edit.py:151
#: .\cookbook\views\edit.py:407 .\cookbook\views\lists.py:17
#: .\cookbook\views\new.py:46
msgid "Keyword"
msgstr "Schlagwort"
#: cookbook/templates/base.html:87
#: .\cookbook\templates\base.html:102
msgid "Batch Edit"
msgstr "Massenbearbeitung"
#: cookbook/templates/base.html:92
#: .\cookbook\templates\base.html:107
msgid "Storage Data"
msgstr "Datenquellen"
#: cookbook/templates/base.html:96
#: .\cookbook\templates\base.html:111
msgid "Storage Backends"
msgstr "Speicher Quellen"
#: cookbook/templates/base.html:98
#: .\cookbook\templates\base.html:113
msgid "Configure Sync"
msgstr "Sync Einstellen"
#: cookbook/templates/base.html:100
#: .\cookbook\templates\base.html:115
msgid "Import Recipes"
msgstr "Importierte Rezepte"
#: cookbook/templates/base.html:102 cookbook/views/lists.py:25
#: .\cookbook\templates\base.html:117 .\cookbook\views\lists.py:25
msgid "Import Log"
msgstr "Import Log"
#: cookbook/templates/base.html:104 cookbook/templates/stats.html:10
#: .\cookbook\templates\base.html:119 .\cookbook\templates\stats.html:10
msgid "Statistics"
msgstr "Statistiken"
#: cookbook/templates/base.html:112
#: .\cookbook\templates\base.html:121
msgid "Units & Ingredients"
msgstr "Einheiten & Zutaten"
#: .\cookbook\templates\base.html:130 .\cookbook\templates\settings.html:6
#: .\cookbook\templates\settings.html:11
msgid "Settings"
msgstr "Einstellungen"
#: .\cookbook\templates\base.html:135
msgid "Admin"
msgstr "Admin"
#: cookbook/templates/base.html:116
#: .\cookbook\templates\base.html:140
msgid "Logout"
msgstr "Ausloggen"
#: cookbook/templates/base.html:119
#: .\cookbook\templates\base.html:143
#: .\cookbook\templates\registration\login.html:44
msgid "Login"
msgstr "Einloggen"
#: cookbook/templates/batch/edit.html:6
#: .\cookbook\templates\batch\edit.html:6
msgid "Batch edit Category"
msgstr "Kategorie massenbearbeitung"
#: cookbook/templates/batch/edit.html:15
#: .\cookbook\templates\batch\edit.html:15
msgid "Batch edit Recipes"
msgstr "Rezept massenbearbeitung"
#: cookbook/templates/batch/edit.html:20
#: .\cookbook\templates\batch\edit.html:20
msgid "Add the specified keywords to all recipes containing a word"
msgstr ""
"Ausgewählte Schlagwörter zu allen Rezepten die das Suchwort enthalten "
"hinzufügen"
#: cookbook/templates/batch/monitor.html:6 cookbook/views/edit.py:114
#: .\cookbook\templates\batch\monitor.html:6 .\cookbook\views\edit.py:135
msgid "Sync"
msgstr "Synchronisieren"
#: cookbook/templates/batch/monitor.html:10
#: .\cookbook\templates\batch\monitor.html:10
msgid "Manage watched Folders"
msgstr "Überwachte Ordner verwalten"
#: cookbook/templates/batch/monitor.html:14
#: .\cookbook\templates\batch\monitor.html:14
msgid ""
"On this Page you can manage all storage folder locations that should be "
"monitored and synced"
@@ -171,20 +266,20 @@ msgstr ""
"Auf dieser Seite kannst du alle Ordner verwalten die überwacht und "
"synchronisiert werden sollen"
#: cookbook/templates/batch/monitor.html:16
#: .\cookbook\templates\batch\monitor.html:16
msgid "The path must be in the following format"
msgstr "Der Pfad muss in folgendem Format sein"
#: cookbook/templates/batch/monitor.html:27
#: .\cookbook\templates\batch\monitor.html:27
msgid "Sync Now!"
msgstr "Jetzt Synchronisieren!"
#: cookbook/templates/batch/waiting.html:4
#: cookbook/templates/batch/waiting.html:10
#: .\cookbook\templates\batch\waiting.html:4
#: .\cookbook\templates\batch\waiting.html:10
msgid "Importing Recipes"
msgstr "Rezept werden importiert"
#: cookbook/templates/batch/waiting.html:23
#: .\cookbook\templates\batch\waiting.html:23
msgid ""
"This can take a few minutes, depending on the number of recipes in sync, "
"please wait."
@@ -192,120 +287,165 @@ msgstr ""
"Abhängig von der Anzahl der Rezepte kann dieser Vorgang einige Minuten "
"dauern, bitte warten."
#: cookbook/templates/books.html:4 cookbook/templates/books.html:10
#: .\cookbook\templates\books.html:4 .\cookbook\templates\books.html:10
msgid "Recipe Books"
msgstr "Rezept Bücher"
#: cookbook/templates/books.html:14
#: .\cookbook\templates\books.html:14
msgid "New Book"
msgstr "Neues Buch"
#: cookbook/templates/books.html:53
#: .\cookbook\templates\books.html:53
msgid "There are no recipes in this book yet."
msgstr "In diesem Buch sind bisher keine Rezepte."
#: cookbook/templates/forms/edit_import_recipe.html:5
#: cookbook/templates/forms/edit_import_recipe.html:9
#: .\cookbook\templates\forms\edit_import_recipe.html:5
#: .\cookbook\templates\forms\edit_import_recipe.html:9
msgid "Import new Recipe"
msgstr "Rezept Importieren"
#: cookbook/templates/forms/edit_import_recipe.html:14
#: cookbook/templates/forms/edit_internal_recipe.html:37
#: cookbook/templates/generic/edit_template.html:23
#: cookbook/templates/generic/new_template.html:23
#: cookbook/templates/recipe_view.html:207
#: .\cookbook\templates\forms\edit_import_recipe.html:14
#: .\cookbook\templates\forms\edit_internal_recipe.html:47
#: .\cookbook\templates\generic\edit_template.html:23
#: .\cookbook\templates\generic\new_template.html:23
#: .\cookbook\templates\recipe_view.html:214
#: .\cookbook\templates\settings.html:33 .\cookbook\templates\settings.html:47
msgid "Save"
msgstr "Speichern"
#: cookbook/templates/forms/edit_internal_recipe.html:7
#: cookbook/templates/forms/edit_internal_recipe.html:16
#: .\cookbook\templates\forms\edit_internal_recipe.html:8
#: .\cookbook\templates\forms\edit_internal_recipe.html:18
msgid "Edit Recipe"
msgstr "Rezept bearbeiten"
#: cookbook/templates/forms/edit_internal_recipe.html:26
#: cookbook/templates/recipe_view.html:63
msgid "Ingredients"
msgstr "Zutaten"
#: .\cookbook\templates\forms\edit_internal_recipe.html:37
msgid ""
"Use <b>Ctrl</b>+<b>Space</b> to insert new Ingredient!<br/>You can also save "
"the recipe using <b>Ctrl</b>+<b>Shift</b>+<b>S</b>."
msgstr ""
#: cookbook/templates/forms/edit_internal_recipe.html:41
#: cookbook/templates/generic/edit_template.html:27
#: cookbook/templates/recipe_view.html:7
#: .\cookbook\templates\forms\edit_internal_recipe.html:51
#: .\cookbook\templates\generic\edit_template.html:27
#: .\cookbook\templates\recipe_view.html:7
msgid "View"
msgstr "Angucken"
#: cookbook/templates/forms/edit_internal_recipe.html:45
#: cookbook/templates/generic/edit_template.html:30
#: .\cookbook\templates\forms\edit_internal_recipe.html:55
#: .\cookbook\templates\generic\edit_template.html:30
msgid "Delete original file"
msgstr "Original löschen"
#: cookbook/templates/forms/edit_internal_recipe.html:90
#: cookbook/templates/forms/edit_internal_recipe.html:127
#: .\cookbook\templates\forms\edit_internal_recipe.html:159
#: .\cookbook\templates\forms\edit_internal_recipe.html:208
msgid "Ingredient"
msgstr "Zutat"
#: cookbook/templates/forms/edit_internal_recipe.html:95
#: .\cookbook\templates\forms\edit_internal_recipe.html:164
msgid "Amount"
msgstr "Menge"
#: cookbook/templates/forms/edit_internal_recipe.html:96
#: .\cookbook\templates\forms\edit_internal_recipe.html:166
msgid "Unit"
msgstr "Einheit"
#: cookbook/templates/generic/delete_template.html:18
#: .\cookbook\templates\forms\edit_internal_recipe.html:179
msgid "Are you sure that you want to delete this ingredient?"
msgstr "Bist du sicher das du diese Zutat löschen willst?"
#: .\cookbook\templates\forms\ingredients.html:15
msgid "Edit Ingredients"
msgstr "Zutaten Bearbeiten"
#: .\cookbook\templates\forms\ingredients.html:16
msgid ""
"\n"
" The following form can be used if, accidentally, two (or more) units "
"or ingredients where created that should be\n"
" the same.\n"
" It merges two units or ingredients and updates all recipes using "
"them.\n"
" "
msgstr ""
"\n"
" Dieses Formular kann genutzt werden wenn versehentlich zwei (oder mehr) Einheiten"
"oder Zutaten erstellt wurden die eigentlich identisch\n"
" sein sollen.\n"
" Es vereint zwei Zutaten oder Einheiten und aktualisiert alle entsprechenden "
"Rezepte.\n"
" "
#: .\cookbook\templates\forms\ingredients.html:24
msgid "Units"
msgstr "Einheiten"
#: .\cookbook\templates\forms\ingredients.html:29
msgid "Are you sure that you want to merge these two units ?"
msgstr "Bist du sicher diese beiden Einheiten zusammengeführt werden sollen ?"
#: .\cookbook\templates\forms\ingredients.html:30
#: .\cookbook\templates\forms\ingredients.html:39
msgid "Merge"
msgstr "Zusammenführen"
#: .\cookbook\templates\forms\ingredients.html:38
msgid "Are you sure that you want to merge these two ingredients ?"
msgstr "Bist du sicher diese beiden Zutaten zusammengeführt werden sollen ?"
#: .\cookbook\templates\generic\delete_template.html:18
#, python-format
msgid "Are you sure you want to delete the %(title)s: <b>%(object)s</b> "
msgstr "Bist du sicher das %(title)s: <b>%(object)s</b> gelöscht werden soll"
#: cookbook/templates/generic/delete_template.html:21
#: .\cookbook\templates\generic\delete_template.html:21
msgid "Confirm"
msgstr ""
msgstr "Bestätigen"
#: cookbook/templates/generic/edit_template.html:6
#: cookbook/templates/generic/edit_template.html:14
#: .\cookbook\templates\generic\edit_template.html:6
#: .\cookbook\templates\generic\edit_template.html:14
msgid "Edit"
msgstr "Bearbeiten"
#: cookbook/templates/generic/list_template.html:6
#: cookbook/templates/generic/list_template.html:12
#: .\cookbook\templates\generic\list_template.html:6
#: .\cookbook\templates\generic\list_template.html:12
msgid "List"
msgstr "Liste"
#: cookbook/templates/generic/list_template.html:19
#: .\cookbook\templates\generic\list_template.html:19
msgid "Import all"
msgstr "Alle importieren"
#: cookbook/templates/generic/new_template.html:6
#: cookbook/templates/generic/new_template.html:14
#: .\cookbook\templates\generic\new_template.html:6
#: .\cookbook\templates\generic\new_template.html:14
msgid "New"
msgstr "Neu"
#: cookbook/templates/generic/table_template.html:76
#: .\cookbook\templates\generic\table_template.html:76
msgid "previous"
msgstr "vorherige"
#: cookbook/templates/generic/table_template.html:98
#: .\cookbook\templates\generic\table_template.html:98
msgid "next"
msgstr "nächste"
#: cookbook/templates/include/recipe_open_modal.html:28
#: cookbook/views/edit.py:258 cookbook/views/edit.py:278
#: cookbook/views/edit.py:298 cookbook/views/new.py:32
#: .\cookbook\templates\include\recipe_open_modal.html:28
#: .\cookbook\views\edit.py:295 .\cookbook\views\edit.py:354
#: .\cookbook\views\edit.py:374 .\cookbook\views\new.py:34
msgid "Recipe"
msgstr "Rezept"
#: cookbook/templates/include/recipe_open_modal.html:39
#: .\cookbook\templates\include\recipe_open_modal.html:39
msgid "Close"
msgstr "Schließen"
#: cookbook/templates/include/recipe_open_modal.html:56
#: .\cookbook\templates\include\recipe_open_modal.html:56
msgid "Open Recipe"
msgstr "Rezept öffnen"
#: cookbook/templates/include/storage_backend_warning.html:4
#: .\cookbook\templates\include\storage_backend_warning.html:4
msgid "Security Warning"
msgstr "Sicherheitswarnung"
#: cookbook/templates/include/storage_backend_warning.html:5
#: .\cookbook\templates\include\storage_backend_warning.html:5
msgid ""
"\n"
" The <b>Password and Token</b> field are stored as <b>plain text</b> "
@@ -327,48 +467,52 @@ msgstr ""
"oder Accounts mit limitiertem Zugriff verwendet werden.\n"
" "
#: cookbook/templates/index.html:21
#: .\cookbook\templates\index.html:21
msgid "Search recipe ..."
msgstr "Suche Rezept ..."
#: cookbook/templates/index.html:40
#: .\cookbook\templates\index.html:40
msgid "Advanced Search"
msgstr "Erweiterte Suche"
#: cookbook/templates/index.html:59
#: .\cookbook\templates\index.html:62
msgid "Log in to view Recipies"
msgstr "Bitte einloggen um Rezepte zu sehen"
#: cookbook/templates/recipe_view.html:27
#: .\cookbook\templates\meal_plan.html:20
msgid "Week"
msgstr "Woche"
#: .\cookbook\templates\recipe_view.html:31
msgid "in"
msgstr "in"
#: cookbook/templates/recipe_view.html:32
#: cookbook/templates/recipe_view.html:174
#: .\cookbook\templates\recipe_view.html:36
#: .\cookbook\templates\recipe_view.html:181
msgid "by"
msgstr "von"
#: cookbook/templates/recipe_view.html:43
#: .\cookbook\templates\recipe_view.html:47
msgid "Preparation time ca."
msgstr "Zubereitungszeit ca."
#: cookbook/templates/recipe_view.html:48
#: .\cookbook\templates\recipe_view.html:52
msgid "Waiting time ca."
msgstr "Zubereitungszeit ca."
#: cookbook/templates/recipe_view.html:110
#: .\cookbook\templates\recipe_view.html:114
msgid "Recipe Image"
msgstr "Rezept Bild"
#: cookbook/templates/recipe_view.html:126
#: .\cookbook\templates\recipe_view.html:133
msgid "View external recipe"
msgstr "Externes Rezept ansehen"
#: cookbook/templates/recipe_view.html:137
#: .\cookbook\templates\recipe_view.html:144
msgid "External recipe"
msgstr "Externes Rezept"
#: cookbook/templates/recipe_view.html:139
#: .\cookbook\templates\recipe_view.html:146
msgid ""
"\n"
" This is an external recipe, which means you can only "
@@ -386,56 +530,81 @@ msgstr ""
"bleibt weiterhin verfügbar.\n"
" "
#: cookbook/templates/recipe_view.html:147
#: .\cookbook\templates\recipe_view.html:154
msgid "Convert now!"
msgstr "Jetzt umwandeln!"
#: cookbook/templates/recipe_view.html:156
#: .\cookbook\templates\recipe_view.html:163
msgid "Comments"
msgstr "Kommentare"
#: cookbook/templates/recipe_view.html:165 cookbook/views/edit.py:191
#: cookbook/views/edit.py:353
#: .\cookbook\templates\recipe_view.html:172 .\cookbook\views\edit.py:212
#: .\cookbook\views\edit.py:429
msgid "Comment"
msgstr "Kommentar"
#: cookbook/templates/registration/login.html:8
#: .\cookbook\templates\registration\login.html:8
msgid "Your username and password didn't match. Please try again."
msgstr "Nutzername oder Passwort falsch. Bitte versuch es erneut."
#: cookbook/templates/stats.html:4
#: .\cookbook\templates\settings.html:17
msgid "Language"
msgstr "Sprache"
#: .\cookbook\templates\settings.html:42
msgid "Style"
msgstr "Stil"
#: .\cookbook\templates\shopping_list.html:15
msgid "Shopping List"
msgstr "Einkaufsliste"
#: .\cookbook\templates\shopping_list.html:20
msgid "Load"
msgstr "Laden"
#: .\cookbook\templates\shopping_list.html:37
#: .\cookbook\templates\shopping_list.html:55
msgid "Copy list to clipboard"
msgstr "Kopiere Liste in Zwischenablage"
#: .\cookbook\templates\shopping_list.html:48
msgid "Copied!"
msgstr "Kopiert!"
#: .\cookbook\templates\stats.html:4
msgid "Stats"
msgstr "Statistiken"
#: cookbook/templates/stats.html:17
#: .\cookbook\templates\stats.html:17
msgid "Number of objects"
msgstr "Anzahl der Objekte"
#: cookbook/templates/stats.html:20
#: .\cookbook\templates\stats.html:20
msgid "Recipes"
msgstr "Rezepte"
#: cookbook/templates/stats.html:24
#: .\cookbook\templates\stats.html:24
msgid "Recipe Imports"
msgstr "Rezept Importe"
#: cookbook/templates/stats.html:32
#: .\cookbook\templates\stats.html:32
msgid "Objects stats"
msgstr "Objekt Statistiken"
#: cookbook/templates/stats.html:35
#: .\cookbook\templates\stats.html:35
msgid "Recipes without Keywords"
msgstr "Rezepte ohne Schlagwort"
#: cookbook/views/api.py:63
#: .\cookbook\views\api.py:63
msgid "Sync successful!"
msgstr "Synchronisation erfolgreich!"
#: cookbook/views/api.py:66
#: .\cookbook\views\api.py:66
msgid "Error synchronizing with Storage"
msgstr "Fehler beim Synchronisieren"
#: cookbook/views/data.py:71
#: .\cookbook\views\data.py:71
#, python-format
msgid "Batch edit done. %(count)d recipe was updated."
msgid_plural "Batch edit done. %(count)d Recipes where updated."
@@ -443,65 +612,77 @@ msgstr[0] "Massenbearbeitung erfolgreich. %(count)d Rezept wurde aktualisiert."
msgstr[1] ""
"Massenbearbeitung erfolgreich. %(count)d Rezepte wurden aktualisiert."
#: cookbook/views/edit.py:88
#: .\cookbook\views\edit.py:109
msgid "Recipe saved!"
msgstr "Rezept gespeichert"
#: cookbook/views/edit.py:91 cookbook/views/new.py:87
msgid "There was an error importing this recipe!"
msgstr "Beim importieren des Rezeptes ist ein Fehler aufgetreten"
#: .\cookbook\views\edit.py:111
msgid "There was an error saving this recipe!"
msgstr "Es gab einen Fehler beim Speichern des Rezepts"
#: cookbook/views/edit.py:139 cookbook/views/edit.py:182
#: .\cookbook\views\edit.py:160 .\cookbook\views\edit.py:203
msgid "You cannot edit this comment!"
msgstr "Du kannst diesen Kommentar nicht bearbeiten!"
#: cookbook/views/edit.py:158
#: .\cookbook\views\edit.py:179
msgid "Storage saved!"
msgstr "Speicherquelle gespeichert"
#: cookbook/views/edit.py:161
#: .\cookbook\views\edit.py:182
msgid "There was an error updating this storage backend.!"
msgstr "Es gab einen Fehler beim aktualisierung dieser Speicher Quelle"
#: cookbook/views/edit.py:208 cookbook/views/edit.py:309
#: cookbook/views/lists.py:34
#: .\cookbook\views\edit.py:229 .\cookbook\views\edit.py:385
#: .\cookbook\views\lists.py:34
msgid "Import"
msgstr "Rezept Importieren"
#: cookbook/views/edit.py:224 cookbook/views/edit.py:364
#: cookbook/views/new.py:110
#: .\cookbook\views\edit.py:245 .\cookbook\views\edit.py:440
#: .\cookbook\views\new.py:112
msgid "Recipe Book"
msgstr "Rezeptbuch"
#: cookbook/views/edit.py:246
#: .\cookbook\views\edit.py:283
msgid "Changes saved!"
msgstr "Änderungen gespeichert"
#: cookbook/views/edit.py:250
#: .\cookbook\views\edit.py:287
msgid "Error saving changes!"
msgstr "Fehler beim Speichern der Daten."
#: cookbook/views/edit.py:320
#: .\cookbook\views\edit.py:317
msgid "Units merged!"
msgstr "Einheiten zusammengeführt"
#: .\cookbook\views\edit.py:330
msgid "Ingredients merged!"
msgstr "Zutaten zusammengeführt"
#: .\cookbook\views\edit.py:396
msgid "Monitor"
msgstr "Monitor"
#: cookbook/views/edit.py:342 cookbook/views/lists.py:42
#: cookbook/views/new.py:62
#: .\cookbook\views\edit.py:418 .\cookbook\views\lists.py:42
#: .\cookbook\views\new.py:64
msgid "Storage Backend"
msgstr "Speicher Quelle"
#: cookbook/views/edit.py:375
#: .\cookbook\views\edit.py:451
msgid "Bookmarks"
msgstr "Lesezeichen"
#: cookbook/views/new.py:84
#: .\cookbook\views\new.py:86
msgid "Imported new recipe!"
msgstr "Importier neue Rezepte"
#: cookbook/views/views.py:42
#: .\cookbook\views\new.py:89
msgid "There was an error importing this recipe!"
msgstr "Beim importieren des Rezeptes ist ein Fehler aufgetreten"
#: .\cookbook\views\views.py:44
msgid "Comment saved!"
msgstr "Kommentar gespeichert"
#: cookbook/views/views.py:52
#: .\cookbook\views\views.py:54
msgid "Bookmark saved!"
msgstr "Lesezeichen gespeichert"

View File

@@ -0,0 +1,23 @@
# Generated by Django 3.0.2 on 2020-02-19 15:05
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('cookbook', '0025_userpreference_nav_color'),
]
operations = [
migrations.AddField(
model_name='recipe',
name='cors_link',
field=models.CharField(blank=True, max_length=1024, null=True),
),
migrations.AlterField(
model_name='recipe',
name='link',
field=models.CharField(blank=True, max_length=512, null=True),
),
]

View File

@@ -84,7 +84,8 @@ class Recipe(models.Model):
storage = models.ForeignKey(Storage, on_delete=models.PROTECT, blank=True, null=True)
file_uid = models.CharField(max_length=256, default="")
file_path = models.CharField(max_length=512, default="")
link = models.CharField(max_length=512, default="")
link = models.CharField(max_length=512, null=True, blank=True)
cors_link = models.CharField(max_length=1024, null=True, blank=True)
keywords = models.ManyToManyField(Keyword, blank=True)
working_time = models.IntegerField(default=0)
waiting_time = models.IntegerField(default=0)

View File

@@ -1,3 +1,4 @@
import base64
import os
from datetime import datetime
@@ -88,6 +89,16 @@ class Dropbox(Provider):
response = Dropbox.create_share_link(recipe)
return response['url']
@staticmethod
def get_base64_file(recipe):
if not recipe.link:
recipe.link = Dropbox.get_share_link(recipe)
recipe.save()
response = requests.get(recipe.link.replace('www.dropbox.', 'dl.dropboxusercontent.'))
return base64.b64encode(response.content)
@staticmethod
def rename_file(recipe, new_name):
url = "https://api.dropboxapi.com/2/files/move_v2"

View File

@@ -1,8 +1,10 @@
import base64
import os
import tempfile
from datetime import datetime
import webdav3.client as wc
import requests
from io import BytesIO
from requests.auth import HTTPBasicAuth
from cookbook.models import Recipe, RecipeImport, SyncLog
@@ -81,6 +83,20 @@ class Nextcloud(Provider):
return Nextcloud.create_share_link(recipe)
@staticmethod
def get_base64_file(recipe):
client = Nextcloud.get_client(recipe.storage)
tmp_file_path = tempfile.gettempdir() + '/' + recipe.name + '.pdf'
client.download_file(remote_path=recipe.file_path, local_path=tmp_file_path)
val = base64.b64encode(open(tmp_file_path, 'rb').read())
os.remove(tmp_file_path)
return val
@staticmethod
def rename_file(recipe, new_name):
client = Nextcloud.get_client(recipe.storage)

View File

@@ -11,10 +11,14 @@ class Provider:
def get_share_link(recipe):
raise Exception('Method not implemented in storage provider')
@staticmethod
def get_base64_file(recipe):
raise Exception('Method not implemented in storage provider')
@staticmethod
def rename_file(recipe, new_name):
raise Exception('Method not implemented in storage provider')
@staticmethod
def delete_file(recipe, new_name):
def delete_file(recipe):
raise Exception('Method not implemented in storage provider')

View File

@@ -8,8 +8,7 @@ from .models import *
class RecipeTable(tables.Table):
id = tables.LinkColumn('edit_recipe', args=[A('id')])
name = tables.TemplateColumn(
"<a href='#' onClick='openRecipe({{record.id}})'>{{record.name}}</a>")
name = tables.LinkColumn('view_recipe', args=[A('id')])
all_tags = tables.Column(
attrs={'td': {'class': 'd-none d-lg-table-cell'}, 'th': {'class': 'd-none d-lg-table-cell'}})

View File

@@ -74,18 +74,22 @@
</button>
<div class="collapse navbar-collapse" id="navbarText">
<ul class="navbar-nav mr-auto">
<li class="nav-item active">
<li class="nav-item {% if request.resolver_match.url_name == "index" %}active{% endif %}">
<a class="nav-link" href="{% url 'index' %}"><i class="fas fa-book"></i> {% trans 'Cookbook' %}<span
class="sr-only">(current)</span></a>
</li>
<li class="nav-item">
<li class="nav-item {% if request.resolver_match.url_name == "view_books" %}active{% endif %}">
<a class="nav-link" href="{% url 'view_books' %}"><i class="fas fa-bookmark"></i> {% trans 'Books' %}
</a>
</li>
<li class="nav-item">
<li class="nav-item {% if request.resolver_match.url_name == "view_plan" %}active{% endif %}">
<a class="nav-link" href="{% url 'view_plan' %}"><i class="fas fa-calendar"></i> {% trans 'Meal-Plan' %}
</a>
</li>
<li class="nav-item {% if request.resolver_match.url_name == "view_shopping" %}active{% endif %}">
<a class="nav-link" href="{% url 'view_shopping' %}"><i class="fas fa-shopping-cart"></i> {% trans 'Shopping' %}
</a>
</li>
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdownMenuLink" data-toggle="dropdown"
aria-haspopup="true" aria-expanded="false">
@@ -121,7 +125,7 @@
</ul>
<ul class="navbar-nav ml-auto">
<li class="nav-item">
<li class="nav-item {% if request.resolver_match.url_name == "view_settings" %}active{% endif %}">
<a class="nav-link" href="{% url 'view_settings' %}"><i
class="fas fa-user-cog"></i> {% trans 'Settings' %}</a>
</li>

View File

@@ -41,7 +41,7 @@
{% for r in b.recipes %}
<div class="row">
<div class="col col-md-10">
<li><a href="#" onClick='openRecipe({{ r.recipe.pk }})'>{{ r.recipe.name }}</a></li>
<li><a href="{% url 'view_recipe' r.recipe.pk %}">{{ r.recipe.name }}</a></li>
</div>
<div class="col col-md-2" style="text-align: right">
<a href="{% url 'delete_recipe_book_entry' r.pk %}"><i class="fas fa-trash-alt"></i></a>
@@ -58,5 +58,4 @@
<br/>
{% endfor %}
{% include 'include/recipe_open_modal.html' %}
{% endblock %}

View File

@@ -7,7 +7,7 @@
{% block title %}{% trans "Cookbook" %}{% endblock %}
{% block extra_head %}
{{ form.media }}
{{ units_form.media }}
{% endblock %}
{% block content %}
@@ -24,11 +24,19 @@
<h4>{% trans 'Units' %}</h4>
<form action="{% url 'edit_ingredient' %}" method="post">
{% csrf_token %}
{{ form|crispy }}
{{ units_form|crispy }}
<button class="btn btn-danger" type="submit"
onclick="confirm('{% trans 'Are you sure that you want to merge these two units ?' %}')"><i
class="fas fa-sync-alt"></i> {% trans 'Merge' %}</button>
</form>
<h4>{% trans 'Ingredients' %}</h4>
<form action="{% url 'edit_ingredient' %}" method="post">
{% csrf_token %}
{{ ingredients_form|crispy }}
<button class="btn btn-danger" type="submit"
onclick="confirm('{% trans 'Are you sure that you want to merge these two ingredients ?' %}')"><i
class="fas fa-sync-alt"></i> {% trans 'Merge' %}</button>
</form>
{% endblock %}

View File

@@ -23,14 +23,15 @@
<button class="btn btn-success" type="submit"><i class="fas fa-save"></i> {% trans 'Save' %}</button>
</form>
{% if default_recipe %}
<script type="text/javascript">
<script type="text/javascript">
{% if default_recipe %}
$(document).ready(function () {
$('#id_recipe').val({{ default_recipe.pk }}).trigger('change');
});
{% endif %}
</script>
</script>
{% endif %}
{% endblock %}

View File

@@ -43,15 +43,12 @@
</div>
<script type="text/javascript">
function openRecipe(id, force_external = false) {
function openRecipe(id) {
var link = $('#a_recipe_open');
link.hide();
$('#div_loader').show();
var url = "{% url 'api_get_file_link' recipe_id=12345 %}".replace(/12345/, id);
if (force_external) {
url = "{% url 'api_get_external_file_link' recipe_id=12345 %}".replace(/12345/, id);
}
var url = "{% url 'api_get_external_file_link' recipe_id=12345 %}".replace(/12345/, id);
link.text("{% trans 'Open Recipe' %}");
$('#modal_recipe').modal('show');

View File

@@ -46,6 +46,9 @@
<div>
{{ filter.form.keywords | as_crispy_field }}
</div>
<div>
{{ filter.form.ingredients | as_crispy_field }}
</div>
</div>
</div>
</form>
@@ -60,6 +63,4 @@
</div>
{% endif %}
{% include 'include/recipe_open_modal.html' %}
{% endblock %}

View File

@@ -56,7 +56,7 @@
<td>
{% for mp in days_value %}
<a href="{% url 'edit_plan' mp.pk %}"><i class="fas fa-edit"></i></a>
<a href="#" onclick="openRecipe({{ mp.recipe.id }})">{{ mp.recipe.name }}</a><br/>
<a href="{% url 'view_recipe' mp.recipe.id %}">{{ mp.recipe.name }}</a><br/>
{% endfor %}
</td>
{% endfor %}
@@ -67,6 +67,4 @@
</div>
</div>
{% include 'include/recipe_open_modal.html' %}
{% endblock %}

View File

@@ -9,6 +9,19 @@
{% block extra_head %}
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/pretty-checkbox@3.0/dist/pretty-checkbox.min.css"
integrity="sha384-ICB8i/maQ/5+tGLDUEcswB7Ch+OO9Oj8Z4Ov/Gs0gxqfTgLLkD3F43MhcEJ2x6/D" crossorigin="anonymous">
<!-- prevent weired character stuff escaping the pdf box -->
<style>
.textLayer > span {
color: transparent;
position: absolute;
white-space: pre;
cursor: text;
transform-origin: 0% 0%;
}
</style>
{% endblock %}
{% block content %}
@@ -20,8 +33,10 @@
<div class="col col-md-3 d-print-none" style="text-align: right">
<button class="btn btn-success" onclick="$('#bookmarkModal').modal({'show':true})"><i
class="fas fa-bookmark"></i></button>
<a class="btn btn-warning" href="{% url 'view_shopping' %}?r={{ recipe.pk }}"><i
class="fas fa-shopping-cart"></i></a>
{% if ingredients %}
<a class="btn btn-warning" href="{% url 'view_shopping' %}?r={{ recipe.pk }}"><i
class="fas fa-shopping-cart"></i></a>
{% endif %}
<a class="btn btn-info" href="{% url 'new_plan' %}?recipe={{ recipe.pk }}"><i
class="fas fa-calendar"></i></a>
</div>
@@ -130,30 +145,98 @@
{% if recipe.storage %}
<a href='#' onClick='openRecipe({{ recipe.id }}, true)' class="d-print-none">{% trans 'View external recipe' %}
<i
class="fas fa-external-link-alt"></i></a>
{% endif %}
<div class="row">
{% if recipe.internal %}
<a href='#' onClick='openRecipe({{ recipe.id }})'
class="d-print-none">{% trans 'View external recipe' %} <i class="fas fa-external-link-alt"></i></a>
{% else %}
{% if not recipe.internal %}
<br/>
<br/>
<br/>
<div class="card border-info">
<div class="card-body text-info">
<h5 class="card-title">{% trans 'External recipe' %}</h5>
<p class="card-text">
{% blocktrans %}
This is an external recipe, which means you can only view it by opening the link above.
You can convert this recipe to a fancy recipe by pressing the convert button. The original file
will still be accessible.
{% endblocktrans %}.
<br/>
<br/>
<a href="{% url 'edit_convert_recipe' recipe.pk %}"
class="card-link btn btn-info">{% trans 'Convert now!' %}</a>
</p>
</div>
<div class="col col-12" style="margin-top: 2vh">
<div class="loader" id="id_loader"></div>
<div id="viewerContainer" class="border">
<div id="viewer" class="pdfViewer"></div>
</div>
<div class="alert alert-warning" role="alert" id="id_warning_no_preview" style="display: none">
{% trans 'Cloud not show a file preview. Maybe its not a PDF ?' %}
</div>
</div>
<div class="col col-12" style="margin-top: 2vh">
<div class="card border-info">
<div class="card-body text-info">
<h5 class="card-title">{% trans 'External recipe' %}</h5>
<p class="card-text">
{% blocktrans %}
This is an external recipe, which means you can only view it by opening the link
above.
You can convert this recipe to a fancy recipe by pressing the convert button. The
original
file
will still be accessible.
{% endblocktrans %}.
<br/>
<br/>
<a href="{% url 'edit_convert_recipe' recipe.pk %}"
class="card-link btn btn-info">{% trans 'Convert now!' %}</a>
<a href='#' onClick='openRecipe({{ recipe.id }})'
class="d-print-none btn btn-warning">{% trans 'View external recipe' %} <i
class="fas fa-external-link-alt"></i></a>
</p>
</div>
</div>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.3.200/pdf.min.js"
integrity="sha256-J4Z8Fhj2MITUakMQatkqOVdtqodUlwHtQ/ey6fSsudE="
crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.3.200/pdf_viewer.js"
integrity="sha256-JW7ackRikw8/UM/hHV6vKaZBYc+t2ZQ77sd3LWR8vh8="
crossorigin="anonymous"></script>
<script type="text/javascript">
var url = "{% url 'api_get_recipe_file' recipe_id=12345 %}".replace(/12345/, {{ recipe.id }});
$('#viewerContainer').hide();
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function () {
if (this.readyState === 4 && this.status === 200) {
var base64Pdf = atob(this.responseText);
$('#id_loader').hide();
$('#viewerContainer').show();
var container = document.getElementById("viewerContainer");
var pdfViewer = new pdfjsViewer.PDFViewer({
container: container,
});
document.addEventListener("pagesinit", function () {
// We can use pdfViewer now, e.g. let's change default scale.
pdfViewer.currentScaleValue = "page-width";
});
var loadingTask = pdfjsLib.getDocument({
data: base64Pdf
});
loadingTask.promise.then(function (pdfDocument) {
// Document loaded, specifying document for the viewer and
// the (optional) linkService.
pdfViewer.setDocument(pdfDocument);
});
}
};
xhttp.open("GET", url, true);
xhttp.send();
</script>
{% endif %}
</div>
{% endif %}

View File

@@ -22,14 +22,22 @@
<br/>
<br/>
<button class="btn btn-success" onclick="copy()"><i class="far fa-copy"></i></button>
<div class="row">
<div class="col col-md-12">
<!--// @formatter:off-->
<textarea id="id_list" style="height: 50vh" class="form-control">{% for i in ingredients %}- [ ] {{ i.amount.normalize }} {{ i.unit }} {{ i.ingredient.name }}&#10;{% endfor %}</textarea>
<textarea id="id_list" class="form-control" rows="{{ ingredients|length|add:1 }}">{% for i in ingredients %}{% if markdown_format %}- [ ]{% endif %} {{ i.amount.normalize }} {{ i.unit }} {{ i.ingredient.name }}&#10;{% endfor %}</textarea>
<!--// @formatter:on-->
</div>
</div>
<br/>
<div class="row">
<div class="col col-md-12 text-center">
<button class="btn btn-success" onclick="copy()" style="width: 15vw" data-toggle="tooltip"
data-placement="top" title="{% trans 'Copy list to clipboard' %}" id="id_btn_copy" onmouseout="resetTooltip()"><i
class="far fa-copy"></i></button>
</div>
</div>
<script type="text/javascript">
function copy() {
@@ -37,8 +45,20 @@
list.select();
$('#id_btn_copy').attr('data-original-title','{% trans 'Copied!' %}').tooltip('show');
document.execCommand("copy");
}
function resetTooltip() {
setTimeout(function () {
$('#id_btn_copy').attr('data-original-title','{% trans 'Copy list to clipboard' %}');
}, 300);
}
$(function () {
$('[data-toggle="tooltip"]').tooltip()
})
</script>
{% endblock %}

View File

@@ -60,8 +60,8 @@ urlpatterns = [
path('data/sync/wait', data.sync_wait, name='data_sync_wait'),
path('data/statistics', data.statistics, name='data_stats'),
path('api/get_file_link/<int:recipe_id>/', api.get_file_link, name='api_get_file_link'),
path('api/get_external_file_link/<int:recipe_id>/', api.get_external_file_link, name='api_get_external_file_link'),
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'),

View File

@@ -1,5 +1,5 @@
from django.contrib import messages
from django.http import HttpResponse
from django.http import HttpResponse, FileResponse
from django.urls import reverse
from django.utils.translation import gettext as _
from django.contrib.auth.decorators import login_required
@@ -10,40 +10,40 @@ from cookbook.provider.dropbox import Dropbox
from cookbook.provider.nextcloud import Nextcloud
@login_required
def get_file_link(request, recipe_id):
recipe = Recipe.objects.get(id=recipe_id)
def get_recipe_provider(recipe):
if recipe.storage.method == Storage.DROPBOX:
return Dropbox
elif recipe.storage.method == Storage.NEXTCLOUD:
return Nextcloud
else:
raise Exception('Provider not implemented')
if recipe.internal:
return HttpResponse(reverse('view_recipe', args=[recipe_id]))
if recipe.storage.method == Storage.DROPBOX: # TODO move to central location (as all provider related functions)
if recipe.link == "":
recipe.link = Dropbox.get_share_link(recipe) # TODO response validation
recipe.save()
if recipe.storage.method == Storage.NEXTCLOUD:
if recipe.link == "":
recipe.link = Nextcloud.get_share_link(recipe) # TODO response validation
recipe.save()
return HttpResponse(recipe.link)
def update_recipe_links(recipe):
if not recipe.link:
recipe.link = get_recipe_provider(recipe).get_share_link(recipe) # TODO response validation in apis
recipe.save()
@login_required
def get_external_file_link(request, recipe_id):
recipe = Recipe.objects.get(id=recipe_id)
if recipe.storage.method == Storage.DROPBOX: # TODO move to central location (as all provider related functions)
if recipe.link == "":
recipe.link = Dropbox.get_share_link(recipe) # TODO response validation
recipe.save()
if recipe.storage.method == Storage.NEXTCLOUD:
if recipe.link == "":
recipe.link = Nextcloud.get_share_link(recipe) # TODO response validation
recipe.save()
if not recipe.link:
update_recipe_links(recipe)
return HttpResponse(recipe.link)
@login_required
def get_recipe_file(request, recipe_id):
recipe = Recipe.objects.get(id=recipe_id)
if not recipe.cors_link:
update_recipe_links(recipe)
return HttpResponse(get_recipe_provider(recipe).get_base64_file(recipe))
@login_required
def sync_all(request):
monitors = Sync.objects.filter(active=True)

View File

@@ -15,7 +15,7 @@ from django.utils.translation import gettext as _, ngettext
from django.views.generic import UpdateView, DeleteView
from cookbook.forms import ExternalRecipeForm, KeywordForm, StorageForm, SyncForm, InternalRecipeForm, CommentForm, \
MealPlanForm, UnitMergeForm
MealPlanForm, UnitMergeForm, IngredientMergeForm
from cookbook.models import Recipe, Sync, Keyword, RecipeImport, Storage, Comment, RecipeIngredient, RecipeBook, \
RecipeBookEntry, MealPlan, Unit, Ingredient
from cookbook.provider.dropbox import Dropbox
@@ -302,23 +302,41 @@ class RecipeUpdate(LoginRequiredMixin, UpdateView):
@login_required
def edit_ingredients(request):
if request.method == "POST":
form = UnitMergeForm(request.POST, prefix=UnitMergeForm.prefix)
if form.is_valid():
new_unit = form.cleaned_data['new_unit']
old_unit = form.cleaned_data['old_unit']
ingredients = RecipeIngredient.objects.filter(unit=old_unit).all()
for i in ingredients:
success = False
units_form = UnitMergeForm(request.POST, prefix=UnitMergeForm.prefix)
if units_form.is_valid():
new_unit = units_form.cleaned_data['new_unit']
old_unit = units_form.cleaned_data['old_unit']
recipe_ingredients = RecipeIngredient.objects.filter(unit=old_unit).all()
for i in recipe_ingredients:
i.unit = new_unit
i.save()
old_unit.delete()
success = True
messages.add_message(request, messages.SUCCESS, _('Units merged!'))
else:
messages.add_message(request, messages.WARNING, _('There was an error in your form.'))
else:
form = UnitMergeForm()
return render(request, 'forms/ingredients.html', {'form': form})
ingredients_form = IngredientMergeForm(request.POST, prefix=IngredientMergeForm.prefix)
if ingredients_form.is_valid():
new_ingredient = ingredients_form.cleaned_data['new_ingredient']
old_ingredient = ingredients_form.cleaned_data['old_ingredient']
recipe_ingredients = RecipeIngredient.objects.filter(ingredient=old_ingredient).all()
for i in recipe_ingredients:
i.ingredient = new_ingredient
i.save()
old_ingredient.delete()
success = True
messages.add_message(request, messages.SUCCESS, _('Ingredients merged!'))
if success:
units_form = UnitMergeForm()
ingredients_form = IngredientMergeForm()
else:
units_form = UnitMergeForm()
ingredients_form = IngredientMergeForm()
return render(request, 'forms/ingredients.html', {'units_form': units_form, 'ingredients_form': ingredients_form})
# Generic Delete views

View File

@@ -117,10 +117,13 @@ def meal_plan(request):
@login_required
def shopping_list(request):
markdown_format = True
if request.method == "POST":
form = RecipeForm(request.POST)
form = ShoppingForm(request.POST)
if form.is_valid():
recipes = form.cleaned_data['recipe']
markdown_format = form.cleaned_data['markdown_format']
else:
recipes = []
else:
@@ -132,15 +135,23 @@ def shopping_list(request):
if Recipe.objects.filter(pk=int(r)).exists():
recipes.append(int(r))
form = RecipeForm(initial={'recipe': recipes})
form = ShoppingForm(initial={'recipe': recipes})
ingredients = []
for r in recipes:
for i in RecipeIngredient.objects.filter(recipe=r).all():
ingredients.append(i)
for ri in RecipeIngredient.objects.filter(recipe=r).all():
index = None
for x, ig in enumerate(ingredients):
if ri.ingredient == ig.ingredient and ri.unit == ig.unit:
index = x
return render(request, 'shopping_list.html', {'ingredients': ingredients, 'recipes': recipes, 'form': form})
if index:
ingredients[index].amount = ingredients[index].amount + ri.amount
else:
ingredients.append(ri)
return render(request, 'shopping_list.html', {'ingredients': ingredients, 'recipes': recipes, 'form': form, 'markdown_format': markdown_format})
@login_required

View File

@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2020-01-13 12:08+0100\n"
"POT-Creation-Date: 2020-02-18 23:20+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -18,10 +18,10 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: recipes/settings.py:136
#: .\recipes\settings.py:136
msgid "German"
msgstr "Deutsch"
#: recipes/settings.py:137
#: .\recipes\settings.py:137
msgid "English"
msgstr "Englisch"