Browse Source

Initial version of cookbook

master
Sebastian 4 months ago
commit
3747f8f381
18 changed files with 612 additions and 0 deletions
  1. +28
    -0
      README.md
  2. BIN
      figures/Coatimundi_lineart.png
  3. BIN
      figures/cc-by.png
  4. BIN
      figures/cc-sa.png
  5. BIN
      figures/cc.png
  6. BIN
      figures/coati.jpg
  7. BIN
      figures/coati.pdf
  8. +66
    -0
      figures/coati.svg
  9. BIN
      figures/coati2.jpg
  10. +66
    -0
      figures/coati2.svg
  11. BIN
      figures/kangaroo.pdf
  12. +22
    -0
      introduction.tex
  13. +309
    -0
      kekse.py
  14. BIN
      kekse.xlsx
  15. BIN
      kekse_v1.xlsx
  16. BIN
      main.pdf
  17. +45
    -0
      main.tex
  18. +76
    -0
      title.tex

+ 28
- 0
README.md View File

@ -0,0 +1,28 @@
# Das Plätzchenkochbuch
Mit diesem Projekt kann man automatisch aus der Plätzchendatei ein PDF-Kochbuch generieren. Das Kochbuch hat ein Titelblatt, Inhaltsverzeichnis, Vorwort und ein Verzeichnis der Zutaten und Plätzchenarten.
Rezepte stehen in Spalten, in Zeilen stehen die Zutaten.
Das Excel-File wird mit openpyxl geparsed. Zuerst wird die Index-Spalte geparsed um die verschiedenen Reihen zu identifizieren. Anschließend werden die Rezepte spaltenweise durchlaufen und nacheinander in die 'rezepte.tex' Datei geschrieben.
Nachdem das Excel-File geparsed wurde wird 'main.tex' mit pdflatex compiled. Diese Datei included die rezepte.tex und es entsteht das fertige Kochbuch.
## Datenformat
Um das Excel-File parsen zu können muss es immer das gleiche Format haben.
Es gibt unter anderem die folgenden Einschränkungen und Regeln:
1. Die Rezpte befinden sich im Worksheet 'Rezepte'.
2. Zutaten
- Die Zutaten stehen in ersten Spalte, in der 'Zutaten' auftaucht
- Die Liste der Zutaten beginnt mit 'Ingredient' und schließt mit 'Sonstige Zutaten' ab.
3. Der Name steht in der Zeile mit 'Zutaten'
4. Die Art steht in der Zeile mit 'Art'
5. Die Hauptzutaten stehen in der Zeile mit 'Ingredient'. Mehrere Hauptzutaten werden mit Komma getrennt.
## Weitere Hinweise:
* Es ist sinnvoll, die Textbeschreibung ausführlich zu machen. Zeilenumbrüche könntne mit <Alt>+<Enter> gemacht werden.
* Es sind keine Rezepte ohne Zutaten möglich

BIN
figures/Coatimundi_lineart.png View File

Before After
Width: 728  |  Height: 900  |  Size: 78 KiB

BIN
figures/cc-by.png View File

Before After
Width: 500  |  Height: 500  |  Size: 15 KiB

BIN
figures/cc-sa.png View File

Before After
Width: 500  |  Height: 500  |  Size: 18 KiB

BIN
figures/cc.png View File

Before After
Width: 500  |  Height: 500  |  Size: 18 KiB

BIN
figures/coati.jpg View File

Before After
Width: 800  |  Height: 989  |  Size: 106 KiB

BIN
figures/coati.pdf View File


+ 66
- 0
figures/coati.svg
File diff suppressed because it is too large
View File


BIN
figures/coati2.jpg View File

Before After
Width: 447  |  Height: 455  |  Size: 74 KiB

+ 66
- 0
figures/coati2.svg
File diff suppressed because it is too large
View File


BIN
figures/kangaroo.pdf View File


+ 22
- 0
introduction.tex View File

@ -0,0 +1,22 @@
\section*{Vorwort}
\addcontentsline{toc}{section}{Vorwort}
\vspace{1cm}
\begin{center}
\textit{„That’s what I do: I bake and I know things.”}
\end{center}
Das T-Shirt mit dem passenden Snoopie haben mir liebe Freunde zum 31. Geburtstag geschenkt. Da stand das achte Jahr unserer Zootierbäckerei zusammen mit dem Koala und dem Schaf bevor, das zweite Mal mit dem Otter und dem Hasen, das dritte Mal zu sechst.
Backen ist nicht nur eine super Möglichkeit neue Dinge auszuprobieren und dabei zu entspannen, es kommt auch noch was leckeres dabei heraus. Und es ist einfach eine Freude, den Zutaten bei ihrer Metamorphose zuzusehen.
Daher kann ich das auch nicht als Aufwand begreifen. In diesem Jahr, dem durchaus faszinierenden 2020, habe ich mich an zuckerfreien Keksen versucht, teilweise waren sie gar gänzlich frei von Kohlehydraten und Süßstoffen.\\
In Summe waren es damit deutlich über 50 Sorten, die den Ofen verlassen haben. Allerdings kann und will ich das weder als Arbeit noch als Aufwand begreifen. Es macht schlicht riesigen Spaß, nicht nur das Backen selbst, sondern insbesondere die Vorbereitung und Planung. Dank meiner Rezeptdatei ist es ein leichtes eine entsprechende Einkaufsliste mit einem Klick zu erstellen. Und das beste: seit diesem Jahr lässt sie sich automatisch in ein lesbares Buch umwandeln.
Dafür muss ich mich bei meinem über alle Maßen geliebten Känguru bedanken, und ihr auch, sonst könntet ihr das nun nicht lesen.
Die häufigste Frage die ich bekomme ist tatsächlich: Warum? --- Die Antwort lautet: Warum nicht?\\
In diesem Sinne: Viel Spaß!\\
\textit{Euer Nasenbär}

+ 309
- 0
kekse.py View File

@ -0,0 +1,309 @@
#!/usr/bin/env python3
from openpyxl import Workbook
from openpyxl import load_workbook
import sys
import os
def escape_latex (content):
content=content.replace("","/")
content=content.replace("", "\ lb")
content=content.replace("½", "$\\frac{1}{2}$")
content=content.replace("¼", "$\\frac{1}{4}$")
content=content.replace("¾", "$\\frac{3}{4}$")
content=content.replace("\n", "\\\\\n")
content=content.replace("°", "$^\circ$")
content = content.replace("&", "\&")
content = content.replace("", "ö")
return content
# Define output file
f_latex = open("rezepte.tex","w+")
# Load cookbook
cb = load_workbook('/home/sebastian/Projekte/Kochbuch/kekse.xlsx', data_only=1)
sheets = cb.sheetnames
ws = None
# Find worksheet with recipes
ws = cb.get_sheet_by_name("Rezepte")
# Variables to store information while parsing
# Legend gives the column with descriptions and starting row for the recipes
legend_row = 0
legend_col = 0
# Ingredients
# Find the corresponding rows to all properties of recipes
ingredients_row_start = 0
in_ingredients = False
in_recipe = False
ingredients = {}
ingredients_unit = {}
ingredients_comment = {}
ingredients_other_row = 0
# Other fields
type_row = 0
instructions_row = 0
baking_time_row = 0
baking_temperature_row = 0
number_row = 0
resting_time_row = 0
specials_row = 0
# Years
first_year_row = 0
last_year_row = 0
first_year = 0
#####
# Go through worksheet and parse all information
for column in ws.columns:
for cell in column:
# Find row of type of recipe
if(cell.value=="Art"):
type_row = cell.row
legend_row = cell.row
legend_col = cell.column
if(cell.column == legend_col):
# Find row of recipe name, this is in the row with 'Zutaten'
if(cell.value == "Zutaten"):
name_row = cell.row
ingredients_row_start = cell.row +1
in_ingredients = True
# Find position of ingredients
if(cell.value == "Hauptaromen"):
aroma_row = cell.row
# Find position of source
if(cell.value == "Quelle"):
source_row = cell.row
# Instructions
if(cell.value== "Anleitung"):
instructions_row = cell.row
# Check if we are at end of ingredients
if(cell.value== "Sonstiges (Deko)"):
ingredients_other_row = cell.row
in_ingredients = False
# Make list of all ingredients and their units
if(cell.row >= ingredients_row_start and in_ingredients):
string = cell.value
if("(" in string and ")" in string):
ingredients[cell.row] = (cell.value.split('(')[0].rstrip())
ingredients_unit[cell.row] = (cell.value.split('(')[1].split(')')[0].rstrip())
ingredients_comment[cell.row] = (cell.value.split(')')[1].rstrip())
else:
ingredients[cell.row] = (string)
ingredients_unit[cell.row] = ("")
ingredients_comment[cell.row] = ("")
# Further instructions
if(cell.value== "Backzeit (Minuten)"):
baking_time_row = cell.row
if(cell.value== "Backtemperatur Umluft (Grad)"):
baking_temperature_row = cell.row
if(cell.value== "Ruhezeit (Minuten)"):
resting_time_row = cell.row
if(cell.value== "Anzahl (ca.)"):
number_row = cell.row
if(cell.value== "Besonderheiten"):
specials_row = cell.row
# Years
if(cell.value == 2013):
first_year_row = cell.row
last_year_row = cell.row
first_year = 2013
if(first_year_row > 0 and cell.value == "Kommentare zur Bearbeitung."):
# cell.row -2 instead of -1 to ignore current year 2021
last_year_row = cell.row-2
# By now, we should have found everything in the legend
#####
# Parse recipe
# Go through worksheet vertically, starting with the first column
# If right of legend, then it must be a recipe
if(cell.column > legend_col):
# Obtain information of recipe in the right order, so that we can directly write it to the tex-file.
# Name of recipe
content = (ws._get_cell(name_row, cell.column).value)
if(content != "None\n" and content != None):
f_latex.write(str("\\begin{minipage}{\\textwidth}\n"))
#if (content[-5:]=="(Oma)"):
# content="Omas " + content[:-6]
f_latex.write(str("\section{%s}\\vspace{0.5cm}\n" % content))
f_latex.write(str("\\begin{tabular}{lll}\n"))
# Aroma
content = (ws._get_cell(aroma_row, cell.column).value)
if(content != "None\n" and content != None):
f_latex.write(str("\\faHeart & \\textbf{Hauptzutat} & %s" % content))
f_latex.write(str("\\index{\\textbf{%s}}" % content.replace(", ", "}} \\index{\\textbf{")))
f_latex.write("\\\\\n")
# Type
content = (ws._get_cell(type_row, cell.column).value)
if(content != "None\n" and content != None):
f_latex.write(str("\\faEye & \\textbf{Form} & %s" % content))
f_latex.write(str("\\index[formen]{\\textbf{%s}}" % content.replace(", ", "}} \\index[formen]{\\textbf{")))
f_latex.write("\\\\\n")
# Source
content = (ws._get_cell(source_row, cell.column).value)
if(content != "None\n" and content != None):
f_latex.write(str("\\faBook & \\textbf{Quelle} & %s" % content))
f_latex.write("\\\\\n")
# Specials
content = (ws._get_cell(specials_row, cell.column).value)
if content == "(vegan)":
content="Vegan"
if(content != "None\n" and content != None):
f_latex.write(str("\\faStar & \\textbf{Besonderheiten} & %s" % content))
f_latex.write("\\\\\n")
# Baking
content1 = str(ws._get_cell(baking_time_row, cell.column).value)
content2 = str(ws._get_cell(baking_temperature_row, cell.column).value)
if((content1 != "None" and content1 != None and content1 != "0") or (content2 != "None" and content2 != None and content2 != "0")):
f_latex.write(str("\\Oven & \\textbf{Backzeit} &"))
if(content1 != "None" and content1 != None and content1 != "0"):
if(content1[-4:]=="Min."):
content1 = content1[:-4]
f_latex.write(str("%s Minuten bei " % content1))
else:
f_latex.write(str(" bei"))
if(content2 != "None" and content2 != None and content2 != "0"):
f_latex.write(str(" %s~$^\circ$C" % content2))
f_latex.write("\\\\\n")
# Resting time
content = str(ws._get_cell(resting_time_row, cell.column).value)
if(content != "None" and content != None and content != "0"):
f_latex.write(str("\\faClockO & \\textbf{Ruhezeit} &"))
if(content[-1:]=="h"):
f_latex.write(str(" %s Stunden" % content[:-1]))
else:
f_latex.write(str("%s Minuten" % content))
f_latex.write("\\\\\n")
# Amount
content = (ws._get_cell(number_row, cell.column).value)
if(content != "None\n" and content != None):
f_latex.write(str("\\faHashtag & \\textbf{Anzahl} & %s Stück\\\\\n" % content))
# Years
year = first_year
count = 0
output = "\\faCalendar & \\textbf{Gebacken} &"
for i in range (first_year_row, last_year_row+1):
content = (ws._get_cell(i, cell.column).value)
if(content != "None\n" and content != None):
# Cookie was baked in year
output = output + " " + str(year) + ","
count +=1
year += 1
if(count == 0):
output = output + " Noch nie gebacken"
else:
output = output[:-1]
f_latex.write(str(output+"\\\\\n"))
if(count > 0):
f_latex.write(str("\\faLineChart & \\textbf{Häufigkeit} & " + str(round(100/(year-first_year)*count))+"\\%\\\\\n"))
f_latex.write(str("\\end{tabular}\n"))
# List of Ingredients
f_latex.write(str("\\subsection*{Zutaten}\n"))
f_latex.write(str("\\begin{compactitem}\n"))
for i in range (ingredients_row_start, ingredients_other_row):
content = str(ws._get_cell(i, cell.column).value)
if(content != "None\n" and content != None and content != "None"):
#content = content + ingredients_unit[i-ingredients_row_start] + ingredients[i-ingredients_row_start]
# Add unit and whitespaces if needed
if (ingredients_unit[i] !="g"):
content = content + " "
if (ingredients_unit[i] != ""):
content = content + ingredients_unit[i] + " "
# Add Ingredient name
if (ingredients[i] == "Ei"):
if(content == "1 "):
content = content + "Ei"
else:
content = content + "Eier"
else:
content = content + ingredients[i]
# Add comment if available
if(ingredients_comment[i] != ""):
content = content + "; " + ingredients_unit[i]
content=escape_latex(content)
f_latex.write(str("\\item %s \n") % content)
f_latex.write(str("\\end{compactitem}\n\\vspace{0.2cm}\n"))
# Other ingredients
content = (ws._get_cell(ingredients_other_row, cell.column).value)
if(content != "None\n" and content != None and content != "None"):
content = escape_latex(content)
f_latex.write("\\textit{Außerdem:} %s \n" % content)
# Instructions
content = (ws._get_cell(instructions_row, cell.column).value)
if(content != "None\n" and content != None):
content=escape_latex(content)
f_latex.write(str("\\subsection*{Anleitung} %s\n" % content))
f_latex.write(str("\\end{minipage}\n\n"))
f_latex.write(str("\\vspace{2cm}\n"))
# That's all, so close files
f_latex.close()
os.system("latexmk -pdf main.tex")

BIN
kekse.xlsx View File


BIN
kekse_v1.xlsx View File


BIN
main.pdf View File


+ 45
- 0
main.tex View File

@ -0,0 +1,45 @@
\documentclass[a4paper, 12pt, twoside, toc=flat, toc=sectionentrywithdots, BCOR=1cm]{scrartcl}
\usepackage[utf8]{inputenc}
\usepackage[german]{babel} % Silbentrennung
\usepackage{mdframed}
\usepackage{anyfontsize}
\usepackage{adjustbox}
\usepackage{graphicx}
\usepackage{siunitx}
\usepackage{tikzsymbols}
\usepackage{paralist}
\usepackage{imakeidx}
\usepackage{fontawesome}
\usepackage{cookingsymbols}
% Schusterjungen und Hurenkinder unterdrücken
\widowpenalty = 10000
\displaywidowpenalty = 10000
\clubpenalty = 10000
\addtokomafont{section}{\normalfont \Huge \bfseries}{\sffamily}
\addtokomafont{subsection}{\normalfont \Large \bfseries}{\sffamily}
\let\labelitemi\labelitemii
% Indices
\makeindex[title=Hauptzutaten,columns=1, intoc]
\makeindex[name=formen, title=Formen und Plätzchenarten, columns=1, intoc]
\begin{document}
\include{title}
\setkomafont{sectionentry}{\rmfamily}
\tableofcontents
\include{introduction}
\include{rezepte}
\printindex
\printindex[formen]
\end{document}

+ 76
- 0
title.tex View File

@ -0,0 +1,76 @@
\titlepage{
\mdfdefinestyle{l3style}{%
leftmargin=0pt,
backgroundcolor=gray,
fontcolor=white,
linewidth=0pt,
innertopmargin=20pt,
innerbottommargin=20pt,
innerleftmargin=20pt,
font={\fontsize{80}{70}\selectfont}
}
\pagestyle{empty}
\begin{adjustbox}{minipage=.6\textwidth,center}
\Large{\em ``Cookies! Me eat!''\par
\hfill --- Cookie Monster}
\end{adjustbox}
\vspace{3em}
\begin{flushright}
\includegraphics[width=10cm]{figures/coati.pdf}
\end{flushright}
\vspace{-1em}
\begin{mdframed}[style=l3style]
Plätzchen
\end{mdframed}
{\noindent\Large\emph{Ein Kompendium}}\vspace{-0.3em}
\vspace{2em}
\begin{adjustbox}{minipage=.3\textwidth,right}
\Large\em
Sarah Wedrich
\end{adjustbox}
\vspace{3.2cm}
%\includegraphics[width=4cm]{figures/kangaroo.pdf}
{\Large\sffamily KANGAROO PRESS}
\cleardoublepage
\thispagestyle{empty}
\setlength{\parindent}{0pt}
~\\
\vspace{5cm}
\bigbreak
\huge{Plätzchen}\par
\smallbreak
\textit{\LARGE{Ein Kompendium}}\par
\vspace{2cm}
\begin{center}{\large{\textit{
Sarah Wedrich\\}}}
\vspace{9cm}
\Large{\today}
\end{center}
\clearpage\thispagestyle{empty}
\vspace*{\fill}\noindent
\normalsize{\textbf{Plätzchen --- Ein Kompendium}\vspace{0.3cm}\\
Sarah Wedrich\vspace{0.2cm}\\
\textit{sarah@wedrich.org}\\
\textit{https://www.wedrich.org} %\\
}}

Loading…
Cancel
Save