From a1b8f736c2b64c9e0860be7b45120619bdc2a590 Mon Sep 17 00:00:00 2001 From: Thomas Preece Date: Mon, 20 Mar 2023 18:18:11 +0000 Subject: [PATCH 01/10] Improve parsing of OpenEats imports --- cookbook/integration/openeats.py | 62 +++++++++++++++++++++++++++++--- docs/features/import_export.md | 4 ++- 2 files changed, 60 insertions(+), 6 deletions(-) diff --git a/cookbook/integration/openeats.py b/cookbook/integration/openeats.py index a8485f050..519d3f4ff 100644 --- a/cookbook/integration/openeats.py +++ b/cookbook/integration/openeats.py @@ -2,24 +2,48 @@ import json from cookbook.helper.ingredient_parser import IngredientParser from cookbook.integration.integration import Integration -from cookbook.models import Ingredient, Recipe, Step +from cookbook.models import Ingredient, Recipe, Step, Keyword, Comment, CookLog class OpenEats(Integration): def get_recipe_from_file(self, file): - recipe = Recipe.objects.create(name=file['name'].strip(), created_by=self.request.user, internal=True, + recipe = Recipe.objects.create(name=file['name'].strip(), description=file['info'], created_by=self.request.user, internal=True, servings=file['servings'], space=self.request.space, waiting_time=file['cook_time'], working_time=file['prep_time']) instructions = '' - if file["info"] != '': - instructions += file["info"] if file["directions"] != '': instructions += file["directions"] if file["source"] != '': - instructions += file["source"] + instructions += f'\nRecipe source: [{file["source"]}]({file["source"]})' + + cuisine_keyword, created = Keyword.objects.get_or_create(name="Cuisine", space=self.request.space) + if file["cuisine"] != '': + keyword, created = Keyword.objects.get_or_create(name=file["cuisine"].strip(), space=self.request.space) + if created: + keyword.move(cuisine_keyword, pos="last-child") + recipe.keywords.add(keyword) + + course_keyword, created = Keyword.objects.get_or_create(name="Course", space=self.request.space) + if file["course"] != '': + keyword, created = Keyword.objects.get_or_create(name=file["course"].strip(), space=self.request.space) + if created: + keyword.move(course_keyword, pos="last-child") + recipe.keywords.add(keyword) + + for tag in file["tags"]: + keyword, created = Keyword.objects.get_or_create(name=tag.strip(), space=self.request.space) + recipe.keywords.add(keyword) + + for comment in file['comments']: + Comment.objects.create(recipe=recipe, text=comment['text'], created_by=self.request.user) + CookLog.objects.create(recipe=recipe, rating=comment['rating'], created_by=self.request.user, space=self.request.space) + + if file["photo"] != '': + recipe.image = f'recipes/openeats-import/{file["photo"]}' + recipe.save() step = Step.objects.create(instruction=instructions, space=self.request.space,) @@ -38,6 +62,9 @@ class OpenEats(Integration): recipe_json = json.loads(file.read()) recipe_dict = {} ingredient_group_dict = {} + cuisine_group_dict = {} + course_group_dict = {} + tag_group_dict = {} for o in recipe_json: if o['model'] == 'recipe.recipe': @@ -50,11 +77,27 @@ class OpenEats(Integration): 'cook_time': o['fields']['cook_time'], 'servings': o['fields']['servings'], 'ingredients': [], + 'photo': o['fields']['photo'], + 'cuisine': o['fields']['cuisine'], + 'course': o['fields']['course'], + 'tags': o['fields']['tags'], + 'comments': [], } if o['model'] == 'ingredient.ingredientgroup': ingredient_group_dict[o['pk']] = o['fields']['recipe'] + if o['model'] == 'recipe_groups.cuisine': + cuisine_group_dict[o['pk']] = o['fields']['title'] + if o['model'] == 'recipe_groups.course': + course_group_dict[o['pk']] = o['fields']['title'] + if o['model'] == 'recipe_groups.tag': + tag_group_dict[o['pk']] = o['fields']['title'] for o in recipe_json: + if o['model'] == 'rating.rating': + recipe_dict[o['fields']['recipe']]["comments"].append({ + "text": o['fields']['comment'], + "rating": o['fields']['rating'] + }) if o['model'] == 'ingredient.ingredient': ingredient = { 'food': o['fields']['title'], @@ -63,6 +106,15 @@ class OpenEats(Integration): } recipe_dict[ingredient_group_dict[o['fields']['ingredient_group']]]['ingredients'].append(ingredient) + for k, r in recipe_dict.items(): + if r["cuisine"] in cuisine_group_dict: + r["cuisine"] = cuisine_group_dict[r["cuisine"]] + if r["course"] in course_group_dict: + r["course"] = course_group_dict[r["course"]] + for index in range(len(r["tags"])): + if r["tags"][index] in tag_group_dict: + r["tags"][index] = tag_group_dict[r["tags"][index]] + return list(recipe_dict.values()) def get_file_from_recipe(self, recipe): diff --git a/docs/features/import_export.md b/docs/features/import_export.md index d775cf9fc..a2fa3040f 100644 --- a/docs/features/import_export.md +++ b/docs/features/import_export.md @@ -178,7 +178,7 @@ This zip file can simply be imported into Tandoor. OpenEats does not provide any way to export the data using the interface. Luckily it is relatively easy to export it from the command line. You need to run the command `python manage.py dumpdata recipe ingredient` inside of the application api container. If you followed the default installation method you can use the following command `docker-compose -f docker-prod.yml run --rm --entrypoint 'sh' api ./manage.py dumpdata recipe ingredient`. -This command might also work `docker exec -it openeats_api_1 ./manage.py dumpdata recipe ingredient > recipe_ingredients.json` +This command might also work `docker exec -it openeats_api_1 ./manage.py dumpdata recipe ingredient rating recipe_groups > recipe_ingredients.json` Store the outputted json string in a `.json` file and simply import it using the importer. The file should look something like this ```json @@ -216,6 +216,8 @@ Store the outputted json string in a `.json` file and simply import it using the ``` +To import your images you'll need to create the folder `openeats-import` in your Tandoor's `recipes` media folder (which is usually found inside `/opt/recipes/mediafiles`). After that you'll need to copy the `/code/site-media/upload` folder from the openeats API docker container to the `openeats` folder you created. You should now have the file path `/opt/recipes/mediafiles/recipes/openeats-import/upload/...` in Tandoor. + ## Plantoeat Plan to eat allows you to export a text file containing all your recipes. Simply upload that text file to Tandoor to import all recipes From 39ab2eb10f3da4c731ac09ecdfb19b434fb4ba20 Mon Sep 17 00:00:00 2001 From: Gabe Cook Date: Sat, 25 Mar 2023 01:02:28 -0500 Subject: [PATCH 02/10] Skip Docker push for Dependabot PRs --- .github/workflows/build-docker.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build-docker.yml b/.github/workflows/build-docker.yml index 629dc2451..072c86fa4 100644 --- a/.github/workflows/build-docker.yml +++ b/.github/workflows/build-docker.yml @@ -69,11 +69,13 @@ jobs: uses: docker/setup-buildx-action@v2 - name: Login to Docker Hub uses: docker/login-action@v2 + if: github.secret_source == 'Actions' with: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} - name: Login to GitHub Container Registry uses: docker/login-action@v2 + if: github.secret_source == 'Actions' with: registry: ghcr.io username: ${{ github.actor }} @@ -100,7 +102,7 @@ jobs: context: . file: ${{ matrix.dockerfile }} pull: true - push: true + push: ${{ github.secret_source == 'Actions' }} platforms: ${{ matrix.platforms }} tags: ${{ steps.meta.outputs.tags }} labels: ${{ steps.meta.outputs.labels }} From e739c4d6273884626e7476cb7222935653932f71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mat=C4=9Bj=20Kubla?= Date: Sat, 25 Mar 2023 11:32:45 +0000 Subject: [PATCH 03/10] Added translation using Weblate (Czech) --- vue/src/locales/cs.json | 482 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 482 insertions(+) create mode 100644 vue/src/locales/cs.json diff --git a/vue/src/locales/cs.json b/vue/src/locales/cs.json new file mode 100644 index 000000000..0ff8bafd9 --- /dev/null +++ b/vue/src/locales/cs.json @@ -0,0 +1,482 @@ +{ + "warning_feature_beta": "", + "err_fetching_resource": "", + "err_creating_resource": "", + "err_updating_resource": "", + "err_deleting_resource": "", + "err_deleting_protected_resource": "", + "err_moving_resource": "", + "err_merging_resource": "", + "success_fetching_resource": "", + "success_creating_resource": "", + "success_updating_resource": "", + "success_deleting_resource": "", + "success_moving_resource": "", + "success_merging_resource": "", + "file_upload_disabled": "", + "warning_space_delete": "", + "food_inherit_info": "", + "facet_count_info": "", + "step_time_minutes": "", + "confirm_delete": "", + "import_running": "", + "all_fields_optional": "", + "convert_internal": "", + "show_only_internal": "", + "show_split_screen": "", + "Log_Recipe_Cooking": "", + "External_Recipe_Image": "", + "Add_to_Shopping": "", + "Add_to_Plan": "", + "Step_start_time": "", + "Sort_by_new": "", + "Table_of_Contents": "", + "Recipes_per_page": "", + "Show_as_header": "", + "Hide_as_header": "", + "Add_nutrition_recipe": "", + "Remove_nutrition_recipe": "", + "Copy_template_reference": "", + "Save_and_View": "", + "Manage_Books": "", + "Meal_Plan": "", + "Select_Book": "", + "Select_File": "", + "Recipe_Image": "", + "Import_finished": "", + "View_Recipes": "", + "Log_Cooking": "", + "New_Recipe": "", + "Url_Import": "", + "Reset_Search": "", + "Recently_Viewed": "", + "Load_More": "", + "New_Keyword": "", + "Delete_Keyword": "", + "Edit_Keyword": "", + "Edit_Recipe": "", + "Move_Keyword": "", + "Merge_Keyword": "", + "Hide_Keywords": "", + "Hide_Recipes": "", + "Move_Up": "", + "Move_Down": "", + "Step_Name": "", + "Step_Type": "", + "Make_Header": "", + "Make_Ingredient": "", + "Amount": "", + "Enable_Amount": "", + "Disable_Amount": "", + "Ingredient Editor": "", + "Description_Replace": "", + "Instruction_Replace": "", + "Auto_Sort": "", + "Auto_Sort_Help": "", + "Private_Recipe": "", + "Private_Recipe_Help": "", + "reusable_help_text": "", + "Add_Step": "", + "Keywords": "", + "Books": "", + "Proteins": "", + "Fats": "", + "Carbohydrates": "", + "Calories": "", + "Energy": "", + "Nutrition": "", + "Date": "", + "Share": "", + "Automation": "", + "Parameter": "", + "Export": "", + "Copy": "", + "Rating": "", + "Close": "", + "Cancel": "", + "Link": "", + "Add": "", + "New": "", + "Note": "", + "Success": "", + "Failure": "", + "Protected": "", + "Ingredients": "", + "Supermarket": "", + "Categories": "", + "Category": "", + "Selected": "", + "min": "", + "Servings": "", + "Waiting": "", + "Preparation": "", + "External": "", + "Size": "", + "Files": "", + "File": "", + "Edit": "", + "Image": "", + "Delete": "", + "Open": "", + "Ok": "", + "Save": "", + "Step": "", + "Search": "", + "Import": "", + "Print": "", + "Settings": "", + "or": "", + "and": "", + "Information": "", + "Download": "", + "Create": "", + "Search Settings": "", + "View": "", + "Recipes": "", + "Move": "", + "Merge": "", + "Parent": "", + "Copy Link": "", + "Copy Token": "", + "delete_confirmation": "", + "move_confirmation": "", + "merge_confirmation": "", + "create_rule": "", + "move_selection": "", + "merge_selection": "", + "Root": "", + "Ignore_Shopping": "", + "Shopping_Category": "", + "Shopping_Categories": "", + "Edit_Food": "", + "Move_Food": "", + "New_Food": "", + "Hide_Food": "", + "Food_Alias": "", + "Unit_Alias": "", + "Keyword_Alias": "", + "Delete_Food": "", + "No_ID": "", + "Meal_Plan_Days": "", + "merge_title": "", + "move_title": "", + "Food": "", + "Original_Text": "", + "Recipe_Book": "", + "del_confirmation_tree": "", + "delete_title": "", + "create_title": "", + "edit_title": "", + "Name": "", + "Type": "", + "Description": "", + "Recipe": "", + "tree_root": "", + "Icon": "", + "Unit": "", + "Decimals": "", + "Default_Unit": "", + "No_Results": "", + "New_Unit": "", + "Create_New_Shopping Category": "", + "Create_New_Food": "", + "Create_New_Keyword": "", + "Create_New_Unit": "", + "Create_New_Meal_Type": "", + "Create_New_Shopping_Category": "", + "and_up": "", + "and_down": "", + "Instructions": "", + "Unrated": "", + "Automate": "", + "Empty": "", + "Key_Ctrl": "", + "Key_Shift": "", + "Time": "", + "Text": "", + "Shopping_list": "", + "Added_by": "", + "Added_on": "", + "AddToShopping": "", + "IngredientInShopping": "", + "NotInShopping": "", + "OnHand": "", + "FoodOnHand": "", + "FoodNotOnHand": "", + "Undefined": "", + "Create_Meal_Plan_Entry": "", + "Edit_Meal_Plan_Entry": "", + "Title": "", + "Week": "", + "Month": "", + "Year": "", + "Planner": "", + "Planner_Settings": "", + "Period": "", + "Plan_Period_To_Show": "", + "Periods": "", + "Plan_Show_How_Many_Periods": "", + "Starting_Day": "", + "Meal_Types": "", + "Meal_Type": "", + "New_Entry": "", + "Clone": "", + "Drag_Here_To_Delete": "", + "Meal_Type_Required": "", + "Title_or_Recipe_Required": "", + "Color": "", + "New_Meal_Type": "", + "Use_Fractions": "", + "Use_Fractions_Help": "", + "AddFoodToShopping": "", + "RemoveFoodFromShopping": "", + "DeleteShoppingConfirm": "", + "IgnoredFood": "", + "Add_Servings_to_Shopping": "", + "Week_Numbers": "", + "Show_Week_Numbers": "", + "Export_As_ICal": "", + "Export_To_ICal": "", + "Cannot_Add_Notes_To_Shopping": "", + "Added_To_Shopping_List": "", + "Shopping_List_Empty": "", + "Next_Period": "", + "Previous_Period": "", + "Current_Period": "", + "Next_Day": "", + "Previous_Day": "", + "Inherit": "", + "InheritFields": "", + "FoodInherit": "", + "ShowUncategorizedFood": "", + "GroupBy": "", + "Language": "", + "Theme": "", + "SupermarketCategoriesOnly": "", + "MoveCategory": "", + "CountMore": "", + "IgnoreThis": "", + "DelayFor": "", + "Warning": "", + "NoCategory": "", + "InheritWarning": "", + "ShowDelayed": "", + "Completed": "", + "OfflineAlert": "", + "shopping_share": "", + "shopping_auto_sync": "", + "one_url_per_line": "", + "mealplan_autoadd_shopping": "", + "mealplan_autoexclude_onhand": "", + "mealplan_autoinclude_related": "", + "default_delay": "", + "plan_share_desc": "", + "shopping_share_desc": "", + "shopping_auto_sync_desc": "", + "mealplan_autoadd_shopping_desc": "", + "mealplan_autoexclude_onhand_desc": "", + "mealplan_autoinclude_related_desc": "", + "default_delay_desc": "", + "filter_to_supermarket": "", + "Coming_Soon": "", + "Auto_Planner": "", + "New_Cookbook": "", + "Hide_Keyword": "", + "Hour": "", + "Hours": "", + "Day": "", + "Days": "", + "Second": "", + "Seconds": "", + "Clear": "", + "Users": "", + "Invites": "", + "err_move_self": "", + "nothing": "", + "err_merge_self": "", + "show_sql": "", + "filter_to_supermarket_desc": "", + "CategoryName": "", + "SupermarketName": "", + "CategoryInstruction": "", + "shopping_recent_days_desc": "", + "shopping_recent_days": "", + "download_pdf": "", + "download_csv": "", + "csv_delim_help": "", + "csv_delim_label": "", + "SuccessClipboard": "", + "copy_to_clipboard": "", + "csv_prefix_help": "", + "csv_prefix_label": "", + "copy_markdown_table": "", + "in_shopping": "", + "DelayUntil": "", + "Pin": "", + "Unpin": "", + "PinnedConfirmation": "", + "UnpinnedConfirmation": "", + "mark_complete": "", + "QuickEntry": "", + "shopping_add_onhand_desc": "", + "shopping_add_onhand": "", + "related_recipes": "", + "today_recipes": "", + "sql_debug": "", + "remember_search": "", + "remember_hours": "", + "tree_select": "", + "OnHand_help": "", + "ignore_shopping_help": "", + "shopping_category_help": "", + "food_recipe_help": "", + "Foods": "", + "Account": "", + "Cosmetic": "", + "API": "", + "enable_expert": "", + "expert_mode": "", + "simple_mode": "", + "advanced": "", + "fields": "", + "show_keywords": "", + "show_foods": "", + "show_books": "", + "show_rating": "", + "show_units": "", + "show_filters": "", + "not": "", + "save_filter": "", + "filter_name": "", + "left_handed": "", + "left_handed_help": "", + "Custom Filter": "", + "shared_with": "", + "sort_by": "", + "asc": "", + "desc": "", + "date_viewed": "", + "last_cooked": "", + "times_cooked": "", + "date_created": "", + "show_sortby": "", + "search_rank": "", + "make_now": "", + "recipe_filter": "", + "book_filter_help": "", + "review_shopping": "", + "view_recipe": "", + "copy_to_new": "", + "recipe_name": "", + "paste_ingredients_placeholder": "", + "paste_ingredients": "", + "ingredient_list": "", + "explain": "", + "filter": "", + "Website": "", + "App": "", + "Message": "", + "Bookmarklet": "", + "Sticky_Nav": "", + "Sticky_Nav_Help": "", + "Nav_Color": "", + "Nav_Color_Help": "", + "Use_Kj": "", + "Comments_setting": "", + "click_image_import": "", + "no_more_images_found": "", + "import_duplicates": "", + "paste_json": "", + "Click_To_Edit": "", + "search_no_recipes": "", + "search_import_help_text": "", + "search_create_help_text": "", + "warning_duplicate_filter": "", + "reset_children": "", + "reset_children_help": "", + "reset_food_inheritance": "", + "reset_food_inheritance_info": "", + "substitute_help": "", + "substitute_siblings_help": "", + "substitute_children_help": "", + "substitute_siblings": "", + "substitute_children": "", + "SubstituteOnHand": "", + "ChildInheritFields": "", + "ChildInheritFields_help": "", + "InheritFields_help": "", + "show_ingredient_overview": "", + "Ingredient Overview": "", + "last_viewed": "", + "created_on": "", + "updatedon": "", + "Imported_From": "", + "advanced_search_settings": "", + "nothing_planned_today": "", + "no_pinned_recipes": "", + "Planned": "", + "Pinned": "", + "Imported": "", + "Quick actions": "", + "Ratings": "", + "Internal": "", + "Units": "", + "Manage_Emails": "", + "Change_Password": "", + "Social_Authentication": "", + "Random Recipes": "", + "parameter_count": "", + "select_keyword": "", + "add_keyword": "", + "select_file": "", + "select_recipe": "", + "select_unit": "", + "select_food": "", + "remove_selection": "", + "empty_list": "", + "Select": "", + "Supermarkets": "", + "User": "", + "Username": "", + "First_name": "", + "Last_name": "", + "Keyword": "", + "Advanced": "", + "Page": "", + "Single": "", + "Multiple": "", + "Reset": "", + "Disabled": "", + "Disable": "", + "Options": "", + "Create Food": "", + "create_food_desc": "", + "additional_options": "", + "Importer_Help": "", + "Documentation": "", + "Select_App_To_Import": "", + "Import_Supported": "", + "Export_Supported": "", + "Import_Not_Yet_Supported": "", + "Export_Not_Yet_Supported": "", + "Import_Result_Info": "", + "Recipes_In_Import": "", + "Toggle": "", + "Import_Error": "", + "Warning_Delete_Supermarket_Category": "", + "New_Supermarket": "", + "New_Supermarket_Category": "", + "Are_You_Sure": "", + "Valid Until": "", + "Split_All_Steps": "", + "Combine_All_Steps": "", + "Plural": "", + "plural_short": "", + "Use_Plural_Unit_Always": "", + "Use_Plural_Unit_Simple": "", + "Use_Plural_Food_Always": "", + "Use_Plural_Food_Simple": "", + "plural_usage_info": "", + "Create Recipe": "", + "Import Recipe": "" +} From 2a5fc22dd71925666b23fed7a85475d6a78e2ec3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mat=C4=9Bj=20Kubla?= Date: Sat, 25 Mar 2023 11:30:10 +0000 Subject: [PATCH 04/10] Translated using Weblate (Czech) Currently translated at 100.0% (362 of 362 strings) Translation: Tandoor/Recipes Backend Translate-URL: http://translate.tandoor.dev/projects/tandoor/recipes-backend/cs/ --- cookbook/locale/cs/LC_MESSAGES/django.po | 62 +++++++++++++++--------- 1 file changed, 38 insertions(+), 24 deletions(-) diff --git a/cookbook/locale/cs/LC_MESSAGES/django.po b/cookbook/locale/cs/LC_MESSAGES/django.po index aa14acdf8..18f4d74f6 100644 --- a/cookbook/locale/cs/LC_MESSAGES/django.po +++ b/cookbook/locale/cs/LC_MESSAGES/django.po @@ -11,8 +11,8 @@ msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2021-02-09 18:01+0100\n" -"PO-Revision-Date: 2023-01-08 17:55+0000\n" -"Last-Translator: Joachim Weber \n" +"PO-Revision-Date: 2023-03-25 11:32+0000\n" +"Last-Translator: Matěj Kubla \n" "Language-Team: Czech \n" "Language: cs\n" @@ -553,7 +553,7 @@ msgstr "Cesta musí být v následujícím formátu" #: .\cookbook\templates\batch\monitor.html:27 msgid "Sync Now!" -msgstr "Zahájit synchronizaci" +msgstr "Zahájit synchronizaci!" #: .\cookbook\templates\batch\waiting.html:4 #: .\cookbook\templates\batch\waiting.html:10 @@ -1036,7 +1036,7 @@ msgstr "Tento text je kurzívou" #: .\cookbook\templates\markdown_info.html:61 #: .\cookbook\templates\markdown_info.html:77 msgid "Blockquotes are also possible" -msgstr "Lze použít i kvotace " +msgstr "Lze použít i kvotace" #: .\cookbook\templates\markdown_info.html:84 msgid "Lists" @@ -1106,8 +1106,8 @@ msgid "" "rel=\"noreferrer noopener\" target=\"_blank\">this one." msgstr "" "Ruční vytváření tabulek pomocí značek je složité. Doporučujeme použít " -"například tento tabulkový editor." +"například tento tabulkový editor." #: .\cookbook\templates\markdown_info.html:155 #: .\cookbook\templates\markdown_info.html:157 @@ -1256,22 +1256,36 @@ msgid "" " " msgstr "" "\n" -"

Modul jídelníčku umožňuje plánovat jídlo pomocí receptů i poznámek.

\n" -"

Jednoduše vyberte recept ze seznamu naposledy navštívených receptů, nebo ho vyhledejte\n" -" s přetáhněte na požadovaný den v rozvrhu. Můžete také přidat poznámku s popiskem\n" -" a poté přetáhnout recept pro vytvoření plánu s vlatními popisky. Vytvořením samotné poznámky\n" -" je možné přetažením pole poznámky do rozvrhu.

\n" -"

Kliknutím na recept zobrazíte detailní náhled. Odtud lze také přidat položky\n" -" do nákupního seznamu. Do nákupního seznamu můžete také přidat všechny recepty na daný den\n" -" kliknutím na ikonu nákupního košíku na horní straně tabulky.

\n" -"

V běžném případě se jídelníček plánuje hromadně, proto můžete v nastavení definovat\n" -" se kterými uživateli si přejete jídelníčky sdílet.\n" +"

Modul jídelníčku umožňuje plánovat jídlo " +"pomocí receptů i poznámek.

\n" +"

Jednoduše vyberte recept ze seznamu naposledy " +"navštívených receptů, nebo ho vyhledejte\n" +" s přetáhněte na požadovaný den v rozvrhu. " +"Můžete také přidat poznámku s popiskem\n" +" a poté přetáhnout recept pro vytvoření plánu " +"s vlatními popisky. Vytvořením samotné poznámky\n" +" je možné přetažením pole poznámky do " +"rozvrhu.

\n" +"

Kliknutím na recept zobrazíte detailní " +"náhled. Odtud lze také přidat položky\n" +" do nákupního seznamu. Do nákupního seznamu " +"můžete také přidat všechny recepty na daný den\n" +" kliknutím na ikonu nákupního košíku na horní " +"straně tabulky.

\n" +"

V běžném případě se jídelníček plánuje " +"hromadně, proto můžete v nastavení definovat\n" +" se kterými uživateli si přejete jídelníčky " +"sdílet.\n" "

\n" -"

Můžete také upravovat typy jídel, které si přejete naplánovat. Pokud budete sdílet jídelníček \n" +"

Můžete také upravovat typy jídel, které si " +"přejete naplánovat. Pokud budete sdílet jídelníček \n" " s někým, kdo\n" -" má přidána jiná jídla, jeho typy jídel se objeví i ve vašem seznamu. Pro předcházení\n" +" má přidána jiná jídla, jeho typy jídel se " +"objeví i ve vašem seznamu. Pro předcházení\n" " duplicitám (např. Ostatní, Jiná)\n" -" pojmenujte váš typ jídla stejně, jako uživatel se kterým své seznamy sdílíte. Tím budou seznamy sloučeny.

\n" +" pojmenujte váš typ jídla stejně, jako " +"uživatel se kterým své seznamy sdílíte. Tím budou seznamy\n" +" sloučeny.

\n" " " #: .\cookbook\templates\meal_plan_entry.html:6 @@ -1333,12 +1347,12 @@ msgstr "Obrázek receptu" #: .\cookbook\templates\recipes_table.html:46 #: .\cookbook\templates\url_import.html:55 msgid "Preparation time ca." -msgstr "Doba přípravy cca" +msgstr "Doba přípravy cca." #: .\cookbook\templates\recipes_table.html:52 #: .\cookbook\templates\url_import.html:60 msgid "Waiting time ca." -msgstr "Doba čekání cca" +msgstr "Doba čekání cca." #: .\cookbook\templates\recipes_table.html:55 msgid "External" @@ -1386,7 +1400,7 @@ msgid "" " in the following examples:" msgstr "" "Použijte tajný klíč jako autorizační hlavičku definovanou slovním klíčem, " -"jak je uvedeno v následujících příkladech." +"jak je uvedeno v následujících příkladech:" #: .\cookbook\templates\settings.html:94 msgid "or" @@ -1808,7 +1822,7 @@ msgstr "Import není pro tohoto poskytovatele implementován!" #: .\cookbook\views\import_export.py:58 msgid "Exporting is not implemented for this provider" -msgstr "Eport není pro tohoto poskytovatele implementován!" +msgstr "Export není pro tohoto poskytovatele implementován!" #: .\cookbook\views\lists.py:42 msgid "Import Log" @@ -1840,7 +1854,7 @@ msgstr "Komentář uložen!" #: .\cookbook\views\views.py:152 msgid "This recipe is already linked to the book!" -msgstr "Tento recept už v kuchařce existuje." +msgstr "Tento recept už v kuchařce existuje!" #: .\cookbook\views\views.py:158 msgid "Bookmark saved!" From 0ba2fa296a6149abe254645e74da9a76a8b62d29 Mon Sep 17 00:00:00 2001 From: vabene1111 Date: Tue, 28 Mar 2023 15:42:34 +0200 Subject: [PATCH 05/10] added token auth for share link endpoint --- cookbook/views/api.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/cookbook/views/api.py b/cookbook/views/api.py index 97b3e7c1f..2a604f211 100644 --- a/cookbook/views/api.py +++ b/cookbook/views/api.py @@ -1401,17 +1401,17 @@ def sync_all(request): return redirect('list_recipe_import') +@api_view(['GET']) +# @schema(AutoSchema()) #TODO add proper schema +@permission_classes([CustomIsUser & CustomTokenHasReadWriteScope]) def share_link(request, pk): - if request.user.is_authenticated: - if request.space.allow_sharing and has_group_permission(request.user, ('user',)): - recipe = get_object_or_404(Recipe, pk=pk, space=request.space) - link = ShareLink.objects.create(recipe=recipe, created_by=request.user, space=request.space) - return JsonResponse({'pk': pk, 'share': link.uuid, - 'link': request.build_absolute_uri(reverse('view_recipe', args=[pk, link.uuid]))}) - else: - return JsonResponse({'error': 'sharing_disabled'}, status=403) - - return JsonResponse({'error': 'not_authenticated'}, status=403) + if request.space.allow_sharing and has_group_permission(request.user, ('user',)): + recipe = get_object_or_404(Recipe, pk=pk, space=request.space) + link = ShareLink.objects.create(recipe=recipe, created_by=request.user, space=request.space) + return JsonResponse({'pk': pk, 'share': link.uuid, + 'link': request.build_absolute_uri(reverse('view_recipe', args=[pk, link.uuid]))}) + else: + return JsonResponse({'error': 'sharing_disabled'}, status=403) @group_required('user') From 5a145d7f8eb91904abc4e98332f3117e10b3d308 Mon Sep 17 00:00:00 2001 From: Thomas Preece Date: Tue, 28 Mar 2023 17:29:38 +0100 Subject: [PATCH 06/10] PR feedback changes --- cookbook/integration/openeats.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/cookbook/integration/openeats.py b/cookbook/integration/openeats.py index 519d3f4ff..9188ca8de 100644 --- a/cookbook/integration/openeats.py +++ b/cookbook/integration/openeats.py @@ -3,12 +3,18 @@ import json from cookbook.helper.ingredient_parser import IngredientParser from cookbook.integration.integration import Integration from cookbook.models import Ingredient, Recipe, Step, Keyword, Comment, CookLog - +from django.utils.translation import gettext as _ class OpenEats(Integration): def get_recipe_from_file(self, file): - recipe = Recipe.objects.create(name=file['name'].strip(), description=file['info'], created_by=self.request.user, internal=True, + + description = file['info'] + description_max_length = Recipe._meta.get_field('description').max_length + if len(description) > description_max_length: + description = description[0:description_max_length] + + recipe = Recipe.objects.create(name=file['name'].strip(), description=description, created_by=self.request.user, internal=True, servings=file['servings'], space=self.request.space, waiting_time=file['cook_time'], working_time=file['prep_time']) instructions = '' @@ -17,7 +23,7 @@ class OpenEats(Integration): instructions += file["directions"] if file["source"] != '': - instructions += f'\nRecipe source: [{file["source"]}]({file["source"]})' + instructions += '\n' + _('Recipe source:') + f'[{file["source"]}]({file["source"]})' cuisine_keyword, created = Keyword.objects.get_or_create(name="Cuisine", space=self.request.space) if file["cuisine"] != '': From 9620689bd091dae4299fc1731720f0164cc6471a Mon Sep 17 00:00:00 2001 From: vabene1111 Date: Tue, 28 Mar 2023 23:21:57 +0200 Subject: [PATCH 07/10] disable space creation for demo user on hosted instance --- cookbook/views/views.py | 35 +++++++++++++++++++---------------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/cookbook/views/views.py b/cookbook/views/views.py index 2907e6391..4a8f61af6 100644 --- a/cookbook/views/views.py +++ b/cookbook/views/views.py @@ -69,25 +69,28 @@ def space_overview(request): if request.POST: create_form = SpaceCreateForm(request.POST, prefix='create') join_form = SpaceJoinForm(request.POST, prefix='join') - if create_form.is_valid(): - created_space = Space.objects.create( - name=create_form.cleaned_data['name'], - created_by=request.user, - max_file_storage_mb=settings.SPACE_DEFAULT_MAX_FILES, - max_recipes=settings.SPACE_DEFAULT_MAX_RECIPES, - max_users=settings.SPACE_DEFAULT_MAX_USERS, - allow_sharing=settings.SPACE_DEFAULT_ALLOW_SHARING, - ) + if settings.HOSTED and request.user.username == 'demo': + messages.add_message(request, messages.WARNING, _('This feature is not available in the demo version!')) + else: + if create_form.is_valid(): + created_space = Space.objects.create( + name=create_form.cleaned_data['name'], + created_by=request.user, + max_file_storage_mb=settings.SPACE_DEFAULT_MAX_FILES, + max_recipes=settings.SPACE_DEFAULT_MAX_RECIPES, + max_users=settings.SPACE_DEFAULT_MAX_USERS, + allow_sharing=settings.SPACE_DEFAULT_ALLOW_SHARING, + ) - user_space = UserSpace.objects.create(space=created_space, user=request.user, active=False) - user_space.groups.add(Group.objects.filter(name='admin').get()) + user_space = UserSpace.objects.create(space=created_space, user=request.user, active=False) + user_space.groups.add(Group.objects.filter(name='admin').get()) - messages.add_message(request, messages.SUCCESS, - _('You have successfully created your own recipe space. Start by adding some recipes or invite other people to join you.')) - return HttpResponseRedirect(reverse('view_switch_space', args=[user_space.space.pk])) + messages.add_message(request, messages.SUCCESS, + _('You have successfully created your own recipe space. Start by adding some recipes or invite other people to join you.')) + return HttpResponseRedirect(reverse('view_switch_space', args=[user_space.space.pk])) - if join_form.is_valid(): - return HttpResponseRedirect(reverse('view_invite', args=[join_form.cleaned_data['token']])) + if join_form.is_valid(): + return HttpResponseRedirect(reverse('view_invite', args=[join_form.cleaned_data['token']])) else: if settings.SOCIAL_DEFAULT_ACCESS and len(request.user.userspace_set.all()) == 0: user_space = UserSpace.objects.create(space=Space.objects.first(), user=request.user, active=False) From 073ee7e96395ada0506196797487483bde354a4d Mon Sep 17 00:00:00 2001 From: vabene1111 Date: Wed, 29 Mar 2023 16:03:08 +0200 Subject: [PATCH 08/10] Update openeats.py --- cookbook/integration/openeats.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cookbook/integration/openeats.py b/cookbook/integration/openeats.py index 9188ca8de..794d35937 100644 --- a/cookbook/integration/openeats.py +++ b/cookbook/integration/openeats.py @@ -9,7 +9,7 @@ class OpenEats(Integration): def get_recipe_from_file(self, file): - description = file['info'] + description = file['info'][:512] description_max_length = Recipe._meta.get_field('description').max_length if len(description) > description_max_length: description = description[0:description_max_length] From 11620ba2b60a53236d62a92d21f444c5eac3e4ea Mon Sep 17 00:00:00 2001 From: vabene1111 Date: Wed, 29 Mar 2023 16:03:33 +0200 Subject: [PATCH 09/10] Update openeats.py --- cookbook/integration/openeats.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cookbook/integration/openeats.py b/cookbook/integration/openeats.py index 794d35937..9188ca8de 100644 --- a/cookbook/integration/openeats.py +++ b/cookbook/integration/openeats.py @@ -9,7 +9,7 @@ class OpenEats(Integration): def get_recipe_from_file(self, file): - description = file['info'][:512] + description = file['info'] description_max_length = Recipe._meta.get_field('description').max_length if len(description) > description_max_length: description = description[0:description_max_length] From 569143a7ee9f737958740bd91ac48bb3b111b957 Mon Sep 17 00:00:00 2001 From: smilerz Date: Wed, 29 Mar 2023 15:52:46 -0500 Subject: [PATCH 10/10] enable toolbar seperate from debug logging --- .env.template | 1 + recipes/settings.py | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.env.template b/.env.template index f0230072a..fca55ec3b 100644 --- a/.env.template +++ b/.env.template @@ -2,6 +2,7 @@ # when unset: 1 (true) - dont unset this, just for development DEBUG=0 SQL_DEBUG=0 +DEBUG_TOOLBAR=0 # HTTP port to bind to # TANDOOR_PORT=8080 diff --git a/recipes/settings.py b/recipes/settings.py index 3dd93bf2d..7a992037e 100644 --- a/recipes/settings.py +++ b/recipes/settings.py @@ -25,6 +25,7 @@ BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) SECRET_KEY = os.getenv('SECRET_KEY') if os.getenv('SECRET_KEY') else 'INSECURE_STANDARD_KEY_SET_IN_ENV' DEBUG = bool(int(os.getenv('DEBUG', True))) +DEBUG_TOOLBAR = bool(int(os.getenv('DEBUG_TOOLBAR', True))) SOCIAL_DEFAULT_ACCESS = bool(int(os.getenv('SOCIAL_DEFAULT_ACCESS', False))) SOCIAL_DEFAULT_GROUP = os.getenv('SOCIAL_DEFAULT_GROUP', 'guest') @@ -158,7 +159,7 @@ MIDDLEWARE = [ 'cookbook.helper.scope_middleware.ScopeMiddleware', ] -if DEBUG: +if DEBUG_TOOLBAR: MIDDLEWARE += ('debug_toolbar.middleware.DebugToolbarMiddleware',) INSTALLED_APPS += ('debug_toolbar',)