improvements to recipekeeper importer
This commit is contained in:
parent
46db6d4186
commit
f7d85bb4b8
@ -358,3 +358,11 @@ def normalize_string(string):
|
|||||||
unescaped_string = re.sub(r'\n\s*\n', '\n\n', unescaped_string)
|
unescaped_string = re.sub(r'\n\s*\n', '\n\n', unescaped_string)
|
||||||
unescaped_string = unescaped_string.replace("\xa0", " ").replace("\t", " ").strip()
|
unescaped_string = unescaped_string.replace("\xa0", " ").replace("\t", " ").strip()
|
||||||
return unescaped_string
|
return unescaped_string
|
||||||
|
|
||||||
|
|
||||||
|
def iso_duration_to_minutes(string):
|
||||||
|
match = re.match(
|
||||||
|
r'P((?P<years>\d+)Y)?((?P<months>\d+)M)?((?P<weeks>\d+)W)?((?P<days>\d+)D)?T((?P<hours>\d+)H)?((?P<minutes>\d+)M)?((?P<seconds>\d+)S)?',
|
||||||
|
string
|
||||||
|
).groupdict()
|
||||||
|
return int(match['days'] or 0) * 24 * 60 + int(match['hours'] or 0) * 60 + int(match['minutes'] or 0)
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import datetime
|
import datetime
|
||||||
import json
|
import json
|
||||||
|
import re
|
||||||
import uuid
|
import uuid
|
||||||
from io import BytesIO, StringIO
|
from io import BytesIO, StringIO
|
||||||
from zipfile import ZipFile, BadZipFile
|
from zipfile import ZipFile, BadZipFile
|
||||||
@ -216,3 +217,4 @@ class Integration:
|
|||||||
- data - string content for file to get created in export zip
|
- data - string content for file to get created in export zip
|
||||||
"""
|
"""
|
||||||
raise NotImplementedError('Method not implemented in integration')
|
raise NotImplementedError('Method not implemented in integration')
|
||||||
|
|
||||||
|
@ -6,6 +6,7 @@ from zipfile import ZipFile
|
|||||||
from django.utils.translation import gettext as _
|
from django.utils.translation import gettext as _
|
||||||
|
|
||||||
from cookbook.helper.ingredient_parser import parse, get_food, get_unit
|
from cookbook.helper.ingredient_parser import parse, get_food, get_unit
|
||||||
|
from cookbook.helper.recipe_url_import import parse_servings, iso_duration_to_minutes
|
||||||
from cookbook.integration.integration import Integration
|
from cookbook.integration.integration import Integration
|
||||||
from cookbook.models import Recipe, Step, Food, Unit, Ingredient, Keyword
|
from cookbook.models import Recipe, Step, Food, Unit, Ingredient, Keyword
|
||||||
|
|
||||||
@ -32,39 +33,37 @@ class RecipeKeeper(Integration):
|
|||||||
keyword, created = Keyword.objects.get_or_create(name=category.get("content"), space=self.request.space)
|
keyword, created = Keyword.objects.get_or_create(name=category.get("content"), space=self.request.space)
|
||||||
recipe.keywords.add(keyword)
|
recipe.keywords.add(keyword)
|
||||||
|
|
||||||
# TODO: import prep and cook times
|
try:
|
||||||
# Recipe Keeper uses ISO 8601 format for its duration periods.
|
recipe.servings = parse_servings(file.find("span", {"itemprop": "recipeYield"}).text.strip())
|
||||||
|
recipe.working_time = iso_duration_to_minutes(file.find("span", {"meta": "prepTime"}).text.strip())
|
||||||
|
recipe.waiting_time = iso_duration_to_minutes(file.find("span", {"meta": "cookTime"}).text.strip())
|
||||||
|
recipe.save()
|
||||||
|
except AttributeError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
step = Step.objects.create(instruction='')
|
||||||
|
|
||||||
|
for ingredient in file.find("div", {"itemprop": "recipeIngredients"}).findChildren("p"):
|
||||||
|
if ingredient.text == "":
|
||||||
|
continue
|
||||||
|
amount, unit, ingredient, note = parse(ingredient.text.strip())
|
||||||
|
f = get_food(ingredient, self.request.space)
|
||||||
|
u = get_unit(unit, self.request.space)
|
||||||
|
step.ingredients.add(Ingredient.objects.create(
|
||||||
|
food=f, unit=u, amount=amount, note=note
|
||||||
|
))
|
||||||
|
|
||||||
source_url_added = False
|
|
||||||
ingredients_added = False
|
|
||||||
for s in file.find("div", {"itemprop": "recipeDirections"}).find_all("p"):
|
for s in file.find("div", {"itemprop": "recipeDirections"}).find_all("p"):
|
||||||
|
|
||||||
if s.text == "":
|
if s.text == "":
|
||||||
continue
|
continue
|
||||||
|
step.instruction += s.text + ' \n'
|
||||||
|
|
||||||
step = Step.objects.create(
|
if file.find("span", {"itemprop": "recipeSource"}).text != '':
|
||||||
instruction=s.text
|
step.instruction += "\n\nImported from: " + file.find("span", {"itemprop": "recipeSource"}).text
|
||||||
)
|
step.save()
|
||||||
|
source_url_added = True
|
||||||
|
|
||||||
if not source_url_added:
|
recipe.steps.add(step)
|
||||||
# If there is a source URL, add it to the first step field.
|
|
||||||
if file.find("span", {"itemprop": "recipeSource"}).text != '':
|
|
||||||
step.instruction += "\n\nImported from: " + file.find("span", {"itemprop": "recipeSource"}).text
|
|
||||||
step.save()
|
|
||||||
source_url_added = True
|
|
||||||
|
|
||||||
if not ingredients_added:
|
|
||||||
ingredients_added = True
|
|
||||||
for ingredient in file.find("div", {"itemprop": "recipeIngredients"}).findChildren("p"):
|
|
||||||
if ingredient.text == "":
|
|
||||||
continue
|
|
||||||
amount, unit, ingredient, note = parse(ingredient.text.strip())
|
|
||||||
f = get_food(ingredient, self.request.space)
|
|
||||||
u = get_unit(unit, self.request.space)
|
|
||||||
step.ingredients.add(Ingredient.objects.create(
|
|
||||||
food=f, unit=u, amount=amount, note=note
|
|
||||||
))
|
|
||||||
recipe.steps.add(step)
|
|
||||||
|
|
||||||
# import the Primary recipe image that is stored in the Zip
|
# import the Primary recipe image that is stored in the Zip
|
||||||
try:
|
try:
|
||||||
|
@ -100,6 +100,9 @@
|
|||||||
<label class="btn btn-outline-info btn-sm" @click="recipe_app='RECIPESAGE'">
|
<label class="btn btn-outline-info btn-sm" @click="recipe_app='RECIPESAGE'">
|
||||||
<input type="radio" autocomplete="off"> Recipesage
|
<input type="radio" autocomplete="off"> Recipesage
|
||||||
</label>
|
</label>
|
||||||
|
<label class="btn btn-outline-info btn-sm" @click="recipe_app='RECIPEKEEPER'">
|
||||||
|
<input type="radio" autocomplete="off"> Recipekeeper
|
||||||
|
</label>
|
||||||
<label class="btn btn-outline-info btn-sm" @click="recipe_app='DOMESTICA'">
|
<label class="btn btn-outline-info btn-sm" @click="recipe_app='DOMESTICA'">
|
||||||
<input type="radio" autocomplete="off"> Domestica
|
<input type="radio" autocomplete="off"> Domestica
|
||||||
</label>
|
</label>
|
||||||
|
@ -35,6 +35,7 @@ Overview of the capabilities of the different integrations.
|
|||||||
| MealMaster | ✔️ | ❌ | ❌ |
|
| MealMaster | ✔️ | ❌ | ❌ |
|
||||||
| RezKonv | ✔️ | ❌ | ❌ |
|
| RezKonv | ✔️ | ❌ | ❌ |
|
||||||
| OpenEats | ✔️ | ❌ | ⌚ |
|
| OpenEats | ✔️ | ❌ | ⌚ |
|
||||||
|
| OpenEats | ✔️ | ❌ | ✔ |
|
||||||
|
|
||||||
✔ = implemented, ❌ = not implemented and not possible/planned, ⌚ = not yet implemented
|
✔ = implemented, ❌ = not implemented and not possible/planned, ⌚ = not yet implemented
|
||||||
|
|
||||||
@ -161,7 +162,11 @@ 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).
|
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.
|
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.
|
As I only had limited sample data feel free to open an issue if your RezKonv export cannot be imported.
|
||||||
|
|
||||||
|
## Recipekeeper
|
||||||
|
Recipe keeper allows to export a zip file containing recipes and images using its apps.
|
||||||
|
This zip file can simply be imported into Tandoor.
|
||||||
|
|
||||||
## OpenEats
|
## 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.
|
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.
|
||||||
|
Loading…
Reference in New Issue
Block a user