added chowdown importer

This commit is contained in:
vabene1111 2021-02-09 17:15:47 +01:00
parent 58c5b2c301
commit 6ba1ff4505
6 changed files with 112 additions and 8 deletions

View File

@ -2,6 +2,7 @@
<dictionary name="vabene1111-PC"> <dictionary name="vabene1111-PC">
<words> <words>
<w>autosync</w> <w>autosync</w>
<w>chowdown</w>
<w>csrftoken</w> <w>csrftoken</w>
<w>gunicorn</w> <w>gunicorn</w>
<w>ical</w> <w>ical</w>

View File

@ -136,8 +136,9 @@ class ImportExportBase(forms.Form):
PAPRIKA = 'PAPRIKA' PAPRIKA = 'PAPRIKA'
NEXTCLOUD = 'NEXTCLOUD' NEXTCLOUD = 'NEXTCLOUD'
MEALIE = 'MEALIE' MEALIE = 'MEALIE'
CHOWDOWN = 'CHOWDOWN'
type = forms.ChoiceField(choices=((DEFAULT, _('Default')), (PAPRIKA, _('Paprika')), (NEXTCLOUD, _('Nextcloud Cookbook')), (MEALIE, _('Mealie')),)) type = forms.ChoiceField(choices=((DEFAULT, _('Default')), (PAPRIKA, _('Paprika')), (NEXTCLOUD, _('Nextcloud Cookbook')), (MEALIE, _('Mealie')), (CHOWDOWN, _('Chowdown')),))
class ImportForm(ImportExportBase): class ImportForm(ImportExportBase):

View File

@ -0,0 +1,80 @@
import json
import re
from io import BytesIO
from zipfile import ZipFile
from cookbook.helper.ingredient_parser import parse
from cookbook.integration.integration import Integration
from cookbook.models import Recipe, Step, Food, Unit, Ingredient, Keyword
class Chowdown(Integration):
def import_file_name_filter(self, zip_info_object):
print("testing", zip_info_object.filename)
return re.match(r'^_recipes/([A-Za-z\d\s-])+.md$', zip_info_object.filename)
def get_recipe_from_file(self, file):
ingredient_mode = False
direction_mode = False
description_mode = False
ingredients = []
directions = []
descriptions = []
for fl in file.readlines():
line = fl.decode("utf-8")
if 'title:' in line:
title = line.replace('title:', '').replace('"', '').strip()
if 'image:' in line:
image = line.replace('image:', '').strip()
if 'tags:' in line:
tags = line.replace('tags:', '').strip()
if ingredient_mode:
if len(line) > 2 and 'directions:' not in line:
ingredients.append(line[2:])
if '---' in line and direction_mode:
direction_mode = False
description_mode = True
if direction_mode:
if len(line) > 2:
directions.append(line[2:])
if 'ingredients:' in line:
ingredient_mode = True
if 'directions:' in line:
ingredient_mode = False
direction_mode = True
if description_mode and len(line) > 3 and '---' not in line:
descriptions.append(line)
recipe = Recipe.objects.create(name=title, created_by=self.request.user, internal=True, )
for k in tags.split(','):
keyword, created = Keyword.objects.get_or_create(name=k.strip())
recipe.keywords.add(keyword)
step = Step.objects.create(
instruction='\n'.join(directions) + '\n\n' + '\n'.join(descriptions)
)
for ingredient in ingredients:
amount, unit, ingredient, note = parse(ingredient)
f, created = Food.objects.get_or_create(name=ingredient)
u, created = Unit.objects.get_or_create(name=unit)
step.ingredients.add(Ingredient.objects.create(
food=f, unit=u, amount=amount, note=note
))
recipe.steps.add(step)
for f in self.files:
if '.zip' in f.name:
import_zip = ZipFile(f.file)
for z in import_zip.filelist:
if re.match(f'^images/{image}$', z.filename):
self.import_recipe_image(recipe, BytesIO(import_zip.read(z.filename)))
return recipe
def get_file_from_recipe(self, recipe):
raise NotImplementedError('Method not implemented in storage integration')

View File

@ -22,6 +22,7 @@ class NextcloudCookbook(Integration):
servings=recipe_json['recipeYield']) servings=recipe_json['recipeYield'])
# TODO parse times (given in PT2H3M ) # TODO parse times (given in PT2H3M )
# TODO parse keywords
ingredients_added = False ingredients_added = False
for s in recipe_json['recipeInstructions']: for s in recipe_json['recipeInstructions']:

View File

@ -6,6 +6,7 @@ from django.utils.translation import gettext as _
from cookbook.forms import ExportForm, ImportForm, ImportExportBase from cookbook.forms import ExportForm, ImportForm, ImportExportBase
from cookbook.helper.permission_helper import group_required from cookbook.helper.permission_helper import group_required
from cookbook.integration.chowdown import Chowdown
from cookbook.integration.default import Default from cookbook.integration.default import Default
from cookbook.integration.mealie import Mealie from cookbook.integration.mealie import Mealie
from cookbook.integration.nextcloud_cookbook import NextcloudCookbook from cookbook.integration.nextcloud_cookbook import NextcloudCookbook
@ -22,6 +23,8 @@ def get_integration(request, export_type):
return NextcloudCookbook(request) return NextcloudCookbook(request)
if export_type == ImportExportBase.MEALIE: if export_type == ImportExportBase.MEALIE:
return Mealie(request) return Mealie(request)
if export_type == ImportExportBase.CHOWDOWN:
return Chowdown(request)
@group_required('user') @group_required('user')

View File

@ -7,12 +7,12 @@ integrations to allow you to both import and export your recipes into whatever f
Feel like there is an important integration missing ? Just take a look at the [integration issues](https://github.com/vabene1111/recipes/issues?q=is%3Aissue+is%3Aopen+label%3Aintegration) or open a new one Feel like there is an important integration missing ? Just take a look at the [integration issues](https://github.com/vabene1111/recipes/issues?q=is%3Aissue+is%3Aopen+label%3Aintegration) or open a new one
if your favorite one is missing. if your favorite one is missing.
!!! warning "WIP" !!! info "Export"
Please note that this feature is relatively new and many integrations are missing. I strongly believe in everyone's right to use their data as they please and therefore want to give you
Additionally, many recipe applications provide formats that are not structured in an easily machine-readable way the most possible flexibility with your recipes.
and thus require a lot of work to integrate even tough the module is very versatile. That said for most of the people getting this application running with their recipes is the biggest priority.
If you are good at writing parsers feel free to add new integrations for your favorite services. Because of this importing as many formats as possible is prioritized over exporting.
Exporter for the different formats will follow over time.
## Default ## Default
The default integration is the build in (and preferred) way to import and export recipes. The default integration is the build in (and preferred) way to import and export recipes.
@ -36,7 +36,7 @@ You will get a `Recipes.zip` file. Simply upload the file and choose the Nextclo
!!! warning "Folder Structure" !!! warning "Folder Structure"
Importing only works if the folder structure is correct. If you do not use the standard path or create the 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 strucutre is as follows zip file in any other way make sure the structure is as follows
``` ```
Recipes.zip/ Recipes.zip/
└── Recipes/ └── Recipes/
@ -57,6 +57,24 @@ To migrate your recipes
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) 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 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`.
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.
The folder structure should look as follows
```
Recipes.zip/
├── _recipes/
│ ├── recipe one.md
│ ├── recipe two.md
│ └── ...
└── images/
├── image-name.jpg
├── second-image-name.jpg
└── ...
```
## Paprika ## Paprika
Paprika can create two types of export. The first is a proprietary `.paprikarecipes` file in some kind of binarized format. Paprika can create two types of export. The first is a proprietary `.paprikarecipes` file in some kind of binarized format.
The second one is HTML files containing at least a bit of microdata. The second one is HTML files containing at least a bit of microdata.