It’s Thanksgiving weekend in the U.S., and that can only mean one thing: it’s time to start homebrewing again. The temperature’s right for fermenting and there’s no better weather for hanging out in the backyard, drinking a beer, and watching 6 gallons of amber liquid boil for an hour or so.

Of course, that leaves plenty of time to whip up some Python scripts for homebrewers (well, homebrewers that dig on Python, anyway). Armed with a netbook and 3 Victory Storm King stouts:

ccbrew.py

ccbrew.py
Python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
#!/usr/bin/env python
class recipe():
"""Define ingredients and instructions for a homebrew beer recipe and
calculate characteristics such as gravity, bitterness, color, etc.
Instantiate like this:
r = ccbrew.recipe("American Pale Ale", "American Pale Ale", 5.5, 6.5, 0.8)
Or like this:
r = ccbrew.recipe("American Pale Ale", "American Pale Ale")
r.batch_size = 5.5
r.boil_volume = 6.5
r.extract_efficiency = 0.8
Add ingredients like this:
r.add_fermentable(ccbrew.fermentable("American 2 Row", 36, 1.7, 11))
r.add_hop(ccbrew.hop("Perle", 1.0, 7.9, 60, "whole"))
etc.
"""
def __init__(self, name, style, batch_size = None, boil_gallons = None, \
extract_efficiency = None):
self.name = name
self.style = style
self.batch_size = batch_size
self.boil_gallons = boil_gallons
self.extract_efficiency = extract_efficiency
self._fermentables = []
self._hops = []
self._yeast = []
@property
def fermentables(self):
"""Returns a list of the recipe's fermentable objects."""
return self._fermentables
@property
def hops(self):
"""Returns a list of the recipe's hop objects."""
return self._hops
def add_fermentable(self, f):
"""Append a fermentable object to the recipe's list of fermentable
objects."""
self._fermentables.append(f)
def add_hop(self, h):
"""Append a hop object to the recipe's list of hop objects."""
self._hops.append(h)
def fermentables_lbs(self):
"""Returns the total weight of the recipe's fermentables in lbs."""
ret = 0.0
for f in self.fermentables:
ret += f.lbs
return ret
def fermentable_yield_contrib(self, f):
"""Returns the gravity points yielded by a given fermentable in a
format such as 52.8."""
return (f.potential * f.lbs / self.boil_gallons) * self.extract_efficiency
def fermentables_yield(self):
"""Returns the total gravity units yielded by the recipe's fermentables
in a format such as 1.059."""
ret = 0.0
for f in self.fermentables:
ret += self.fermentable_yield_contrib(f)
return (ret / 1000) + 1
def hop_ibu_contrib(self, h):
"""Returns IBU contribution of a given hop addition in a format such
as 23.66978."""
return (h.oz * self.get_hop_util(h) * (h.alpha/100) * 7489) \
/ (self.boil_gallons * self.get_gravity_correction())
def get_hop_util(self, h):
"""Returns the utilization percentage of a given hop addition (based
on Ray Daniels' 'Designing Great Beers', p80, Table 9.3)."""
ret = 0.0
if h.form == "whole" or h.form == "w":
if h.boil_mins >= 0 and h.boil_mins < = 9:
ret = 0.05
elif h.boil_mins >= 10 and h.boil_mins < = 19:
ret = 0.12
elif h.boil_mins >= 20 and h.boil_mins < = 29:
ret = 0.15
elif h.boil_mins >= 30 and h.boil_mins < = 44:
ret = 0.19
elif h.boil_mins >= 35 and h.boil_mins < = 59:
ret = 0.22
elif h.boil_mins >= 60 and h.boil_mins < = 74:
ret = 0.24
elif h.boil_mins >= 75:
ret = 0.27
elif h.form == "pellet" or h.form == "p":
if h.boil_mins >= 0 and h.boil_mins < = 9:
ret = 0.06
elif h.boil_mins >= 10 and h.boil_mins < = 19:
ret = 0.15
elif h.boil_mins >= 20 and h.boil_mins < = 29:
ret = 0.19
elif h.boil_mins >= 30 and h.boil_mins < = 44:
ret = 0.24
elif h.boil_mins >= 35 and h.boil_mins < = 59:
ret = 0.27
elif h.boil_mins >= 60 and h.boil_mins < = 74:
ret = 0.30
elif h.boil_mins >= 75:
ret = 0.34
return ret
def get_gravity_correction(self):
"""Returns correction factor for worts above 1.050 for purposes of
calculating IBU contribution."""
return 1 + ((self.fermentables_yield() - 1.050)) / 0.2
def ibu(self):
"""Returns the total IBU of the recipe."""
ret = 0.0
for h in self.hops:
ret += self.hop_ibu_contrib(h)
return ret
def color(self):
"""Returns the color of the beer in Lovibond points."""
pass
class fermentable():
"""Define a fermentable in the recipe.
Instantiate like this:
f = ccbrew.fermentable("American 2 Row", 36, 1.7, 11)
Or like this:
f = ccbrew.fermentable("American 2 Row")
f.potential = 36
f.color = 1.7
f.lbs = 11
"""
def __init__(self, name, potential = None, color = None, lbs = None):
self.name = name
self.potential = potential
self.color = color
self.lbs = lbs
class hop():
"""Define a hop (more exactly: a hop addition) in the recipe. 'form' can be
'pellet' or 'p' for pellet hops, or 'whole' or 'w' for whole hops.
Instantiate like this:
h = ccbrew.hop("East Kent Golding", 3.0, 5.0, 60, "pellet")
Or like this:
h = ccbrew.hop("East Kent Golding")
h.oz = 3.0
h.alpha = 5.0
h.boil mins = 60
h.form = "pellet"
"""
def __init__(self, name, oz = None, alpha = None, boil_mins = None, \
form = "whole"):
self.name = name
self.oz = oz
self.alpha = alpha
self.boil_mins = boil_mins
self.form = form
class yeast(): pass

And all good boys write unit tests, right? (OK, OK … this is more like a glorified main() intermingled with some unittest assertions.)

test_ccbrew.py

test_ccbrew.py
Python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#!/usr/bin/env python
import ccbrew
import unittest
class recipe_tests(unittest.TestCase):
def test_pale_ale(self):
"""Load up a classic American Pale Ale recipe"""
r = ccbrew.recipe("American Pale Ale", "American Pale Ale", 5.5, 6.5, 0.8)
r.add_fermentable(ccbrew.fermentable("American 2 Row", 36, 1.7, 11))
r.add_fermentable(ccbrew.fermentable("Cara-Pils", 34, 1.7, 0.75))
r.add_fermentable(ccbrew.fermentable("Crystal 60", 32, 60, 0.75))
r.add_hop(ccbrew.hop("Perle", 1.0, 7.9, 60, "whole"))
r.add_hop(ccbrew.hop("Cascade", 0.5, 5.6, 45, "whole"))
r.add_hop(ccbrew.hop("Cascade", 0.5, 5.6, 30, "whole"))
r.add_hop(ccbrew.hop("Cascade", 0.5, 5.6, 15, "whole"))
r.add_hop(ccbrew.hop("Cascade", 0.5, 5.6, 2, "whole"))
self.assertEqual(r.name, "American Pale Ale")
self.assertEqual(r.style, "American Pale Ale")
self.assertEqual(r.fermentables_lbs(), 12.5)
self.assertAlmostEqual(r.fermentables_yield(), 1.05483076)
self.assertAlmostEqual(r.ibu(), 39.5993390)
def test_old_ale(self):
"""Load up the house Calebs Creek Old Ale recipe"""
r = ccbrew.recipe("Calebs Creek Old Ale", "Old Ale", 5.5, 6.5, 0.8)
r.add_fermentable(ccbrew.fermentable("American 2 Row", 36, 1.7, 15))
r.add_fermentable(ccbrew.fermentable("Victory", 35, 5, 0.75))
r.add_fermentable(ccbrew.fermentable("Crystal 40", 33, 40, 1.0))
r.add_hop(ccbrew.hop("East Kent Golding", 3.0, 5.0, 60, "pellet"))
r.add_hop(ccbrew.hop("Fuggle", 1.0, 5.0, 15, "pellet"))
self.assertEqual(r.name, "Calebs Creek Old Ale")
self.assertEqual(r.style, "Old Ale")
self.assertEqual(r.fermentables_lbs(), 16.75)
self.assertAlmostEqual(r.fermentables_yield(), 1.0737538)
self.assertAlmostEqual(r.ibu(), 54.0666254)
suite = unittest.TestLoader().loadTestsFromTestCase(recipe_tests)
unittest.TextTestRunner(verbosity=2).run(suite)

TODO

Pretty much everything relevant to water chemistry, mash schedules, gravity, color, and much, much more. I only had an hour or so. Relax.

Share
 
Evolution

Some chicken, some beer, 13 years, and some kids who are now clearly smarter than you were at the same age: Don’t ask me why I have a bag of ballpark peanuts in my kitchen …

Share
 

These are taken directly from Major League Baseball’s official 2011 season statistics.  2011 Cleveland Indians   2011 Boston Red Sox   Payroll $48,339,167 $160,257,476  Wins 80 90  Cost per win $604,239.59 $1,780,638.62  Playoff revenue  $0 $0  Likeability 7/10 3/10  FAIL rating 7/10 11/10

Share
 

If I had a Twitter account, I would have fumbled this out with a smartphone after 4 rum and cokes. But I don’t have either, so I’ll fumble it out on an antique comfort-curve keyboard and a PC weighing roughly as much as a tractor engine. After 4 rum and cokes: The crash of an airplane costing 43 people their lives today is an event that transcends affinities to sports or leagues or teams or vocations at all. It was a human event. Tragic and sorrowful. The sort of happening that creates a feeling of anti-gravity in your stomach and a sensation that your heart is trying to make its way to the bottom of your feet. Nobody with more than two brain cells to rub together to [...]

Share
 
HOW-TO: Convert FLAC files to OGG/Vorbis files with a bash script

So you have a bunch of FLAC files for whatever reason, but if you’re like me, it’s because you are an audiophile and prefer a lossless codec for files you play on your high-end stereo equipment. However, FLAC files are huge, which can make them impractical for small personal music players with limited storage. This is especially true if you’re just going to plug the player into a cheap-ass stereo (like the one in my base model Toyota RAV4, for example.) That’s a bit like hunting squirrels with hand-grenades: overkill. Your best bet is to convert your FLAC files to something smaller and more manageable (and sounds better than MP3!): OGG/Vorbis. Let’s get started. We’ll assume your FLAC files are tagged with the artist name, song title, etc. [...]

Share
Your Ad Here