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.0.tar.gz (56.8 kB view details)

Uploaded Source

Built Distribution

gorps-1.0.0-py3-none-any.whl (38.1 kB view details)

Uploaded Python 3

File details

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

File metadata

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

File hashes

Hashes for gorps-1.0.0.tar.gz
Algorithm Hash digest
SHA256 b96b02a83ad8d45cf48f29f7f621d01452aea0ad46095b3cd3c54adcf6a970a2
MD5 77e121df1a3d26af7ca0138d133953fe
BLAKE2b-256 cab32b186ee0011ef9dd8fdbc5fc6938eb06e3abbb7f4fdacb8d88907d050843

See more details on using hashes here.

File details

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

File metadata

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

File hashes

Hashes for gorps-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 172875a15c64d31bbdb20e1db3249ce5a05be03809a9c9f3811535f63e037f8c
MD5 9fb77317eee8e8803ea5fb61c02c8707
BLAKE2b-256 cfdbb80aa40c6c2b2a360b80179e088e86cc01944f77baf69bca0be0065df83d

See more details on using hashes here.

Supported by

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