Skip to content

Commit

Permalink
Add friendly error methods on style object
Browse files Browse the repository at this point in the history
  • Loading branch information
chrisgilmerproj committed Oct 14, 2016
1 parent 790cf54 commit 6265cdf
Show file tree
Hide file tree
Showing 3 changed files with 175 additions and 2 deletions.
97 changes: 97 additions & 0 deletions brew/styles.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,21 @@ def og_matches(self, og):
"""
return (self.og[0] <= og <= self.og[1])

def og_errors(self, og):
"""
Return list of errors if og doesn't match the style
:param float og: Original Gravity
:return: List
:rtyle: list
"""
errors = []
if og < self.og[0]:
errors.append('OG is below style')
if og > self.og[1]:
errors.append('OG is above style')
return errors

def fg_matches(self, fg):
"""
Determine if fg matches the style
Expand All @@ -111,6 +126,21 @@ def fg_matches(self, fg):
"""
return (self.fg[0] <= fg <= self.fg[1])

def fg_errors(self, fg):
"""
Return list of errors if fg doesn't match the style
:param float fg: Final Gravity
:return: List
:rtyle: list
"""
errors = []
if fg < self.fg[0]:
errors.append('FG is below style')
if fg > self.fg[1]:
errors.append('FG is above style')
return errors

def abv_matches(self, abv):
"""
Determine if abv matches the style
Expand All @@ -121,6 +151,21 @@ def abv_matches(self, abv):
"""
return (self.abv[0] <= abv <= self.abv[1])

def abv_errors(self, abv):
"""
Return list of errors if abv doesn't match the style
:param float abv: Alcohol by Volume
:return: List
:rtyle: list
"""
errors = []
if abv < self.abv[0]:
errors.append('ABV is below style')
if abv > self.abv[1]:
errors.append('ABV is above style')
return errors

def ibu_matches(self, ibu):
"""
Determine if ibu matches the style
Expand All @@ -131,6 +176,21 @@ def ibu_matches(self, ibu):
"""
return (self.ibu[0] <= ibu <= self.ibu[1])

def ibu_errors(self, ibu):
"""
Return list of errors if ibu doesn't match the style
:param float ibu: IBU
:return: List
:rtyle: list
"""
errors = []
if ibu < self.ibu[0]:
errors.append('IBU is below style')
if ibu > self.ibu[1]:
errors.append('IBU is above style')
return errors

def color_matches(self, color):
"""
Determine if color matches the style
Expand All @@ -141,6 +201,21 @@ def color_matches(self, color):
"""
return (self.color[0] <= color <= self.color[1])

def color_errors(self, color):
"""
Return list of errors if color doesn't match the style
:param float color: Color in SRM
:return: List
:rtyle: list
"""
errors = []
if color < self.color[0]:
errors.append('Color is below style')
if color > self.color[1]:
errors.append('Color is above style')
return errors

def recipe_matches(self, recipe):
"""
Determine if a recipe matches the style
Expand All @@ -162,6 +237,28 @@ def recipe_matches(self, recipe):
return True
return False

def recipe_errors(self, recipe):
"""
Return list errors if the recipe doesn't match the style
:param Recipe recipe: A Recipe object
:return: Errors
:rtype: list
"""
recipe_og = recipe.get_original_gravity()
recipe_fg = recipe.get_final_gravity()
recipe_abv = alcohol_by_volume_standard(recipe_og, recipe_fg)
recipe_ibu = recipe.get_total_ibu()
recipe_color = recipe.get_total_wort_color()

errors = []
errors.extend(self.og_errors(recipe_og))
errors.extend(self.fg_errors(recipe_fg))
errors.extend(self.abv_errors(recipe_abv))
errors.extend(self.ibu_errors(recipe_ibu))
errors.extend(self.color_errors(recipe_color))
return errors

def to_dict(self):
style_dict = {
'style': self.style,
Expand Down
13 changes: 11 additions & 2 deletions docs/source/tutorial/matching_styles.rst
Original file line number Diff line number Diff line change
Expand Up @@ -66,13 +66,22 @@ In order to match the recipe we use a method on the class:
True
>>> recipe_color = recipe.get_total_wort_color()
>>> style.color_matches(recipe_color)
False
True
Interestingly the recipe used in the examples does not match the BJCP style!
The only feature that matches the style is the IBUs, but the remaining values
for og, fg, abv, and color are all too high. That means its time to correct
our recipe.

As a short hand you can also get this information in a more friendly way:

.. code-block:: python
>>> style.recipe_matches(recipe)
['OG is above style', 'FG is above style', 'ABV is above style']
This will help you quickly discover the problems with your recipe.

Correcting a Recipe
-------------------

Expand Down Expand Up @@ -136,7 +145,7 @@ crystal 20L has come down from 0.78 lbs to 0.51 lbs. Let's try this again.
False
It turns out the recipe still doesn't match. Why? It appears that our color
is off.
is now off after our adjustments.

Correcting for Color
--------------------
Expand Down
67 changes: 67 additions & 0 deletions tests/test_styles.py
Original file line number Diff line number Diff line change
Expand Up @@ -150,22 +150,62 @@ def test_og_matches(self):
out = self.style.og_matches(1.050)
self.assertTrue(out)

def test_og_matches_low(self):
out = self.style.og_errors(1.044)
self.assertEquals(out, ['OG is below style'])

def test_og_matches_high(self):
out = self.style.og_errors(1.061)
self.assertEquals(out, ['OG is above style'])

def test_fg_matches(self):
out = self.style.fg_matches(1.012)
self.assertTrue(out)

def test_fg_matches_low(self):
out = self.style.fg_errors(1.009)
self.assertEquals(out, ['FG is below style'])

def test_fg_matches_high(self):
out = self.style.fg_errors(1.016)
self.assertEquals(out, ['FG is above style'])

def test_abv_matches(self):
out = self.style.abv_matches(0.050)
self.assertTrue(out)

def test_abv_matches_low(self):
out = self.style.abv_errors(0.044)
self.assertEquals(out, ['ABV is below style'])

def test_abv_matches_high(self):
out = self.style.abv_errors(0.063)
self.assertEquals(out, ['ABV is above style'])

def test_ibu_matches(self):
out = self.style.ibu_matches(33.0)
self.assertTrue(out)

def test_ibu_matches_low(self):
out = self.style.ibu_errors(29)
self.assertEquals(out, ['IBU is below style'])

def test_ibu_matches_high(self):
out = self.style.ibu_errors(51)
self.assertEquals(out, ['IBU is above style'])

def test_color_matches(self):
out = self.style.color_matches(7.5)
self.assertTrue(out)

def test_color_matches_low(self):
out = self.style.color_errors(4)
self.assertEquals(out, ['Color is below style'])

def test_color_matches_high(self):
out = self.style.color_errors(11)
self.assertEquals(out, ['Color is above style'])

def test_recipe_matches(self):
pale_add = GrainAddition(pale,
weight=8.69)
Expand All @@ -189,6 +229,33 @@ def test_recipe_matches_false(self):
out = self.style.recipe_matches(recipe)
self.assertFalse(out)

def test_recipe_errors(self):
out = self.style.recipe_errors(recipe)
expected = ['OG is above style',
'FG is above style',
'ABV is above style']
self.assertEquals(out, expected)

def test_recipe_errors_none(self):
pale_add = GrainAddition(pale,
weight=8.69)
crystal_add = GrainAddition(crystal,
weight=1.02)
pale_ale = Recipe(name='pale ale',
grain_additions=[
pale_add,
crystal_add,
],
hop_additions=hop_additions,
yeast=yeast,
percent_brew_house_yield=0.70,
start_volume=7.0,
final_volume=5.0,
)
out = self.style.recipe_errors(pale_ale)
expected = []
self.assertEquals(out, expected)

def test_to_json(self):
out = self.style.to_json()
expected = '{"abv": [0.045, 0.062], "category": "18", "color": [5, 10], "fg": [1.01, 1.015], "ibu": [30, 50], "og": [1.045, 1.06], "style": "American Pale Ale", "subcategory": "B"}' # nopep8
Expand Down

0 comments on commit 6265cdf

Please sign in to comment.