Giter Site home page Giter Site logo

recipemd's People

Contributors

dadada avatar dasnessie avatar evylon avatar gindex avatar tstehr avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

recipemd's Issues

A Link reference defintion is dropped

I use Reference-style images with a data-URL. When fed into recipemd, the definition is gone.

Testcase test.md:

# Title
---
- *1* Thing
---
Image: ![Step 1][step_1].


[step_1]: data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAABg2lDQ1BJQ0MgcHJvZmlsZQAAKJF9kT1Iw0AcxV9TRZEWBzuoOGSoThakijhqFYpQIdQKrTqYXPoFTRqSFBdHwbXg4Mdi1cHFWVcHV0EQ/ABxdHJSdJES/5cUWsR4cNyPd/ced+8AoVFhmtU1AWi6baaTCTGbWxV7XiFgCGHEEZOZZcxJUgq+4+seAb7exXiW/7k/R1jNWwwIiMSzzDBt4g3i6U3b4LxPHGElWSU+Jx436YLEj1xXPH7jXHRZ4JkRM5OeJ44Qi8UOVjqYlUyNeIo4qmo65QtZj1XOW5y1So217slfGMrrK8tcpzmCJBaxBAkiFNRQRgU2YrTqpFhI037Cxz/s+iVyKeQqg5FjAVVokF0/+B/87tYqTMa9pFAC6H5xnI9RoGcXaNYd5/vYcZonQPAZuNLb/moDmPkkvd7WokdA/zZwcd3WlD3gcgcYfDJkU3alIE2hUADez+ibcsDALdC35vXW2sfpA5ChrlI3wMEhMFak7HWfd/d29vbvmVZ/P8BWcsbntpxwAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAC4jAAAuIwF4pT92AAAAB3RJTUUH5gIZDTQc/OsloQAAABl0RVh0Q29tbWVudABDcmVhdGVkIHdpdGggR0lNUFeBDhcAAASbSURBVHja7ZpfKHtvHMffB1FqirSy3MzFXAxT86fIHW4kl9wSteRK7dqtQruhVrsgLia18v8GSdLMNhNaoRWN5c+GHWG2c/b53Z2f2bCD7+9Pnk+dep49z+dzPu/X2fPvdDgiIvxiy8AvNwaAAWAAGAAGgAFgABgABoABYAAYAAagubkZHMeB4zjEYrH/rSg5Otg/gAFgAH63cTzPU15eXtoOoVAIBQUFKdvC4TB2dnbgcDhwcHCAvb09qNVq6HQ61NXVob6+HoWFhWndY3t7Gy6XC263G0dHR1CpVNBoNFCpVNBqtSgvL4darUZ2djYA4OHhAV/SwfM8AUj7CoVC9NZEUaT5+XnSaDQf+iqVSpqdnSVRFOk9W15eJqVSmVYuVqtV8vuqjm8DEASBhoeHZcUYHx9PKd5ut8uK8yMAXifQ1NQkdYhGo5SO2Wy2hCc8OTlJPp+PHh8fSRRFikQidHZ2RuPj4wkJuN3upFhdXV1S+8DAAHm9XuJ5ngRBIEEQKBwOUyAQIJfLRWazmebn51PmJEfHtwAEg0FSKBQEgCoqKsjn833Y3+l0SvF7enooHo9LbbFYTGpra2ujWCxGXzU5Or61Cqyvr+Ph4QEAYDKZUFJS8mH/qqoqDA4OAgAsFguurq7+no05DkqlEgCgVCqRlZX1318G19bWEsSlY7W1tVLZ7/dL5czMTPT390twZmZmEA6H/ziAL2OOxWIwm81SXavVSuVoNCqVn5+fpWXqrfE8n1Dv7OzE2toaVlZW0N7eDgDo7e1FbW0tSkpKoFarUVRUhIyMH9y+fHXs3N/fy5p1U12Li4sp405NTVF1dXVKn5aWFrLZbBQOh39kDvgygNvb228DWFhYeDd+NBoln89Hq6urNDo6Sq2trQm+DQ0NdHx8/O8BiEQiUl+NRvPh5uYnLB6P08XFBVksFum+9fX19Pj4+HOrgJyxlZOTg46ODgDA8fExAoHAn92zcxxUKhW6u7sxNDQEANja2sLh4WHyzC5DR0LP13tpURQ/dW5tbZXKc3Nz/9gBRqfTSeXr6+ukdjk6EgAUFRVJ5bu7u08TaWxshEKhAAD09fVhaWkJ6XxvcXZ2homJiYTfLi8vMT09nXK1eDNpw+l0SvXc3NykPrJ0vB4Pr8fX8PAwXV9fkyAIaW+FAZDRaCSHw0E3NzcUjUZJFEV6enqi8/Nz2tjYIKPRSADIYDAkxLm4uCAAVFxcTKOjo7S7u0vBYJBeXl5IEATieZ68Xi8NDg4m3M/v9yflJEdHAoD9/X3ZhyFRFGlsbEz2CvAeADmXxWJJKUqODrydaUdGRmQfh+PxOK2vr1NDQ0NaiZtMJrq8vEyKsbe3RwaD4VN/hUJBVqv13acqRwf39iOpeDwOj8eDzc1NOBwOeL1e7O/vp/VCJBKJwOPxwOPxwO124+TkBKFQCGVlZaiurkZlZSX0ej3y8/M/HOOnp6dwuVyw2+1wuVy4urpCaWkp9Ho99Ho9ampqpHPDe5auDo59JcZeijIADAADwAAwAAwAA8AAMAAMAAPAADAADAADwAAwAAwAA8AAMAAMAAPwS+wvNfSdDz8/sGoAAAAASUVORK5CYII=

Output of recipemd test.md:

# Title

---

- *1* Thing

---

Image: ![Step 1][step_1].

crash when writing to same file

When piping the output of recipemd to the file that it reads from, it crashes with

Traceback (most recent call last):
  File "/home/bart/.local/bin/recipemd", line 11, in <module>
    load_entry_point('recipemd==4.0.3', 'console_scripts', 'recipemd')()
  File "/home/bart/programming/python/recipemd/.pyvenv/lib/python3.7/site-packages/recipemd/cli/main.py", line 40, in main
    r = rp.parse(src)
  File "/home/bart/programming/python/recipemd/.pyvenv/lib/python3.7/site-packages/recipemd/data.py", line 154, in parse
    self._parse_title()
  File "/home/bart/programming/python/recipemd/.pyvenv/lib/python3.7/site-packages/recipemd/data.py", line 183, in _parse_title
    raise RuntimeError(f"Invalid, title (heading with level 1) required, got "
RuntimeError: Invalid, title (heading with level 1) required, got None instead

steps to reproduce:

echo '# FOO

**4 bar**

---

- *4* baz

---

FooBarBaz
' > test.md
recipemd test.md > test.md

round ingredient amounts

When scaling a recipe, especially when specifying a yield, the ingredient results can become pretty unreadable.

For example:

$ recipemd -y "4 servings" -i celeriac_mushroom_and_tomato_lasagna.md 
[...]
0.5714285714285714285714285714 small celeriac root
1.714285714285714285714285714 parsnip roots
0.5714285714285714285714285714 tbsp coconut oil, butter, ghee or olive oil
1.142857142857142857142857143 clove garlic
11.42857142857142857142857143 brown mushrooms
142.8571428571428571428571428 g frozen spinach, thawed (fresh is fine too)
11.42857142857142857142857143 cherry tomatoes (or 4 regular tomatoes), sliced

Also, I'm not really sure how to measure out 0.5714285714285714285714285714 tbsp of something. I think rounding to two or three decimals should suffice for most use cases.

Approximate amount specifier

In #38 a did a survey of recipes that will be considered invalid once we fix the incorrect treatment of amounts without factors. On thing that came out of that is users like to mark amounts as approximate, in particular for the yield amount of a recipe.

Of the 43 recipes that had an error, 12 were instances of approximate yields. Additionally there was one recipe using an approximate ingredient amount.

Use cases

The survey turned up 3 use cases:

  1. indicate that a yield is using an inexact unit (mostly servings, where a serving size can vary wildly), see also "Lerngruppz" for a related approach
  2. for yields of a certain number of pieces (e.g. cookies or muffins), indicate that it's normal if you don't exactly reach that number
  3. recipes where you add an ingredient as needed (e.g. add flour until a certain consistency is reached)

I've also wanted a syntax like this in my own use from time to time, mostly for uses cases 2 and 3.

Syntax and spec ideas

For syntax 9 recipes used ~ as a prefix for the amount (~3 Portionen) the remaining 4 used ca/ca.. I'd also prefer using ~ independently of existing use as it is not language specific.

For the spec right now my idea is to allow an optional ~ prefix for amounts in yields and ingredients. This translates into a boolean approximate flag added to the amount data type. The semantics of that flag would be "The recipe author has indicated that the amount is approximate". If a more detailed explanation of what that entails is required authors need to put that in the instructions.

Proposal: Git pre-commit hook for RecipeMD format validation

I like the idea of managing my recipes with recipemd and git ❤️ 🚀 👍 Thank you for the tool!

To be sure that only valid recipes are committed, it is convenient to use a pre-commit hook. For me, I created a hook that can be used out-of-the-box with pre-commit. Here is the repo.

Do you think it is worth mentioning in the Recommended Tools section? If welcomed, I can submit the PR.

IngredientGroups can't be terminated

Since IngredientGroups can't be terminated, it is impossible to create a recipe where an Ingredient follows an ingredient group. It is however possible in the AST which can lead to unexpected behaviour.

Example:

Recipe(
    title="Test Recipe",
    ingredients=[
        Ingredient(name="Ingredient 1"),
        IngredientGroup(
            title="Test Group",
            children=[
                Ingredient(name="Group Ingredient 1"),
                Ingredient(name="Group Ingredient 2")
            ]
        ),
        Ingredient(name="Ingredient 2"),
    ]
)

Serialization:

# Test Recipe

---

- Ingredient 1

## Test Group

- Group Ingredient 1
- Group Ingredient 2
- Ingredient 2

Parse Result:

Recipe(
    title="Test Recipe",
    ingredients=[
        Ingredient(name="Ingredient 1"),
        IngredientGroup(
            title="Test Group",
            children=[
                Ingredient(name="Group Ingredient 1"),
                Ingredient(name="Group Ingredient 2"),
                Ingredient(name="Ingredient 2")
            ]
        )
    ]
)

We need to either introduce a group terminator or disallow such ASTs.

Indentation on recipemd.org

On mobile, the indentation for bulleted lists on recipemd.org looks very weird. I feel that the bulleted lists should be at the same indentation level as the numbered lists.
Here's a quick screenshot I took:

Screenshot_20200307-132246_Firefox

Reference implementation/testcases differ from specification

As a long-time user of the RecipeMD format for storing and displaying my recipe collection, I am deeply grateful for your work on this project.

I am currently implementing a RecipeMD parser in Rust based on the specification, and I am encountering a few issues. In some aspects, the reference implementation and its accompanying testcases show and enforce behavior that differs from what I expect based on the specification.

(I previously created a TypeScript implementation, but since it is a direct port of the Python reference implementation, it has the exact same behavior as the latter one.)


Serialization

The serialized representation (e.g. as JSON) is not specified which results in some uncertainties for testing and general interoperability problems between different implementations.

Main aspects:

  • naming of fields
  • casing of fields: e.g. ingredient_groups vs. ingredientGroups
  • representation of empty fields: [] (for list-like data types), null or complete absence of the field
  • ordering of fields: specific order, alphabetical or any order
  • whitespace: should leading/trailing newlines and other whitespace be stripped?

Data types

In the following, I use some pseudocode syntax to describe the structure/schema of different data type representations. I hope it helps to represent them language-independently (is there an actual standard for such a format?).

Recipe

The reference implementation assigns ingredients without a preceding heading to a ingredients field of the recipe. The specification only mentions a field that holds ingredient groups.

Schema descriptions

Specification

Recipe {
    title: String,
    description: String | None,
    tags: String[1..n] | None,
    yields: Amount[1..n] | None,
    ingredients: IngredientGroup[1..n],
    instructions: String | None,
}

Reference implementation

Recipe {
    title: String,
    description: String | None,
    tags: String[0..n],
    yields: Amount[0..n],
    ingredients: Ingredient[0..n],
    ingredient_groups: IngredientGroup[0..n],
    instructions: String | None,
}

Ingredient Group

The reference implementation uses nested ingredient groups based on the heading level. Such behavior is not defined by the specification.

The specification does not allow ingredient groups with no ingredients (“1..n ingredients”). The reference implementation accepts them and many testcases require them and have no ingredients at all.

Schema descriptions

Specification

IngredientGroup {
    title: String | None,
    ingredients: Ingredient[1..n],
}

Reference implementation

IngredientGroup {
    title: String | None,
    ingredients: Ingredient[0..n],
    ingredient_groups: IngredientGroup[0..n],
}

Amount

See #38.

Schema descriptions

Specification

Amount {
    factor: Number,
    unit: String | None,
}

Reference implementation

Amount {
    factor: Number | None,
    unit: String | None,
}

What now?

Regarding serialization, it would be great if the JSON representation was formalized and a JSON Schema would be created.

Regarding nested ingredient groups, I think they make sense and should be incorporated into the specification.

Regarding empty ingredient groups, I think they are useful when drafting a recipe and should be allowed by the specification.


I am willing to discuss any changes that need to be taken, and I am happy to help where I can.

Add a license

The library and specification should have a license. I propose to license both under the LGPL.

All persons who have in the past contributed to this repository need to agree to this change. According to the repository statistics the contributors are:

Please comment below whether you agree to licensing your contribution to RecipeMD under the LGPL. I'd be happy to discuss licensing options or other concerns.

Fix inconsistent treatment of amounts without factors

Amounts are required to have a factor, but the implementation allows factors to be None:

class Amount:
factor: Optional[Decimal] = field(

There are also testcases for amounts in ingredients and yields without a factor:

**1.2 cups, 1,5 Tassen, 1 1/4 servings, 5 servings, factorless, 5**

- *hey* amount is valid without factor

We need to decided wether factorless amounts should be allowed and then either adopt the spec or the implementation and the testcases to be consistent with that decision.

Discuss syntax for pair-shaped metadata

Issue spun out of the discussion in PR #46.

There's a wish from some users to add some sort of structured metadata to recipes. Examples of metadata include:

  • source
  • times (baking, cooking, cooling, resting, active, total, …) (see also #27)
  • taste
  • vegetarian: yes/no
  • nutrition information

Some of these can be incorporated into tags, e.g. by adding the taste as a tag or tagging a recipe as vegeratian. But that doesn't really work for pair-shaped metadata like times or nutrition information. For these the only solution currently is to put them into the recipe description, instructions or tags in an ad-hoc format. This has the drawback that different recipe author are going to choose different formats which is not bad per se but it would be nice to have a consistent format to enable tooling support.

State of the art

To figure out where recipe authors put data like this, I did an informal "survey" by looking at some cookbooks I own and recipe websites I tend to use:

  • Recipe metadata usually occurs in these places (sorted by roughly by number of appearances in my non-representative sample)
    1. before the ingredient list
    2. after the instructions at the bottom of the page
    3. after the ingredient list
    4. after the recipe title before the description
  • It's also common to split metadata by type, for example seriouseats.com puts cooking times with the yields before the ingredients, while nutrition information is located below the instructions.

Requirements for RecipeMD

From the discussion in #46 I extracted the following set of requirements that I'd like to satisfy

The overarching concern of adding a feature like this to RecipeMD is that it should feel natural and fit the way people might author their recipes in Markdown if not guided by the specification or tooling requirements.

  • The source text should follow the markdown design philosophy:

    Markdown-formatted document should be publishable as-is, as plain text, without looking like it’s been marked up with tags or formatting instructions.

    This includes using a minimal amount of additional required markup and retaining a natural reading order.

  • The output of running the RecipeMD file to a commonmark renderer should look reasonable (e.g. converting to HTML or to PDF for printing via pandoc).

  • The metadata format should allow tooling to do stuff with the metadata in a way that makes it more useful than ad-hoc metadata in the description.

  • If the metadata embedded in RecipeMD uses a pre-existing format, it should fit with RecipeMD in spirit and be easy to understand and author.

Approaches

So far two approaches have been suggested. We're open for arguments for/against both of these and also for additional suggestions.

Extended Tags

In #46 I considered using tags as a more generic meta data field, by allowing colon-separated key-value-pairs there:

*vegan, summer, calories/serving: 39, protein/serving: 1.3 g, sugar/serving: 5.9 g,
fat/serving: 0.4 g, active time: 45 min, total time: 2h*

Metadata as fenced code blocks

In discussion with @AberDerBart an alternative approach came up. We could allow adding a fenced code block (aka ```-block) where tags and yields are currently located and parse its contents (as YAML or a something like that):

*vegan, summer*

```yaml
calories/serving: 39
protein/serving: 1.3 g
sugar/serving: 5.9 g
fat/serving: 0.4 g
active time: 45 min
total time: 2h
```

Add times for specification

I plan to move my recipes from my Database to markdown files. But I have information about

  • baking time
  • cooling time
  • cooking time
  • rest time

Theses informations are very useful but specifications not allow me to write down. Where can I put them ? Should I put them into tags ?

Ignore YAML Frontmatter

This is similar to #46 which was closed as supporting metadata through a YAML frontmatter was not deemed a good fit in favour of supporting pair based metadata in #47 .

However would possible for RecipeMD to simply ignore YAML frontmatter, or anything that comes before the title?

Currently I am trying to add my personal recipes to my personal notes folder, which already uses YAML frontmatter for a number of unrelated functions. For example, I would just like to be able have a creation date and a type metadata identifying it as a recipe in the frontmatter. Like this:

---
created: 2023-09-25
type: recipe
---

# Title of Recipe

I use some other Markdown processors that do use this metadata, however when I add it to my recipes I can't use RecipeMD to parse them anymore. Giving it the above document will break parsing altogether.

I don't really want you to implement anything for the metadata, just to ignore anything in a frontmatter block, or anything before the title. I don't think this would clash with any of the existing spec as a title is required anyway.

crash since last python update

Since I upgraded to Python 3.9.1, I get a crash when trying to use recipemd-find or recipemd:

$ recipemd-find tags
Traceback (most recent call last):
  File "/usr/bin/recipemd-find", line 5, in <module>
    from recipemd.cli.find import main
  File "/home/nessie/.local/lib/python3.9/site-packages/recipemd/cli/find.py", line 23, in <module>
    from recipemd.data import RecipeParser, Recipe
  File "/home/nessie/.local/lib/python3.9/site-packages/recipemd/data.py", line 12, in <module>
    import commonmark
  File "/home/nessie/.local/lib/python3.9/site-packages/commonmark/__init__.py", line 4, in <module>
    from commonmark.main import commonmark
  File "/home/nessie/.local/lib/python3.9/site-packages/commonmark/main.py", line 14, in <module>
    from commonmark.blocks import Parser
  File "/home/nessie/.local/lib/python3.9/site-packages/commonmark/blocks.py", line 5, in <module>
    from commonmark import common
  File "/home/nessie/.local/lib/python3.9/site-packages/commonmark/common.py", line 14, in <module>
    HTMLunescape = html.parser.HTMLParser().unescape
AttributeError: 'HTMLParser' object has no attribute 'unescape'

Bug when trying to use "flatten"?

I got an unhandled exception:

$ recipemd -f chili-kaese-spaetzle.md 
Traceback (most recent call last):
  File "/usr/bin/recipemd", line 8, in <module>
    sys.exit(main())
  File "/home/nessie/.local/lib/python3.9/site-packages/recipemd/cli/main.py", line 55, in main
    r = _get_flattened_recipe(r, recipe_url=recipe_url, parser=rp)
  File "/home/nessie/.local/lib/python3.9/site-packages/recipemd/cli/main.py", line 151, in _get_flattened_recipe
    recipe = _create_flattened_substituted_ingredients(recipe, ingr_to_recipe)
  File "/home/nessie/.local/lib/python3.9/site-packages/recipemd/cli/main.py", line 246, in _create_flattened_substituted_ingredients
    flattened_ingr = _create_flattened_substituted_ingredients(ingr, ingr_to_recipe)
  File "/home/nessie/.local/lib/python3.9/site-packages/recipemd/cli/main.py", line 254, in _create_flattened_substituted_ingredients
    return replace(
TypeError: replace() missing 1 required positional argument: 'obj'

Fenced code blocks at the end of instructions are cut off

When a fenced code block is the last thing in the instructions, the closing ``` inside the recipe.instructions property is sometimes cut off. When there are fewer than 3 characters in the the last line of the code block, only this many backticks are returned in the string. The bug doesn't happen with more content after the code block.

I think the problem is near

RecipeMD/recipemd/data.py

Lines 410 to 415 in 3146efb

while self.current is not None and (predicate is None or predicate(self.current)):
end = self.current.sourcepos[1]
self._next_node()
if end is not None:
return self._get_sourcepos_source((start, end))

Test1.md (working)

# Bug
---
- *1* Thing
---
Make something.
```
abc
d
```
Have fun!

Output of recipemd Test1.md

# Bug

---

- *1* Thing

---

Make something.
```
abc
d
```
Have fun!

Test2.md (bug)

# Bug
---
- *1* Thing
---
Make something.
```
abc
d
```

Output of recipemd Test2.md

# Bug

---

- *1* Thing

---

Make something.
```
abc
d
`

There is only one backtick in the output.

Test3.md (bug)

# Bug
---
- *1* Thing
---
Make something.
```
abc
de
```

Output of recipemd Test3.md

# Bug

---

- *1* Thing

---

Make something.
```
abc
de
``

There are only two backticks in the output.

Test4.md (working)

# Bug
---
- *1* Thing
---
Make something.
```
abc
def
```

Output of recipemd Test4.md

# Bug

---

- *1* Thing

---

Make something.
```
abc
def
```

Alternative ingredients

Got this idea from a pizza recipe, where it specified multiple valid Flour types.
While #4 could be used to express this thing, I think that it is a bit clumsy to express such xor Relations for single ingredients.

My suggestion:

- *1kg* Weizenmehl Type 405
  or *1kg* Weizenmehl Type 550

For the spec, we consider it an alternative ingredient, if it comes after a linebreak and contains a valid amount. The text before the amout is ignored.

I know that this won't work with ingredients without amount, but it was the best idea I could think of.

Also useful for fresh and dried yeast (different amounts/amount units)

Meta-Recipes

Recipes should have the the ability to use other recipes as Ingredients. I propose roughly the following syntax:

# Nachos

---

- 200g [Guacamole](guacamole.md)
- 200g [Refried Beans](refried_beans.md)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.