mirror of
https://github.com/TandoorRecipes/recipes.git
synced 2025-12-23 18:29:23 -05:00
import of gourmet files
* in gourmet export as html and zip folder * 'menus' are not imported Note: it appears that the native format '.grmt' does not include the unit for ingredients
This commit is contained in:
@@ -89,12 +89,13 @@ class ImportExportBase(forms.Form):
|
||||
COOKMATE = 'COOKMATE'
|
||||
REZEPTSUITEDE = 'REZEPTSUITEDE'
|
||||
PDF = 'PDF'
|
||||
GOURMET = 'GOURMET'
|
||||
|
||||
type = forms.ChoiceField(choices=((DEFAULT, _('Default')), (PAPRIKA, 'Paprika'), (NEXTCLOUD, 'Nextcloud Cookbook'), (MEALIE, 'Mealie'), (CHOWDOWN, 'Chowdown'),
|
||||
(SAFFRON, 'Saffron'), (CHEFTAP, 'ChefTap'), (PEPPERPLATE, 'Pepperplate'), (RECETTETEK, 'RecetteTek'), (RECIPESAGE, 'Recipe Sage'),
|
||||
(DOMESTICA, 'Domestica'), (MEALMASTER, 'MealMaster'), (REZKONV, 'RezKonv'), (OPENEATS, 'Openeats'), (RECIPEKEEPER, 'Recipe Keeper'),
|
||||
(PLANTOEAT, 'Plantoeat'), (COOKBOOKAPP, 'CookBookApp'), (COPYMETHAT, 'CopyMeThat'), (PDF, 'PDF'), (MELARECIPES, 'Melarecipes'),
|
||||
(COOKMATE, 'Cookmate'), (REZEPTSUITEDE, 'Recipesuite.de')))
|
||||
(COOKMATE, 'Cookmate'), (REZEPTSUITEDE, 'Recipesuite.de'), (GOURMET, 'Gourmet')))
|
||||
|
||||
|
||||
class MultipleFileInput(forms.ClearableFileInput):
|
||||
|
||||
211
cookbook/integration/gourmet.py
Normal file
211
cookbook/integration/gourmet.py
Normal file
@@ -0,0 +1,211 @@
|
||||
import base64
|
||||
from io import BytesIO
|
||||
from lxml import etree
|
||||
import requests
|
||||
from pathlib import Path
|
||||
|
||||
from bs4 import BeautifulSoup, Tag
|
||||
|
||||
from cookbook.helper.HelperFunctions import validate_import_url
|
||||
from cookbook.helper.ingredient_parser import IngredientParser
|
||||
from cookbook.helper.recipe_url_import import parse_servings, parse_servings_text, parse_time, iso_duration_to_minutes
|
||||
from cookbook.integration.integration import Integration
|
||||
from cookbook.models import Ingredient, Recipe, Step, Keyword
|
||||
from recipe_scrapers import scrape_html
|
||||
|
||||
|
||||
class Gourmet(Integration):
|
||||
|
||||
def split_recipe_file(self, file):
|
||||
encoding = 'utf-8'
|
||||
byte_string = file.read()
|
||||
text_obj = byte_string.decode(encoding, errors="ignore")
|
||||
soup = BeautifulSoup(text_obj, "html.parser")
|
||||
return soup.find_all("div", {"class": "recipe"})
|
||||
|
||||
def get_ingredients_recursive(self, step, ingredients, ingredient_parser):
|
||||
if isinstance(ingredients, Tag):
|
||||
for ingredient in ingredients.children:
|
||||
if not isinstance(ingredient, Tag):
|
||||
continue
|
||||
|
||||
if ingredient.name in ["li"]:
|
||||
step_name = "".join(ingredient.findAll(text=True, recursive=False)).strip().rstrip(":")
|
||||
|
||||
step.ingredients.add(Ingredient.objects.create(
|
||||
is_header=True,
|
||||
note=step_name[:256],
|
||||
original_text=step_name,
|
||||
space=self.request.space,
|
||||
))
|
||||
next_ingrediets = ingredient.find("ul", {"class": "ing"})
|
||||
self.get_ingredients_recursive(step, next_ingrediets, ingredient_parser)
|
||||
|
||||
else:
|
||||
try:
|
||||
amount, unit, food, note = ingredient_parser.parse(ingredient.text.strip())
|
||||
f = ingredient_parser.get_food(food)
|
||||
u = ingredient_parser.get_unit(unit)
|
||||
step.ingredients.add(
|
||||
Ingredient.objects.create(
|
||||
food=f,
|
||||
unit=u,
|
||||
amount=amount,
|
||||
note=note,
|
||||
original_text=ingredient.text.strip(),
|
||||
space=self.request.space,
|
||||
)
|
||||
)
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
def get_recipe_from_file(self, file):
|
||||
# 'file' comes is as a beautifulsoup object
|
||||
|
||||
source_url = None
|
||||
for item in file.find_all('a'):
|
||||
if item.has_attr('href'):
|
||||
source_url = item.get("href")
|
||||
break
|
||||
|
||||
name = file.find("p", {"class": "title"}).find("span", {"itemprop": "name"}).text.strip()
|
||||
|
||||
recipe = Recipe.objects.create(
|
||||
name=name[:128],
|
||||
source_url=source_url,
|
||||
created_by=self.request.user,
|
||||
internal=True,
|
||||
space=self.request.space,
|
||||
)
|
||||
|
||||
for category in file.find_all("span", {"itemprop": "recipeCategory"}):
|
||||
keyword, created = Keyword.objects.get_or_create(name=category.text, space=self.request.space)
|
||||
recipe.keywords.add(keyword)
|
||||
|
||||
try:
|
||||
recipe.servings = parse_servings(file.find("span", {"itemprop": "recipeYield"}).text.strip())
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
try:
|
||||
prep_time = file.find("span", {"itemprop": "prepTime"}).text.strip().split()
|
||||
prep_time[0] = prep_time[0].replace(',', '.')
|
||||
if prep_time[1].lower() in ['stunde', 'stunden', 'hour', 'hours']:
|
||||
prep_time_min = int(float(prep_time[0]) * 60)
|
||||
elif prep_time[1].lower() in ['tag', 'tage', 'day', 'days']:
|
||||
prep_time_min = int(float(prep_time[0]) * 60 * 24)
|
||||
else:
|
||||
prep_time_min = int(prep_time[0])
|
||||
recipe.waiting_time = prep_time_min
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
try:
|
||||
cook_time = file.find("span", {"itemprop": "cookTime"}).text.strip().split()
|
||||
cook_time[0] = cook_time[0].replace(',', '.')
|
||||
if cook_time[1].lower() in ['stunde', 'stunden', 'hour', 'hours']:
|
||||
cook_time_min = int(float(cook_time[0]) * 60)
|
||||
elif cook_time[1].lower() in ['tag', 'tage', 'day', 'days']:
|
||||
cook_time_min = int(float(cook_time[0]) * 60 * 24)
|
||||
else:
|
||||
cook_time_min = int(cook_time[0])
|
||||
|
||||
recipe.working_time = cook_time_min
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
for cuisine in file.find_all('span', {'itemprop': 'recipeCuisine'}):
|
||||
cuisine_name = cuisine.text
|
||||
keyword = Keyword.objects.get_or_create(space=self.request.space, name=cuisine_name)
|
||||
if len(keyword):
|
||||
recipe.keywords.add(keyword[0])
|
||||
|
||||
for category in file.find_all('span', {'itemprop': 'recipeCategory'}):
|
||||
category_name = category.text
|
||||
keyword = Keyword.objects.get_or_create(space=self.request.space, name=category_name)
|
||||
if len(keyword):
|
||||
recipe.keywords.add(keyword[0])
|
||||
|
||||
step = Step.objects.create(
|
||||
instruction='',
|
||||
space=self.request.space,
|
||||
show_ingredients_table=self.request.user.userpreference.show_step_ingredients,
|
||||
)
|
||||
|
||||
ingredient_parser = IngredientParser(self.request, True)
|
||||
|
||||
ingredients = file.find("ul", {"class": "ing"})
|
||||
self.get_ingredients_recursive(step, ingredients, ingredient_parser)
|
||||
|
||||
instructions = file.find("div", {"class": "instructions"})
|
||||
if isinstance(instructions, Tag):
|
||||
for instruction in instructions.children:
|
||||
if not isinstance(instruction, Tag) or instruction.text == "":
|
||||
continue
|
||||
if instruction.name == "h3":
|
||||
if step.instruction:
|
||||
step.save()
|
||||
recipe.steps.add(step)
|
||||
step = Step.objects.create(
|
||||
instruction='',
|
||||
space=self.request.space,
|
||||
)
|
||||
|
||||
step.name = instruction.text.strip()[:128]
|
||||
else:
|
||||
if instruction.name == "div":
|
||||
for instruction_step in instruction.children:
|
||||
for br in instruction_step.find_all("br"):
|
||||
br.replace_with("\n")
|
||||
step.instruction += instruction_step.text.strip() + ' \n\n'
|
||||
|
||||
notes = file.find("div", {"class": "modifications"})
|
||||
if notes:
|
||||
for n in notes.children:
|
||||
if n.text == "":
|
||||
continue
|
||||
if n.name == "h3":
|
||||
step.instruction += f'*{n.text.strip()}:* \n\n'
|
||||
else:
|
||||
for br in n.find_all("br"):
|
||||
br.replace_with("\n")
|
||||
|
||||
step.instruction += '*' + n.text.strip() + '* \n\n'
|
||||
|
||||
description = ''
|
||||
try:
|
||||
description = file.find("div", {"id": "description"}).text.strip()
|
||||
except AttributeError:
|
||||
pass
|
||||
if len(description) <= 512:
|
||||
recipe.description = description
|
||||
else:
|
||||
recipe.description = description[:480] + ' ... (full description below)'
|
||||
step.instruction += '*Description:* \n\n*' + description + '* \n\n'
|
||||
|
||||
step.save()
|
||||
recipe.steps.add(step)
|
||||
|
||||
# import the Primary recipe image that is stored in the Zip
|
||||
try:
|
||||
image_path = file.find("img").get("src")
|
||||
image_filename = image_path.split("\\")[1]
|
||||
|
||||
for f in self.import_zip.filelist:
|
||||
zip_file_name = Path(f.filename).name
|
||||
if image_filename == zip_file_name:
|
||||
image_file = self.import_zip.read(f)
|
||||
image_bytes = BytesIO(image_file)
|
||||
self.import_recipe_image(recipe, image_bytes, filetype='.jpeg')
|
||||
break
|
||||
except Exception as e:
|
||||
print(recipe.name, ': failed to import image ', str(e))
|
||||
|
||||
recipe.save()
|
||||
return recipe
|
||||
|
||||
def get_files_from_recipes(self, recipes, el, cookie):
|
||||
raise NotImplementedError('Method not implemented in storage integration')
|
||||
|
||||
def get_file_from_recipe(self, recipe):
|
||||
raise NotImplementedError('Method not implemented in storage integration')
|
||||
@@ -153,6 +153,19 @@ class Integration:
|
||||
il.total_recipes = len(new_file_list)
|
||||
file_list = new_file_list
|
||||
|
||||
if isinstance(self, cookbook.integration.gourmet.Gourmet):
|
||||
self.import_zip = import_zip
|
||||
new_file_list = []
|
||||
for file in file_list:
|
||||
if file.file_size == 0:
|
||||
next
|
||||
if file.filename.startswith("index.htm"):
|
||||
next
|
||||
if file.filename.endswith(".htm"):
|
||||
new_file_list += self.split_recipe_file(BytesIO(import_zip.read(file.filename)))
|
||||
il.total_recipes = len(new_file_list)
|
||||
file_list = new_file_list
|
||||
|
||||
for z in file_list:
|
||||
try:
|
||||
if not hasattr(z, 'filename') or isinstance(z, Tag):
|
||||
|
||||
@@ -31,6 +31,7 @@ from cookbook.integration.recipesage import RecipeSage
|
||||
from cookbook.integration.rezeptsuitede import Rezeptsuitede
|
||||
from cookbook.integration.rezkonv import RezKonv
|
||||
from cookbook.integration.saffron import Saffron
|
||||
from cookbook.integration.gourmet import Gourmet
|
||||
from cookbook.models import ExportLog, Recipe
|
||||
from recipes import settings
|
||||
|
||||
@@ -80,6 +81,8 @@ def get_integration(request, export_type):
|
||||
return Cookmate(request, export_type)
|
||||
if export_type == ImportExportBase.REZEPTSUITEDE:
|
||||
return Rezeptsuitede(request, export_type)
|
||||
if export_type == ImportExportBase.GOURMET:
|
||||
return Gourmet(request, export_type)
|
||||
|
||||
|
||||
@group_required('user')
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
This application features a very versatile import and export feature in order
|
||||
This application features a very versatile import and export feature in order
|
||||
to offer the best experience possible and allow you to freely choose where your data goes.
|
||||
|
||||
!!! WARNING "WIP"
|
||||
The Module is relatively new. There is a known issue with [Timeouts](https://github.com/vabene1111/recipes/issues/417) on large exports.
|
||||
A fix is being developed and will likely be released with the next version.
|
||||
The Module is relatively new. There is a known issue with [Timeouts](https://github.com/vabene1111/recipes/issues/417) on large exports.
|
||||
A fix is being developed and will likely be released with the next version.
|
||||
|
||||
The Module is built with maximum flexibility and expandability in mind and allows to easily add new
|
||||
integrations to allow you to both import and export your recipes into whatever format you desire.
|
||||
@@ -12,59 +12,64 @@ Feel like there is an important integration missing? Just take a look at the [in
|
||||
if your favorite one is missing.
|
||||
|
||||
!!! info "Export"
|
||||
I strongly believe in everyone's right to use their data as they please and therefore want to give you
|
||||
the best possible flexibility with your recipes.
|
||||
That said for most of the people getting this application running with their recipes is the biggest priority.
|
||||
Because of this importing as many formats as possible is prioritized over exporting.
|
||||
Exporter for the different formats will follow over time.
|
||||
I strongly believe in everyone's right to use their data as they please and therefore want to give you
|
||||
the best possible flexibility with your recipes.
|
||||
That said for most of the people getting this application running with their recipes is the biggest priority.
|
||||
Because of this importing as many formats as possible is prioritized over exporting.
|
||||
Exporter for the different formats will follow over time.
|
||||
|
||||
Overview of the capabilities of the different integrations.
|
||||
|
||||
| Integration | Import | Export | Images |
|
||||
|--------------------| ------ | -- | ------ |
|
||||
| Default | ✔️ | ✔️ | ✔️ |
|
||||
| Nextcloud | ✔️ | ⌚ | ✔️ |
|
||||
| Mealie | ✔️ | ⌚ | ✔️ |
|
||||
| Chowdown | ✔️ | ⌚ | ✔️ |
|
||||
| Safron | ✔️ | ✔️ | ❌ |
|
||||
| Paprika | ✔️ | ⌚ | ✔️ |
|
||||
| ChefTap | ✔️ | ❌ | ❌ |
|
||||
| Pepperplate | ✔️ | ⌚ | ❌ |
|
||||
| RecipeSage | ✔️ | ✔️ | ✔️ |
|
||||
| Rezeptsuite.de | ✔️ | ❌ | ✔️ |
|
||||
| Domestica | ✔️ | ⌚ | ✔️ |
|
||||
| MealMaster | ✔️ | ❌ | ❌ |
|
||||
| RezKonv | ✔️ | ❌ | ❌ |
|
||||
| OpenEats | ✔️ | ❌ | ⌚ |
|
||||
| Plantoeat | ✔️ | ❌ | ✔ |
|
||||
| CookBookApp | ✔️ | ⌚ | ✔️ |
|
||||
| CopyMeThat | ✔️ | ❌ | ✔️ |
|
||||
| Melarecipes | ✔️ | ⌚ | ✔️ |
|
||||
| Cookmate | ✔️ | ⌚ | ✔️ |
|
||||
| PDF (experimental) | ⌚️ | ✔️ | ✔️ |
|
||||
| ------------------ | ------ | ------ | ------ |
|
||||
| Default | ✔️ | ✔️ | ✔️ |
|
||||
| Nextcloud | ✔️ | ⌚ | ✔️ |
|
||||
| Mealie | ✔️ | ⌚ | ✔️ |
|
||||
| Chowdown | ✔️ | ⌚ | ✔️ |
|
||||
| Safron | ✔️ | ✔️ | ❌ |
|
||||
| Paprika | ✔️ | ⌚ | ✔️ |
|
||||
| ChefTap | ✔️ | ❌ | ❌ |
|
||||
| Pepperplate | ✔️ | ⌚ | ❌ |
|
||||
| RecipeSage | ✔️ | ✔️ | ✔️ |
|
||||
| Rezeptsuite.de | ✔️ | ❌ | ✔️ |
|
||||
| Domestica | ✔️ | ⌚ | ✔️ |
|
||||
| MealMaster | ✔️ | ❌ | ❌ |
|
||||
| RezKonv | ✔️ | ❌ | ❌ |
|
||||
| OpenEats | ✔️ | ❌ | ⌚ |
|
||||
| Plantoeat | ✔️ | ❌ | ✔ |
|
||||
| CookBookApp | ✔️ | ⌚ | ✔️ |
|
||||
| CopyMeThat | ✔️ | ❌ | ✔️ |
|
||||
| Melarecipes | ✔️ | ⌚ | ✔️ |
|
||||
| Cookmate | ✔️ | ⌚ | ✔️ |
|
||||
| PDF (experimental) | ⌚️ | ✔️ | ✔️ |
|
||||
| Gourmet | ✔️ | ❌ | ✔️ |
|
||||
|
||||
✔️ = implemented, ❌ = not implemented and not possible/planned, ⌚ = not yet implemented
|
||||
|
||||
## Default
|
||||
|
||||
The default integration is the built in (and preferred) way to import and export recipes.
|
||||
It is maintained with new fields added and contains all data to transfer your recipes from one installation to another.
|
||||
|
||||
It is also one of the few recipe formats that is actually structured in a way that allows for
|
||||
easy machine readability if you want to use the data for any other purpose.
|
||||
It is also one of the few recipe formats that is actually structured in a way that allows for
|
||||
easy machine readability if you want to use the data for any other purpose.
|
||||
|
||||
## RecipeSage
|
||||
Go to Settings > Export Recipe Data and select `EXPORT AS JSON-LD (BEST)`. Then simply upload the exported file
|
||||
|
||||
Go to Settings > Export Recipe Data and select `EXPORT AS JSON-LD (BEST)`. Then simply upload the exported file
|
||||
to Tandoor.
|
||||
|
||||
The RecipeSage integration also allows exporting. To migrate from Tandoor to RecipeSage simply export with Recipe Sage
|
||||
The RecipeSage integration also allows exporting. To migrate from Tandoor to RecipeSage simply export with Recipe Sage
|
||||
selected and import the json file in RecipeSage. Images are currently not supported for exporting.
|
||||
|
||||
## Domestica
|
||||
Go to Import/Export and select `Export Recipes`. Then simply upload the exported file
|
||||
|
||||
Go to Import/Export and select `Export Recipes`. Then simply upload the exported file
|
||||
to Tandoor.
|
||||
|
||||
## Nextcloud
|
||||
Importing recipes from Nextcloud cookbook is very easy and since Nextcloud Cookbook provides nice, standardized and
|
||||
|
||||
Importing recipes from Nextcloud cookbook is very easy and since Nextcloud Cookbook provides nice, standardized and
|
||||
structured information most of your recipe is going to be intact.
|
||||
|
||||
Follow these steps to import your recipes
|
||||
@@ -77,10 +82,9 @@ Follow these steps to import your recipes
|
||||
You will get a `Recipes.zip` file. Simply upload the file and choose the Nextcloud Cookbook type.
|
||||
|
||||
!!! WARNING "Folder Structure"
|
||||
Importing only works if the folder structure is correct. If you do not use the standard path or create the
|
||||
zip file in any other way make sure the structure is as follows
|
||||
```
|
||||
Recipes.zip/
|
||||
Importing only works if the folder structure is correct. If you do not use the standard path or create the
|
||||
zip file in any other way make sure the structure is as follows
|
||||
` Recipes.zip/
|
||||
└── Recipes/
|
||||
├── Recipe1/
|
||||
│ ├── recipe.json
|
||||
@@ -88,27 +92,29 @@ You will get a `Recipes.zip` file. Simply upload the file and choose the Nextclo
|
||||
└── Recipe2/
|
||||
├── recipe.json
|
||||
└── full.jpg
|
||||
```
|
||||
`
|
||||
|
||||
## Mealie
|
||||
Mealie provides structured data similar to nextcloud.
|
||||
|
||||
To migrate your recipes
|
||||
Mealie provides structured data similar to nextcloud.
|
||||
|
||||
To migrate your recipes
|
||||
|
||||
1. Go to your Mealie settings and create a new Backup.
|
||||
2. Download the backup by clicking on it and pressing download (this wasn't working for me, so I had to manually pull it from the server).
|
||||
3. Upload the entire `.zip` file to the importer page and import everything.
|
||||
|
||||
## Chowdown
|
||||
Chowdown stores all your recipes in plain text markdown files in a directory called `_recipes`.
|
||||
|
||||
Chowdown stores all your recipes in plain text markdown files in a directory called `_recipes`.
|
||||
Images are saved in a directory called `images`.
|
||||
|
||||
In order to import your Chowdown recipes simply create a `.zip` file from those two folders and import them.
|
||||
In order to import your Chowdown recipes simply create a `.zip` file from those two folders and import them.
|
||||
The folder structure should look as follows
|
||||
|
||||
!!! info "_recipes"
|
||||
For some reason chowdown uses `_` before the `recipes` folder. To avoid confusion the import supports both
|
||||
`_recipes` and `recipes`
|
||||
For some reason chowdown uses `_`before the`recipes`folder. To avoid confusion the import supports both
|
||||
`\_recipes`and`recipes`
|
||||
|
||||
```
|
||||
Recipes.zip/
|
||||
@@ -123,31 +129,35 @@ Recipes.zip/
|
||||
```
|
||||
|
||||
## Safron
|
||||
|
||||
Go to your safron settings page and export your recipes.
|
||||
Then simply upload the entire `.zip` file to the importer.
|
||||
|
||||
!!! warning "Images"
|
||||
Safron exports do not contain any images. They will be lost during import.
|
||||
Safron exports do not contain any images. They will be lost during import.
|
||||
|
||||
## Paprika
|
||||
|
||||
A Paprika export contains a folder with a html representation of your recipes and a `.paprikarecipes` file.
|
||||
|
||||
The `.paprikarecipes` file is basically just a zip with gzipped contents. Simply upload the whole file and import
|
||||
all your recipes.
|
||||
The `.paprikarecipes` file is basically just a zip with gzipped contents. Simply upload the whole file and import
|
||||
all your recipes.
|
||||
|
||||
## Pepperplate
|
||||
|
||||
Pepperplate provides a `.zip` file containing all of your recipes as `.txt` files. These files are well-structured and allow
|
||||
the import of all data without losing anything.
|
||||
|
||||
Simply export the recipes from Pepperplate and upload the zip to Tandoor. Images are not included in the export and
|
||||
Simply export the recipes from Pepperplate and upload the zip to Tandoor. Images are not included in the export and
|
||||
thus cannot be imported.
|
||||
|
||||
## ChefTap
|
||||
|
||||
ChefTaps allows you to export your recipes from the app (I think). The export is a zip file containing a folder called
|
||||
`cheftap_export` which in turn contains `.txt` files with your recipes.
|
||||
|
||||
This format is basically completely unstructured and every export looks different. This makes importing it very hard
|
||||
and leads to suboptimal results. Images are also not supported as they are not included in the export (at least
|
||||
and leads to suboptimal results. Images are also not supported as they are not included in the export (at least
|
||||
the tests I had).
|
||||
|
||||
Usually the import should recognize all ingredients and put everything else into the instructions. If your import fails
|
||||
@@ -156,31 +166,36 @@ or is worse than this feel free to provide me with more example data and I can t
|
||||
As ChefTap cannot import these files anyway there won't be an exporter implemented in Tandoor.
|
||||
|
||||
## MealMaster
|
||||
Meal master can be imported by uploading one or more meal master files.
|
||||
The files should either be `.txt`, `.MMF` or `.MM` files.
|
||||
|
||||
Meal master can be imported by uploading one or more meal master files.
|
||||
The files should either be `.txt`, `.MMF` or `.MM` files.
|
||||
|
||||
The MealMaster spec allows for many variations. Currently, only the one column format for ingredients is supported.
|
||||
Second line notes to ingredients are currently also not imported as a note but simply put into the instructions.
|
||||
If you have MealMaster recipes that cannot be imported feel free to raise an issue.
|
||||
|
||||
## RezKonv
|
||||
The RezKonv format is primarily used in the german recipe manager RezKonv Suite.
|
||||
|
||||
The RezKonv format is primarily used in the german recipe manager RezKonv Suite.
|
||||
To migrate from RezKonv Suite to Tandoor select `Export > Gesamtes Kochbuch exportieren` (the last option in the export menu).
|
||||
The generated file can simply be imported into Tandoor.
|
||||
|
||||
As I only had limited sample data feel free to open an issue if your RezKonv export cannot be imported.
|
||||
|
||||
## Recipekeeper
|
||||
Recipe keeper allows you to export a zip file containing recipes and images using its apps.
|
||||
|
||||
Recipe keeper allows you to export a zip file containing recipes and images using its apps.
|
||||
This zip file can simply be imported into Tandoor.
|
||||
|
||||
## OpenEats
|
||||
|
||||
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 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
|
||||
[
|
||||
{
|
||||
@@ -231,30 +246,44 @@ CookBookApp can export .zip files containing .html files. Upload the entire ZIP
|
||||
CopyMeThat can export .zip files containing an `.html` file as well as a folder containing all the images. Upload the entire ZIP to Tandoor to import all included recipes.
|
||||
|
||||
## Cookmate
|
||||
|
||||
Cookmate allows you to export a `.mcb` file which you can simply upload to tandoor and import all your recipes.
|
||||
|
||||
## RecetteTek
|
||||
RecetteTek exports are `.rtk` files which can simply be uploaded to tandoor to import all your recipes.
|
||||
|
||||
RecetteTek exports are `.rtk` files which can simply be uploaded to tandoor to import all your recipes.
|
||||
|
||||
## Rezeptsuite.de
|
||||
|
||||
Rezeptsuite.de exports are `.xml` files which can simply be uploaded to tandoor to import all your recipes.
|
||||
|
||||
It appears that Reptsuite, depending on the client, might export a `.zip` file containing a `.cml` file.
|
||||
If this happens just unzip the zip file and change `.cml` to `.xml` to import your recipes.
|
||||
If this happens just unzip the zip file and change `.cml` to `.xml` to import your recipes.
|
||||
|
||||
## Melarecipes
|
||||
|
||||
Melarecipes provides multiple export formats but only the `MelaRecipes` format can export the complete collection.
|
||||
Perform this export and open the `.melarecipes` file using your favorite archive opening program (e.g 7zip).
|
||||
Perform this export and open the `.melarecipes` file using your favorite archive opening program (e.g 7zip).
|
||||
Repeat this if the file contains another `.melarecipes` file until you get a list of one or many `.melarecipe` files.
|
||||
Upload all `.melarecipe` files you want to import to tandoor and start the import.
|
||||
|
||||
## PDF
|
||||
|
||||
The PDF Exporter is an experimental feature that uses the puppeteer browser renderer to render each recipe and export it to PDF.
|
||||
For that to work it downloads a chromium binary of about 140 MB to your server and then renders the PDF files using that.
|
||||
The PDF Exporter is an experimental feature that uses the puppeteer browser renderer to render each recipe and export it to PDF.
|
||||
For that to work it downloads a chromium binary of about 140 MB to your server and then renders the PDF files using that.
|
||||
|
||||
Since that is something some server administrators might not want there the PDF exporter is disabled by default and can be enabled with `ENABLE_PDF_EXPORT=1` in `.env`.
|
||||
|
||||
See [this issue](https://github.com/TandoorRecipes/recipes/pull/1211) for more discussion on this and
|
||||
See [this issue](https://github.com/TandoorRecipes/recipes/pull/1211) for more discussion on this and
|
||||
[this issue](https://github.com/TandoorRecipes/recipes/issues/781) for the future plans to support server side rendering.
|
||||
|
||||
## Gourmet
|
||||
|
||||
An importer for files from [Gourmet](https://github.com/thinkle/gourmet/). As the `.grmt` files appears to lack the unit for ingredients
|
||||
a file with `.zip` file with `.htm` and `.jpg`is expected.
|
||||
|
||||
To generate the file export to 'html' in Gourmet and zip the folder generated.
|
||||
|
||||
The import of menues is not supported
|
||||
|
||||
Export is not supported due to problems with `.grmt` format.
|
||||
|
||||
@@ -22,4 +22,5 @@ export const INTEGRATIONS = [
|
||||
{id: 'REZKONV', name: "Rezkonv", import: true, export: false, help_url: 'https://docs.tandoor.dev/features/import_export/#rezkonv'},
|
||||
{id: 'SAFRON', name: "Safron", import: true, export: true, help_url: 'https://docs.tandoor.dev/features/import_export/#safron'},
|
||||
{id: 'REZEPTSUITEDE', name: "Rezeptsuite.de", import: true, export: false, help_url: 'https://docs.tandoor.dev/features/import_export/#rezeptsuitede'},
|
||||
{id: 'GOURMET', name: "Gourmet", import: true, export: false, help_url: 'https://docs.tandoor.dev/features/import_export/#gourmet'},
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user