Skip to main content

logo{width=25%}

python version latest version pipeline status License Ruff Checked with mypy

Conversion tool around a yml cooking recipe format.

Install • Usage

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/

Metadata

Release files for gorps 1.0.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for gorps 1.0.1
File Size Uploaded Provenance Actions
gorps-1.0.1.tar.gz 75.8 kB Without provenance Details for gorps-1.0.1.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for gorps 1.0.1
File Interpreter ABI Platform Actions
gorps-1.0.1-py3-none-any.whl Python 3 none any Details for gorps-1.0.1-py3-none-any.whl

Total release size: 140.7 kB

Release files / gorps-1.0.1.tar.gz

Download URL gorps-1.0.1.tar.gz
Size 75.8 kB
Tags Source
SHA-256 checksum
How to use checksums
1e216877f5a8b93966df328ef38dd4fe41efdf19762e916c6b2c7fd12792b8db
BLAKE2b-256 checksum
How to use checksums
929d89291aa3cc10002f542ffc8c150ef4debdbe56fb71f696322cc06efcaec5
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/5.1.1 CPython/3.13.0

Release files / gorps-1.0.1-py3-none-any.whl

Download URL gorps-1.0.1-py3-none-any.whl
Size 64.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
c25e5d8b34c00cca32c2380c2dac29470437e16457e9c7ab3759292294bd41da
BLAKE2b-256 checksum
How to use checksums
d61455f53de5f518c594a19a36a19352843f0b9184c2962f3a5e286ebf02231c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/5.1.1 CPython/3.13.0

Security for gorps 1.0.1

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page