--- /dev/null
+<!DOCTYPE html>
+<html lang="">
+ <head>
+ <meta charset="utf-8">
+ <link rel="stylesheet" href="../style.css">
+ <title>Bread</title>
+ </head>
+ <body>
+ <main>
+ <a href="../index.html">Back to main page</a>
+ <h1>Bakers Percentage</h1>
+ <p>
+ To avoid making mistakes when converting recipe quantities, a system called "bakers percentage" is used. Each ingredient is given as a percentage where flour usually represents 100%. This way recipies can be easily compared to each other with flour as the common ground between the two.
+ </p>
+ <p>
+ Take a look at these two recipes:
+ </p>
+ <table>
+ <tr>
+ <th></th>
+ <th>Recipe 1</th>
+ <th>Recipe 2</th>
+ </tr>
+ <tr>
+ <td>Flour</td>
+ <td>100%</td>
+ <td>100%</td>
+ </tr>
+ <tr>
+ <td>Water</td>
+ <td>65%</td>
+ <td>74%</td>
+ </tr>
+ <tr>
+ <td>Olive oil</td>
+ <td>-</td>
+ <td>6%</td>
+ </tr>
+ <tr>
+ <td>Yeast</td>
+ <td>1%</td>
+ <td>1%</td>
+ </tr>
+ <tr>
+ <td>Salt</td>
+ <td>1.5%</td>
+ <td>1.5%</td>
+ </tr>
+ <table>
+ <p>
+ If all these figures where given as weights (and one recipe was portioned to produce significantly more) it might be difficult to compare the two recipes. However, becasue of the percentages it becomes easy to see that they both have a hydration of 65% and 74% respectively. This tells us recipe 1 is likely to be conventionally kneeded white yeasted bread and recipe 2 is likely to be some lighter bread with an airier crumb such as Ciabatta.
+ </p>
+ <p>
+ If we then wanted to make either of these recipes, this would be easy. Simply add up the percentages to get a total then divide the target weight by this figure to work out what "1%" means in terms of grams. This can then be multiplied by the percentage of each ingredient to get the weight of each for your target quantity.
+ </p>
+ <math>
+ <mtable>
+ <mtr>
+ <mtd>
+ <mn>100</mn>
+ <mtext>%</mtext>
+ <mo>+</mo>
+ <mn>65</mn>
+ <mtext>%</mtext>
+ <mo>+</mo>
+ <mn>1</mn>
+ <mtext>%</mtext>
+ <mo>+</mo>
+ <mn>1.5</mn>
+ <mtext>%</mtext>
+ </mtd>
+ <mtd>
+ <mo>=</mo>
+ </mtd>
+ <mtd>
+ <mn>167.5</mn>
+ <mtext>%</mtext>
+ </mtd>
+ </mtr>
+ <mtr>
+ <mtd>
+ <mtext>Total weight (%)</mtext>
+ </mtd>
+ <mtd>
+ <mo>=</mo>
+ </mtd>
+ <mtd>
+ <mn>167.5</mn>
+ <mtext>%</mtext>
+ </mtd>
+ </mtr>
+ <mtr>
+ <mtd>
+ <mtext>Total weight (kg)</mtext>
+ </mtd>
+ <mtd>
+ <mo>=</mo>
+ </mtd>
+ <mtd>
+ <mn>10</mn>
+ <mtext>kg</mtext>
+ </mtd>
+ </mtr>
+ <mtr>
+ <mtd>
+ <mo>∴</mo>
+ <mn>1</mn>
+ <mtext>%</mtext>
+ </mtd>
+ <mtd>
+ <mo>≈</mo>
+ </mtd>
+ <mtd>
+ <mn>59.70</mn>
+ <mtext>g</mtext>
+ </mtd>
+ </mtr>
+ </mtable>
+ </math>
+ </main>
+ </body>
+</html>
-#! /bin/python
+#! .venv/bin/python
import tomllib
-import os
+from os import path, listdir, system
+from jinja2 import Environment, FileSystemLoader
+from datetime import datetime
-class recipe:
+class RecipeBook:
def __init__(self):
- pass
+ self.html_environment = Environment(loader = FileSystemLoader("templates"))
+ self.latex_environment = Environment(
+ loader = FileSystemLoader("templates"),
+ block_start_string = "|%",
+ block_end_string = "%|",
+ variable_start_string = "|~",
+ variable_end_string = "~|",
+ comment_start_string = "|#",
+ comment_end_string = "#|",
+ trim_blocks = True,
+ lstrip_blocks = True
+ )
+ self.recipes = []
-with open("recipes/straight-white.toml", "rb") as f:
- recipe = tomllib.load(f)
+ for filename in listdir("src"):
+ self.recipes.append(Recipe(filename))
+
+ def render_html(self):
+ keywords = {}
+
+ for recipe in self.recipes:
+ recipe.render_html(self.html_environment)
+
+ for word in recipe.recipe["information"]["keywords"]:
+ if word in keywords:
+ keywords[word].append((recipe.recipe["information"]["title"], recipe.recipe["html"]))
+ else:
+ keywords |= { word: [(recipe.recipe["information"]["title"], recipe.recipe["html"])]}
+
+ template = self.html_environment.get_template("index.html")
+ with open("index.html", "w") as f:
+ f.write(template.render(recipes = self.recipes, keywords = keywords, time = datetime.now()))
+
+ def render_latex(self):
+ for recipe in self.recipes:
+ recipe.render_latex(self.latex_environment)
+
+class Recipe:
+ def __init__(self, filename: str):
+ with open(path.join("src", filename), "rb") as f:
+ self.recipe = tomllib.load(f)
+
+ total = sum([int(x) for x in self.recipe["ingredients"].values()])
+ self.recipe.update(total = total)
+ self.recipe.update(kg = 1000 / total)
+ self.recipe.update(filename = filename)
+ self.recipe.update(html = filename[:-5] + ".html")
+ self.recipe.update(tex = filename[:-5] + ".tex")
+ self.recipe.update(pdf = filename[:-5] + ".pdf")
+ self.recipe.update(image = path.isfile(f"media/{filename[:-5]}.png"))
+
+ def render_html(self, environment: Environment):
+ template = environment.get_template("template.html")
+
+ with open(path.join("recipes", self.recipe["html"]), "w") as f:
+ f.write(template.render(recipe = self.recipe, time = datetime.now()))
+
+ def render_latex(self, environment: Environment):
+ template = environment.get_template("template.tex")
+
+ with open(path.join("build", self.recipe["tex"]), "w") as f:
+ f.write(template.render(recipe = self.recipe))
+
+ system(f"pdflatex -interaction='nonstopmode' -output-directory='build' 'build/{self.recipe["tex"]}'")
+ system(f"mv build/{self.recipe["pdf"]} recipes")
+
+# main
+
+recipe_book = RecipeBook()
+recipe_book.render_html()
+recipe_book.render_latex()
<!DOCTYPE html>
<html lang="">
- <head>
- <meta charset="utf-8">
- <title></title>
- </head>
- <body>
- <header></header>
- <main></main>
- <footer></footer>
- </body>
+ <head>
+ <meta charset="utf-8">
+ <link rel="stylesheet" href="../style.css">
+ <title>{{ recipe.information.title }}</title>
+ </head>
+ <body>
+ <main>
+ <a href="../index.html">Back to main page</a>
+ <h1>{{ recipe.information.title }}</h1>
+ <a href="https://git.ozva.co.uk/?p=bread;a=history;f=src/{{ recipe.filename }};">[History]</a> -
+ <a href="{{ recipe.pdf }}">[PDF]</a>
+ <p>{{ recipe.information.description }}</p>
+ <h2>Notes</h2>
+ {{ recipe.information.notes|replace("\n", "<br>") }}
+ <h2>Ingredients</h2>
+ <table>
+ <tr>
+ <th></th>
+ <th>Bakers %</th>
+ <th>500g total</th>
+ <th>1kg total</th>
+ <th>2kg total</th>
+ <th>3kg total</th>
+ <th>4kg total</th>
+ </tr>
+ {% for name, weight in recipe.ingredients.items() %}
+ <tr>
+ <td>{{ name|replace("-", " ")|title }}</td>
+ <td>{{ weight }}%</td>
+
+ {% macro weighted(multiplier) %}
+ {% set rounded = (weight * recipe.kg * multiplier)|round|int %}
+ {% set real = (weight * recipe.kg * multiplier)|round(2) %}
+ {% set error = (real - rounded)|abs %}
+ {% if error < real * 0.05 %}
+ {{ rounded }}
+ {% else %}
+ {{ real }}
+ {% endif %}
+ {% endmacro %}
+
+ <td>{{ weighted(0.5) }}</td>
+ <td>{{ weighted(1) }}</td>
+ <td>{{ weighted(2) }}</td>
+ <td>{{ weighted(3) }}</td>
+ <td>{{ weighted(4) }}</td>
+ </tr>
+ {% endfor %}
+ </table>
+ <p><em>
+ Percentage total: {{ recipe.total }}%<br>
+ Values have been rounded where the error would not be significant (p < 0.05).
+ </em></p>
+ <p>Last compiled at <a href="https://git.ozva.co.uk/?p=bread;a=commit;h=HEAD">{{ time }}</a></p>
+ </main>
+ </body>
</html>
+\documentclass{article}
+
+\usepackage[margin=2cm]{geometry}
+\usepackage[scaled]{helvet}
+\usepackage[T1]{fontenc}
+\renewcommand\familydefault{\sfdefault}
+
+\begin{document}
+
+\section*{|~ recipe.information.title ~|}
+
+|~ recipe.information.description ~|
+
+\subsection*{Notes}
+
+|~ recipe.information.notes|replace("\n", "\n\n") ~|
+
+\subsection*{Ingredients}
+
+\renewcommand{\arraystretch}{1.2}
+\begin{tabular}{lllllll}
+ & \textbf{Bakers \%} & \textbf{500g total} & \textbf{1kg total} & \textbf{2kg total} & \textbf{3kg total} & \textbf{4kg total} \\
+ \hline
+ |% for name, weight in recipe.ingredients.items() %|
+ |~ name|replace("-", " ")|title ~| &
+ |~ weight ~|\% &
+
+ |% macro weighted(multiplier) %|
+ |% set rounded = (weight * recipe.kg * multiplier)|round|int %|
+ |% set real = (weight * recipe.kg * multiplier)|round(2) %|
+ |% set error = (real - rounded)|abs %|
+ |% if error < real * 0.05 %|
+ |~ rounded ~|
+ |% else %|
+ |~ real ~|
+ |% endif %|
+ |% endmacro %|
+
+ |~ weighted(0.5) ~| &
+ |~ weighted(1) ~| &
+ |~ weighted(2) ~| &
+ |~ weighted(3) ~| &
+ |~ weighted(4) ~| \\
+ |% endfor %|
+\end{tabular}
+
+\end{document}