]> OzVa Git service - bread/commitdiff
Completed renderer
authorMax Value <greenwoodw50@gmail.com>
Mon, 12 May 2025 19:36:35 +0000 (20:36 +0100)
committerMax Value <greenwoodw50@gmail.com>
Mon, 12 May 2025 19:36:35 +0000 (20:36 +0100)
+ stylesheet
+ rendering individual to latex
+ rendering individual to html
+ homepage
+ page on bakers percentage
+ gitignore
+ extra feilds to example recipe

.gitignore [new file with mode: 0644]
pages/percentage.html [new file with mode: 0644]
sous.py
src/straight-white.toml
style.css
templates/index.tex [new file with mode: 0644]
templates/template.html
templates/template.tex

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..596f14d
--- /dev/null
@@ -0,0 +1,3 @@
+build/*
+recipes/*
+index.html
diff --git a/pages/percentage.html b/pages/percentage.html
new file mode 100644 (file)
index 0000000..d5f747b
--- /dev/null
@@ -0,0 +1,122 @@
+<!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>&Therefore;</mo>
+                                                       <mn>1</mn>
+                                                       <mtext>%</mtext>
+                                               </mtd>
+                                               <mtd>
+                                                       <mo>&approx;</mo>
+                                               </mtd>
+                                               <mtd>
+                                                       <mn>59.70</mn>
+                                                       <mtext>g</mtext>
+                                               </mtd>
+                                       </mtr>
+                               </mtable>
+                       </math>
+               </main>
+       </body>
+</html>
diff --git a/sous.py b/sous.py
index d6171c43c28885b80a0cd003cdfe47e6b6f2c540..3671b91d618d6ac505e2bfc2a73d91310e612fc0 100755 (executable)
--- a/sous.py
+++ b/sous.py
@@ -1,13 +1,82 @@
-#! /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()
 
 
index e0c8c35603bd71a00540f86677d4db33c02bf185..b81aad50bb6784975622119397604a5d7e6967e6 100644 (file)
@@ -1,5 +1,17 @@
 [information]
 title = "Straight white yeasted loaf"
+description = "This is the note"
+
+keywords = ["white", "yeasted"]
+
+notes = """
+This is the note
+
+
+
+This is a line break
+"""
+
 
 [ingredients]
 white-flour = 90
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..29b228e9a3e486fa1c0aab0cd41bf4c5a72eb37f 100644 (file)
--- a/style.css
+++ b/style.css
@@ -0,0 +1,13 @@
+body {
+       font-family: sans-serif;
+}
+main {
+       width: min(750px, 80%);
+       margin: auto;
+}
+table {
+       width: 100%;
+}
+th {
+       text-align: left;
+}
diff --git a/templates/index.tex b/templates/index.tex
new file mode 100644 (file)
index 0000000..e69de29
index 4af76622a2b97dc6dde66c9ab51fb78bdc894b90..ac544b5ef66922d05d389998784faf018e3a36b8 100644 (file)
@@ -1,12 +1,59 @@
 <!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 &lt; 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>
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..6881d92b5f7493e21f56b95edfdc75dd74cfb055 100644 (file)
@@ -0,0 +1,47 @@
+\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}