Skip to main content

Recipy converter

Project description

logo{width=25%}

python version latest version pipeline status License Ruff Checked with mypy

Conversion tool around a yml cooking recipe format.

InstallUsage

gorps

The idea: The format is simple enough so that an editor can be used instead of a GUI and this script is intended to be a "mini pandoc" for recipes.

About the name: there is a famous (unconfirmed) quote due to Luther:

Warum rülpset und furzet ihr nicht, hat es euch nicht geschmacket?

Translation:

Why don't you burp and fart? Did it not taste good?

Gorps is the Swiss German word for burp (abuse of emoji 🗯 for the logo).

A typical recipe looks like:

title: Beans with Bacon a la Bud Spencer
description: Chuck Norris? Never heard about her!
instruction: |
  Finely dice the onion and briefly sauté in hot oil together
  with the bacon.
  ...
amount: 1
amount_unit: Pan
preparation_time: 900
cooking_time: 600
source: https://www.kabeleins.ch/sosiehtsaus/essen-trinken/rezepte/bohnen-mit-speck-nach-bud-spencer
link: https://www.kabeleins.ch/sosiehtsaus/essen-trinken/rezepte/bohnen-mit-speck-nach-bud-spencer
ingredients:
- name: Finely diced bacon
  amount: 125
  unit: g
- name: Clove of garlic
  amount: 1
- name: Salami or Cabanossi
  amount: 150
  unit: g
- name: ...
notes: |
  Bud gives a damn about cream, but if you prever, serve with cream!
tags:
  category:
  - Main course

An amount of an ingredient can be

  • A number:
    - name: Clove of garlic
      amount: 1
    
  • A fraction:
    - name: Clove of garlic
      amount: 1/3
    
  • A range:
    - name: Clove of garlic
      amount:
        min: 2/3
        max: 1
    

Ingredients can be grouped:

ingredients:
  - name: Trimming
    ingredients:
      - name: ...
        amount: ...
        unit: ...

It is also possible to include an image:

image:
  fmt: image/jpeg
  data: !!binary |
    /9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0a

Where the binary data is the base64 encoded binary data of the image.

An image /tmp/1x1.png can be inserted into a recipe using

gorps set-image --pic=/tmp/1x1.png examples/recipes/yml/more-beans.yml

This will include the following in the yml file:

image:
  fmt: image/png
  data: !!binary |
    ...

where ... is the base64 encoded content of the image file. The image file can be encoded / decoded as follows:

# Encode:
base64 image.png > image.b64.txt
# Decode:
base64 -d image.b64.txt > image.png

An image also can be extracted from a recipe:

gorps extract-image -o /tmp/1x1.png examples/recipes/yml/more-beans.yml

Supported Formats:

Import:

Export:

  • The above yml format
  • Markdown
  • svg
  • xml (via templates, e.g. XSL-FO xml output)
  • openrecipes sqlite and xml (not installed by default)
  • html
graph LR
  yml --> gorps{gorps} --> md & yml & svg & xml & html & or_xml[openrecipes xml] & or_sql[openrecipes sql]
  xml --> fop{fop} --> pdf
  html --> weasyprint{weasyprint} --> pdf

Install

Requirement: python >= 3.9.

pip install gorps
# or first clone / cd, then:
pip install .

With support for openrecipes:

pip install --install-option="--extras-require=openrecipes" gorps
# or first clone / cd, then:
pip install .[openrecipes]

Cooklang support

Import

Cooklang documents are imported as follows:

  • Everything, except metadata (lines starting with >> ), including comments is stored unchanged as the instruction field. The content type of the instruction is set to cooklang, so it can be properly formatted to other formats.
  • Tagged ingredients (@ and {}) and cookware (# / {}) are collected from the instruction field.
  • The title is taken from the file name.
  • If an image with extension .jpg, .jpeg, .png is present in the same folder as the .cook file, it is loaded as the image of the recipe.
  • Every field name of the Ingredient class appearing in the metadata, will be passed to the Ingredient instance. Exceptions:
    • ingredients, instruction, title, image, instruction_content_type, cookware already are read in from outside the metadata. If one of the above mentioned keys are present in the metadata, the key / value will be stored in the tags field of the recipe.
    • nutrition_labels: Keys for values in nutrition labels have to be specified prefixed by nutrition_labels., the values have to be of the form number unit. Example:
      >> nutrition_labels.Energy Value: 150 kcal
      
    Specialities:
    • preparation_time / cooking_time: The unit must be one of h, min, sec or the value must be formatted as ##:##:## (3 :).
  • Support for multivalued metadata entries (e.g. categories): Append [] to a key to put the value into a list. Example:
    >> category[]: Main dishes
    >> category[]: Starters
    
    will be loaded as recipe.tags.category = ["Main dishes", "Starters"].

Export

If a recipe has instruction content type cooklang (this is currently only the case if it either has been imported from a .cook file or from a .yml file where instruction_content_type=text/cooklang), the instruction field is written without modification to the output file followed by metadata. Otherwise all ingredient names / cookware names appearing in the instruction as whole words (case insensitive) will be marked up with cooklang tags. All ingredients / cookware not found in the instructions will be appended to the cooklang file as a list. Also here, metadata will be written at the end of the file. Example:

Ingredients:

- @Finely diced bacon{125%g}
- @Clove of garlic{1}
...

Cookware:

- #Pan{}
...

>> description: Chuck Norris? Never heard about her!
...

Usage - Examples

svg

To export the folder examples/recipes/yml/` using a template examples/svg/template.svg:

gorps export --template=examples/svg/template.svg -o /tmp/out.svg examples/recipes/yml/

xml

In this advanced example, we compose the recipes from examples/recipes/yml/ to a menu card.

The template examples/menu-card/xml-fo/template.fo.xml is used. This specific template expects to be used together with the --group-by option (recipes should be grouped by category). Also, we are specifying, that we only want to include the groups Starters, Main courses and Dessert (--group options). With the --variable option we pass to the template some other required parameters, like logos for categories, fonts and more. The example also shows, how to filter the source recipes by title (--title options).

gorps export \
  --template examples/menu-card/xml-fo/template.fo.xml \
  --group-by 'tags["category"]' \
  --group Starters \
  --group "Main courses" \
  --group Dessert \
  --variable-file examples/menu-card/xml-fo/variables.json \
  --title "Beans with Bacon a la Bud Spencer" \
  --title "More Beans" \
  -o /tmp/menucard.fo.xml \
  examples/recipes/yml/

The resulting fo.xml can then be further processed by Apache fop to a pdf:

cp -r examples/menucard/img /tmp
fop /tmp/menucard.fo.xml /tmp/menucard.pdf

Note: The resulting xml file will refer to the fonts Linux Libertine and Linux Biolinum which are part of the Libertine Open Fonts Project.

ℹ️ If you want to use custom fonts, you can specify a font config like this:

fop -c fonts.cfg /tmp/menucard.fo.xml /tmp/menucard.pdf

where fonts.cfg looks like:

<?xml version="1.0"?>
<fop version="1.0">
  <renderers>
    <renderer mime="application/pdf">
      <fonts>
        <directory>/path/to/fonts</directory>
        <auto-detect/>
      </fonts>
    </renderer>
  </renderers>
</fop>

The template syntax is inspired by vue.js: Currently, the following directives are implemented:

  • Text Interpolation
    <span>Title: {{ recipe.title }}</span>
    
  • v-if (Conditionally render an element based on the truthy-ness of the expression value)
    <div v-if="recipe.ingredients">
      {{ recipe.title }}
    </div>
    
  • v-for (Render the element block multiple times based on the source data)
    <div v-for="recipe in recipes">
      {{ recipe.title }}
    </div>
    
    Tuple unpacking is also possible:
    <div v-for="group, recipes in groups.items()">
      {{ group }}: {{ recipes[0].title }}
    </div>
    
  • v-bind (Dynamically bind one or more attributes to an element)
    <!-- bind an attribute -->
    <a v-bind:href="recipe.link">Link</a>
    
    <!-- bind a dict of attributes -->
    <div v-bind="{ 'id': '1', 'class': 'X'}"></div>
    
  • <template> on v-if and v-for

html

Menu card

This is a slight variant of the xml fo example from above. This time, we use html as output format and make use of the --grouped-titles option, to manually group recipes by their titles. Also, --filter-ingredient is used, to remove "obvious" ingredients from the ingredient list.

gorps export \
  --template examples/menu-card/html/menucard.template.html \
  -V title="Beans & Beans" \
  --filter-ingredient Salt \
  --filter-ingredient Pepper \
  --grouped-titles examples/menu-card/html/grouped_titles.json \
  -o /tmp/menucard.html \
  examples/recipes/yml/

The resulting html can be further processed to a pdf by e.g. weasyprint:

cp -r examples/menucard/img examples/menucard/html/style.css /tmp
weasyprint /tmp/menucard.html /tmp/menucard.pdf

Note: see the comment about fonts from the xml fo example.

web

Here, we export a recipe to html, using the same template syntax as for xml:

gorps export \
  --template examples/html/template.html \
  -o /tmp/beans.html \
  examples/recipes/yml/beans.yml

openrecipes

There are two possibilities: either direct export to the sqlite database file or export to an .openrecipe xml file.

sql

To sync to an openrecipes db on an android phone, first pull the db via adb:

adb root
adb pull /data/user/0/org.jschwab.openrecipes/files/database.db /tmp/

Then export all recipes to the sqlite db:

gorps export --fmt openrecipes -o /tmp/database.db examples/recipes/yml/

Finally, replace the db on the phone with the extended version:

adb push /tmp/database.db /data/user/0/org.jschwab.openrecipes/files/database.db
adb kill-server # terminate adb

openrecipe xml

gorps export --fmt openrecipes-xml -o /tmp/out/ examples/recipes/yml/

markdown

To export the folder examples/recipes/ to individual .md files in /tmp/out/:

gorps export --fmt markdown -o /tmp/out/ examples/recipes/yml/

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

gorps-1.0.1.tar.gz (75.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

gorps-1.0.1-py3-none-any.whl (64.9 kB view details)

Uploaded Python 3

File details

Details for the file gorps-1.0.1.tar.gz.

File metadata

  • Download URL: gorps-1.0.1.tar.gz
  • Upload date:
  • Size: 75.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/5.1.1 CPython/3.13.0

File hashes

Hashes for gorps-1.0.1.tar.gz
Algorithm Hash digest
SHA256 1e216877f5a8b93966df328ef38dd4fe41efdf19762e916c6b2c7fd12792b8db
MD5 c6fdba24e0b8f31706772347c76fa9c4
BLAKE2b-256 929d89291aa3cc10002f542ffc8c150ef4debdbe56fb71f696322cc06efcaec5

See more details on using hashes here.

File details

Details for the file gorps-1.0.1-py3-none-any.whl.

File metadata

  • Download URL: gorps-1.0.1-py3-none-any.whl
  • Upload date:
  • Size: 64.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/5.1.1 CPython/3.13.0

File hashes

Hashes for gorps-1.0.1-py3-none-any.whl
Algorithm Hash digest
SHA256 c25e5d8b34c00cca32c2380c2dac29470437e16457e9c7ab3759292294bd41da
MD5 299eb254656ede90ef2a5012f96dbb6a
BLAKE2b-256 d61455f53de5f518c594a19a36a19352843f0b9184c2962f3a5e286ebf02231c

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page