Skip to content

Instantly share code, notes, and snippets.

@hetsch
Last active May 29, 2020 06:37
Show Gist options
  • Save hetsch/e44f79d57b8f3ea9d54de65a94ce0754 to your computer and use it in GitHub Desktop.
Save hetsch/e44f79d57b8f3ea9d54de65a94ce0754 to your computer and use it in GitHub Desktop.
Pizza dough calculator
import collections
from enum import IntEnum
from copy import copy
import click
class EnumChoice(click.Choice):
def __init__(self, enum, case_sensitive=False, use_value=False):
self.enum = enum
self.use_value = use_value
choices = [str(e.value) if use_value else e.name for e in self.enum]
super().__init__(choices, case_sensitive)
def convert(self, value, param, ctx):
if value in self.enum:
return value
result = super().convert(value, param, ctx)
# Find the original case in the enum
if not self.case_sensitive and result not in self.choices:
result = next(c for c in self.choices if result.lower() == c.lower())
if self.use_value:
return next(e for e in self.enum if str(e.value) == result)
return self.enum[result]
class PIZZA_TYPE(IntEnum):
Napolitan = 0
American = 1
Sicilian = 2
Sourdough = 3
class PIZZA_SIZE(IntEnum):
Small = 0
Medium = 1
Large = 2
# https://www.stadlermade.com/de/pizza-dough-calculator/
# https://www.stadlermade.com/blog/how-to-make-neapolitan-style-pizza-dough/
RATIOS = {
PIZZA_TYPE.Napolitan: {"flour": 1.0, "salt": 0.03, "yeast": 0.002, "water": 0.65},
PIZZA_TYPE.American: {
"flour": 1.0,
"salt": 0.02, # not 1.5 like in the description
"yeast": 0.004,
"water": 0.65,
"oliveoil": 0.025,
"sugar": 0.02,
},
PIZZA_TYPE.Sicilian: {
"flour": 1.0,
"salt": 0.02,
"yeast": 0.015,
"water": 0.75,
"oliveoil": 0.015,
},
PIZZA_TYPE.Sourdough: {
"flour": 1.0,
"salt": 0.03,
"mother_dough": 0.16,
"water": 0.64,
},
}
SIZES = {
PIZZA_TYPE.Napolitan: {
PIZZA_SIZE.Small: 160,
PIZZA_SIZE.Medium: 230,
PIZZA_SIZE.Large: 300,
},
PIZZA_TYPE.American: {
PIZZA_SIZE.Small: 175,
PIZZA_SIZE.Medium: 240,
PIZZA_SIZE.Large: 315,
},
PIZZA_TYPE.Sicilian: 650,
}
WATER_MIN_MAX = {
PIZZA_TYPE.Napolitan: (0.55, 0.75),
PIZZA_TYPE.American: (0.55, 0.65),
PIZZA_TYPE.Sicilian: (0.55, 0.85),
}
fields = ("flour", "water", "salt", "yeast", "mother_dough", "oliveoil", "sugar")
Result = collections.namedtuple("Result", fields, defaults=(None,) * len(fields))
# https://www.stadlermade.com/blog/how-to-make-neapolitan-style-pizza-dough/
def calculate(pieces, grams_per_piece, ratios):
if style == PIZZA_TYPE.Napolitan:
return Result(
flour=round(flour_weight),
water=round(flour_weight * ratios["water"]),
salt=round(flour_weight * ratios["salt"], 1),
yeast=round(flour_weight * ratios["yeast"], 1),
)
elif style == PIZZA_TYPE.American:
return Result(
flour=round(flour_weight),
water=round(flour_weight * ratios["water"]),
salt=round(flour_weight * ratios["salt"], 1),
yeast=round(flour_weight * ratios["yeast"], 1),
oliveoil=round(flour_weight * ratios["oliveoil"]),
sugar=round(flour_weight * ratios["sugar"], 1),
)
elif style == PIZZA_TYPE.Sicilian:
return Result(
flour=round(flour_weight),
water=round(flour_weight * ratios["water"]),
salt=round(flour_weight * ratios["salt"], 1),
yeast=round(flour_weight * ratios["yeast"], 1),
oliveoil=round(flour_weight * ratios["oliveoil"]),
)
elif style == PIZZA_TYPE.Sourdough:
return Result(
flour=round(flour_weight),
water=round(flour_weight * ratios["water"]),
salt=round(flour_weight * ratios["salt"], 1),
mother_dough=round(flour_weight * ratios["mother_dough"]),
)
def get_floor_weight(desired_total_weight, ratios):
# Backer's Percentage Formula: Calculate the amount of floor as basis
# https://www.wildyeastblog.com/bakers-percentage-3/
# Total Flour Weight = (Desired Dough Weight / Total %) x 100
return (desired_total_weight / (sum(ratios.values()) * 100)) * 100
@click.group()
def cli():
pass
def validate_water(value, pizza_type):
_min, _max = WATER_MIN_MAX[pizza_type]
if _min <= value <= _max:
raise click.BadParameter(f"Water ratio must be between {_min} and {_max}")
return value
@cli.command()
@click.option("-p", "--pieces", default=1, help="The number of pizzas")
@click.option(
"-s",
"--size",
type=EnumChoice(PIZZA_SIZE),
default=PIZZA_SIZE.Medium,
help="S Pizza = Ø 16 CM/6 INCH == 160 gram, M Pizza = Ø 28CM/ 11 INCH == 230 gramm, L Pizza = Ø 34CM/ 13 INCH == 300 gram",
)
@click.option(
"-g",
"--grams",
default=0,
help="The weight of each pizza piece. Overwrites size argument if given",
)
@click.option(
"-w",
"--water_ratio",
default=0.0,
help="The pizza dough calculator assumes that you’re using tipo 00 flour. If this is the case, you won’t need to make any changes to the water percentage indicated. In case you’re using regular bread flour, it would be wise to set the parameter between 50% and 55%, since regular bread flower can’t absorb large quantities of water. If you don’t use less water with bread flour, Your dough will get wet and soupy",
)
def neapolitan(pieces, size, grams, water_ratio):
ratios = copy(RATIOS[PIZZA_TYPE.Napolitan])
if water_ratio:
ratios["water"] = water_ratio
if not grams and size:
click.echo(click.style(f"Pizza size: {PIZZA_SIZE(size).name}", fg="blue"))
grams = SIZES[PIZZA_TYPE.Napolitan][size]
else:
click.echo(click.style(f"Using {grams} g per piece", fg="blue"))
flour_weight = get_floor_weight(pieces * grams, ratios)
print(
Result(
flour=round(flour_weight),
water=round(flour_weight * ratios["water"]),
salt=round(flour_weight * ratios["salt"], 1),
yeast=round(flour_weight * ratios["yeast"], 1),
)
)
@cli.command()
@click.option("-p", "--pieces", default=1, help="The number of pizzas")
@click.option(
"-s",
"--size",
type=EnumChoice(PIZZA_SIZE),
default=PIZZA_SIZE.Medium,
help="S Pizza = Ø 16 CM/6 INCH == 175 gram, M Pizza = Ø 28CM/ 11 INCH == 240 gramm, L Pizza = Ø 34CM/ 13 INCH == 315 gram",
)
@click.option(
"-g",
"--grams",
default=0,
help="The weight of each pizza piece. Overwrites size argument if given",
)
@click.option(
"-w",
"--water_ratio",
callback=lambda _, __, x: validate_water(x, PIZZA_TYPE.American),
default=RATIOS[PIZZA_TYPE.Napolitan]["water"],
help="The pizza dough calculator assumes that you’re using tipo 00 flour. If this is the case, you won’t need to make any changes to the water percentage indicated. In case you’re using regular bread flour, it would be wise to set the parameter between 50% and 55%, since regular bread flower can’t absorb large quantities of water. If you don’t use less water with bread flour, Your dough will get wet and soupy",
)
def american(pieces, size, grams, water_ratio):
click.echo(click.style(f"Pizza style: {PIZZA_TYPE.American.name}", fg="green"))
ratios = copy(RATIOS[PIZZA_TYPE.American])
if water_ratio:
ratios["water"] = water_ratio
if not grams and size:
click.echo(click.style(f"Pizza size: {PIZZA_SIZE(size).name}", fg="blue"))
grams = SIZES[PIZZA_TYPE.American][size]
else:
click.echo(click.style(f"Using {grams} g per piece", fg="blue"))
flour_weight = get_floor_weight(pieces * grams, ratios)
print(
Result(
flour=round(flour_weight),
water=round(flour_weight * ratios["water"]),
salt=round(flour_weight * ratios["salt"], 1),
yeast=round(flour_weight * ratios["yeast"], 1),
oliveoil=round(flour_weight * ratios["oliveoil"]),
sugar=round(flour_weight * ratios["sugar"], 1),
)
)
if __name__ == "__main__":
cli()
# print(calculate(1, 230, style=PIZZA_TYPE.Sourdoug
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment