added cookmate importer

This commit is contained in:
vabene1111
2022-04-04 22:49:50 +02:00
parent 12865437d7
commit 48e9f3f8a9
6 changed files with 97 additions and 3 deletions

View File

@ -159,6 +159,7 @@ class ImportExportBase(forms.Form):
PLANTOEAT = 'PLANTOEAT' PLANTOEAT = 'PLANTOEAT'
COOKBOOKAPP = 'COOKBOOKAPP' COOKBOOKAPP = 'COOKBOOKAPP'
COPYMETHAT = 'COPYMETHAT' COPYMETHAT = 'COPYMETHAT'
COOKMATE = 'COOKMATE'
PDF = 'PDF' PDF = 'PDF'
type = forms.ChoiceField(choices=( type = forms.ChoiceField(choices=(
@ -167,6 +168,7 @@ class ImportExportBase(forms.Form):
(PEPPERPLATE, 'Pepperplate'), (RECETTETEK, 'RecetteTek'), (RECIPESAGE, 'Recipe Sage'), (DOMESTICA, 'Domestica'), (PEPPERPLATE, 'Pepperplate'), (RECETTETEK, 'RecetteTek'), (RECIPESAGE, 'Recipe Sage'), (DOMESTICA, 'Domestica'),
(MEALMASTER, 'MealMaster'), (REZKONV, 'RezKonv'), (OPENEATS, 'Openeats'), (RECIPEKEEPER, 'Recipe Keeper'), (MEALMASTER, 'MealMaster'), (REZKONV, 'RezKonv'), (OPENEATS, 'Openeats'), (RECIPEKEEPER, 'Recipe Keeper'),
(PLANTOEAT, 'Plantoeat'), (COOKBOOKAPP, 'CookBookApp'), (COPYMETHAT, 'CopyMeThat'), (PDF, 'PDF'), (MELARECIPES, 'Melarecipes'), (PLANTOEAT, 'Plantoeat'), (COOKBOOKAPP, 'CookBookApp'), (COPYMETHAT, 'CopyMeThat'), (PDF, 'PDF'), (MELARECIPES, 'Melarecipes'),
(COOKMATE, 'Cookmate')
)) ))

View File

@ -0,0 +1,77 @@
import base64
import json
from io import BytesIO
from gettext import gettext as _
import requests
from lxml import etree
from cookbook.helper.ingredient_parser import IngredientParser
from cookbook.helper.recipe_url_import import parse_servings, parse_time, parse_servings_text
from cookbook.integration.integration import Integration
from cookbook.models import Ingredient, Keyword, Recipe, Step
class Cookmate(Integration):
def import_file_name_filter(self, zip_info_object):
return zip_info_object.filename.endswith('.xml')
def get_files_from_recipes(self, recipes, el, cookie):
raise NotImplementedError('Method not implemented in storage integration')
def get_recipe_from_file(self, file):
recipe_xml = file
recipe = Recipe.objects.create(
name=recipe_xml.find('title').text.strip(),
created_by=self.request.user, internal=True, space=self.request.space)
if recipe_xml.find('preptime') is not None:
recipe.working_time = parse_time(recipe_xml.find('preptime').text.strip())
if recipe_xml.find('cooktime') is not None:
recipe.waiting_time = parse_time(recipe_xml.find('cooktime').text.strip())
if recipe_xml.find('quantity') is not None:
recipe.servings = parse_servings(recipe_xml.find('quantity').text.strip())
recipe.servings_text = parse_servings_text(recipe_xml.find('quantity').text.strip())
if recipe_xml.find('url') is not None:
recipe.source_url = recipe_xml.find('url').text.strip()
if recipe_xml.find('description') is not None: # description is a list of <li>'s with text
if len(recipe_xml.find('description')) > 0:
recipe.description = recipe_xml.find('description')[0].text[:512]
for step in recipe_xml.find('recipetext').getchildren():
step = Step.objects.create(
instruction=step.text.strip(), space=self.request.space,
)
recipe.steps.add(step)
ingredient_parser = IngredientParser(self.request, True)
for ingredient in recipe_xml.find('ingredient').getchildren():
if ingredient.text.strip() != '':
amount, unit, food, note = ingredient_parser.parse(ingredient.text.strip())
f = ingredient_parser.get_food(food)
u = ingredient_parser.get_unit(unit)
recipe.steps.first().ingredients.add(Ingredient.objects.create(
food=f, unit=u, amount=amount, note=note, original_text=ingredient.text.strip(), space=self.request.space,
))
if recipe_xml.find('imageurl') is not None:
try:
response = requests.get(recipe_xml.find('imageurl').text.strip())
self.import_recipe_image(recipe, BytesIO(response.content))
except Exception as e:
print('failed to import image ', str(e))
recipe.save()
return recipe
def get_file_from_recipe(self, recipe):
raise NotImplementedError('Method not implemented in storage integration')

View File

@ -5,6 +5,8 @@ import traceback
import uuid import uuid
from io import BytesIO, StringIO from io import BytesIO, StringIO
from zipfile import BadZipFile, ZipFile from zipfile import BadZipFile, ZipFile
import lxml
from django.core.cache import cache from django.core.cache import cache
import datetime import datetime
@ -16,6 +18,7 @@ from django.http import HttpResponse
from django.utils.formats import date_format from django.utils.formats import date_format
from django.utils.translation import gettext as _ from django.utils.translation import gettext as _
from django_scopes import scope from django_scopes import scope
from lxml import etree
from cookbook.forms import ImportExportBase from cookbook.forms import ImportExportBase
from cookbook.helper.image_processing import get_filetype, handle_image from cookbook.helper.image_processing import get_filetype, handle_image
@ -144,7 +147,7 @@ class Integration:
il.imported_recipes += 1 il.imported_recipes += 1
il.save() il.save()
import_zip.close() import_zip.close()
elif '.zip' in f['name'] or '.paprikarecipes' in f['name']: elif '.zip' in f['name'] or '.paprikarecipes' in f['name'] or '.mcb' in f['name']:
import_zip = ZipFile(f['file']) import_zip = ZipFile(f['file'])
file_list = [] file_list = []
for z in import_zip.filelist: for z in import_zip.filelist:
@ -157,9 +160,16 @@ class Integration:
file_list = self.split_recipe_file(BytesIO(import_zip.read('recipes.html'))) file_list = self.split_recipe_file(BytesIO(import_zip.read('recipes.html')))
il.total_recipes += len(file_list) il.total_recipes += len(file_list)
if isinstance(self, cookbook.integration.cookmate.Cookmate):
new_file_list = []
for file in file_list:
new_file_list += etree.parse(BytesIO(import_zip.read(file.filename))).getroot().getchildren()
il.total_recipes = len(new_file_list)
file_list = new_file_list
for z in file_list: for z in file_list:
try: try:
if isinstance(z, Tag): if not hasattr(z, 'filename'):
recipe = self.get_recipe_from_file(z) recipe = self.get_recipe_from_file(z)
else: else:
recipe = self.get_recipe_from_file(BytesIO(import_zip.read(z.filename))) recipe = self.get_recipe_from_file(BytesIO(import_zip.read(z.filename)))

View File

@ -15,6 +15,7 @@ from cookbook.helper.recipe_search import RecipeSearch
from cookbook.integration.cheftap import ChefTap from cookbook.integration.cheftap import ChefTap
from cookbook.integration.chowdown import Chowdown from cookbook.integration.chowdown import Chowdown
from cookbook.integration.cookbookapp import CookBookApp from cookbook.integration.cookbookapp import CookBookApp
from cookbook.integration.cookmate import Cookmate
from cookbook.integration.copymethat import CopyMeThat from cookbook.integration.copymethat import CopyMeThat
from cookbook.integration.default import Default from cookbook.integration.default import Default
from cookbook.integration.domestica import Domestica from cookbook.integration.domestica import Domestica
@ -77,6 +78,8 @@ def get_integration(request, export_type):
return PDFexport(request, export_type) return PDFexport(request, export_type)
if export_type == ImportExportBase.MELARECIPES: if export_type == ImportExportBase.MELARECIPES:
return MelaRecipes(request, export_type) return MelaRecipes(request, export_type)
if export_type == ImportExportBase.COOKMATE:
return Cookmate(request, export_type)
@group_required('user') @group_required('user')

View File

@ -38,7 +38,8 @@ Overview of the capabilities of the different integrations.
| Plantoeat | ✔️ | ❌ | ✔ | | Plantoeat | ✔️ | ❌ | ✔ |
| CookBookApp | ✔️ | ⌚ | ✔️ | | CookBookApp | ✔️ | ⌚ | ✔️ |
| CopyMeThat | ✔️ | ❌ | ✔️ | | CopyMeThat | ✔️ | ❌ | ✔️ |
| CopyMeThat | ✔️ | ⌚ | ✔️ | | Melarecipes | ✔️ | ⌚ | ✔️ |
| Cookmate | ✔️ | ⌚ | ✔️ |
| PDF (experimental) | ⌚️ | ✔️ | ✔️ | | PDF (experimental) | ⌚️ | ✔️ | ✔️ |
✔️ = implemented, ❌ = not implemented and not possible/planned, ⌚ = not yet implemented ✔️ = implemented, ❌ = not implemented and not possible/planned, ⌚ = not yet implemented

View File

@ -5,6 +5,7 @@ export const INTEGRATIONS = [
{id: 'CHEFTAP', name: "Cheftap", import: true, export: false}, {id: 'CHEFTAP', name: "Cheftap", import: true, export: false},
{id: 'CHOWDOWN', name: "Chowdown", import: true, export: false}, {id: 'CHOWDOWN', name: "Chowdown", import: true, export: false},
{id: 'COOKBOOKAPP', name: "CookBookApp", import: true, export: false}, {id: 'COOKBOOKAPP', name: "CookBookApp", import: true, export: false},
{id: 'COOKMATE', name: "Cookmate", import: true, export: false},
{id: 'COPYMETHAT', name: "CopyMeThat", import: true, export: false}, {id: 'COPYMETHAT', name: "CopyMeThat", import: true, export: false},
{id: 'DOMESTICA', name: "Domestica", import: true, export: false}, {id: 'DOMESTICA', name: "Domestica", import: true, export: false},
{id: 'MEALIE', name: "Mealie", import: true, export: false}, {id: 'MEALIE', name: "Mealie", import: true, export: false},