Champlain Valley Water Quality, 2009

I missed putting up the water quality numbers from this spring, but in the mean time it turns out the Champlain Water District has the Water Quality Reports up on the web. Unfortunately, they’re huge PDFs since they seem to be simply bitmap scans of the paper report. :(

In any case, here’s the relevant homebrewer water quality details:

Aluminum0.056 mg/L
Alkalinity57-74 ppm as CaCO3
Calcium Hardness45-56 ppm as CaCO3
Total Hardness61 ppm as CaCO3 (3.6 grains/gal)
Chloride17 ppm
Foaming Agentsless than 0.1ppm
Total Organic Carbon (TOC)2.10 ppm (1.49-2.74)
Conductivity182 ┬ÁS/cm (158-202)
pH7.66 (7.4-7.94)
Total Dissolved Solids113 ppm
Ironless than 0.01ppm
Manganese.006 mg/L (0 – 0.023)
Potassium1.31 ppm
Sulfate15 ppm
Silverless than 0.005 ppm
Silica1.4 ppm
Silicon0.67 ppm
Bromideless tahn 0.010 ppm
Iodideless than 1 ppm
Flouride0.96 mg/L (0.71 – 1.19)
Ammonium Ion0.12 ppm (0.02-0.23)

django timezone view handling

One simple way to handle user-time-zone localization in an app is to always store the timestamp in UTC, and localize viewing/editing of that timestamp in a specific timezone.

By default, Django traffics in the single timezone.

Timezone handling appear incomplete. The two options you’ll run across are this snippet for a timezone-localizing filter and django-timezones.

django-timezones has support for a TimeZone model/view field, and a model/view LocalizedDateTimeField. The View field, however, will only do the parse-time conversion of the datetime value in to the settings-defined TZ. This only solves half of the problem, since you still need to convert the settings-defined TZ into the user’s TZ, and there’s no component for that.

The following is a LocalizedDateTimeInput widget which will handle this last step, building on django.forms.DateTimeInput and django-timezones’ utility code:

from django import forms
from timezones.utils import adjust_datetime_to_timezone
class LocalizedDateTimeInput (forms.DateTimeInput):
    def __init__(self, tz):
        self._tz = tz
        super(LocalizedDateTimeInput, self).__init__()
    def render(self, name, value, attrs=None):
        if isinstance(value, datetime):
            value = adjust_datetime_to_timezone(value, 'UTC', self._tz)
        # @fixme: output the string rep of the timezone, probably after the <input />
        return super(LocalizedDateTimeInput, self).render(name, value, attrs)

Unfortunately, you’ll need to add a level of indirection to your view Form constructors to bind the ‘tz’ argument on the widget. Instead of a normal:

class MumbleForm (forms.Form):
    name = forms.CharField()
    date = forms.DateTimeField()

You’ll want to do something like:

def MumbleForm(user, *args, **kwargs):
    tz = settings.TIME_ZONE
    if user and hasattr(user, 'get_profile'):
        tz = user.get_profile().timezone
    class _MumbleForm (forms.Form):
        name = forms.CharField()
        date = LocalizedDateTimeField(tz, widget=LocalizedDateTimeInput(tz))
    return _MumbleForm(*args, **kwargs)

You can see more usage examples from the brew-journal commit.

Any tips or improvements welcome, of course.

Update, 2009-11-28: I finally got around to moving to using mysql instead of sqlite for this project, and discovered that the above solution needs one more piece. The default LocalizedDatetimeField seems to return datetimes which are “[tz] aware” in python’s vernacular, but mysql complains that datetimes with a tz portion are not allowed. So, we add a SafeLocalizedDateTimeField that returns “naive” datetimes instead:

class SafeLocalizedDateTimeField (LocalizedDateTimeField):
    def clean(self, value):
        val = super(SafeLocalizedDateTimeField, self).clean(value)
        if val is not None:
            val = val.replace(tzinfo=None)
        return val

Champlain Valley water anlysis, 2007

Since the previous water analysis from 2005, I’ve since bought a house, which means the Champlain Water District sends me a report about how their water is the best water. Of note to me as a homebrewer is the analysis of certain chemical properties of the water. Since a few google searches for this information basically turned up my previous post and not the actual source of the data, here’s the data:

  • aluminum: < 0.06ppm
  • color: 2 units
  • alkalinity: 42-56 ppm as CaCO3
  • calcium hardness: 45-56 ppm as CaCO3
  • total hardness: 61 ppm as CaCO3
  • chloride: 17ppm
  • foaming agents: < 0.1 ppm
  • total organic carbon: 2.22 pm (1.60-3.1)
  • conductivity: 189 micro-S/cm (163-208)
  • pH: 7.56 (7.29 – 7.89)
  • total disolved solids: 113 ppm
  • iron: < 0.01ppm
  • manganese: .007ppm
  • sodium: 7.5ppm
  • potassium: 1.31 ppm
  • sulfate: 15 ppm
  • silver: < 0.05ppm
  • silica: 1.4ppm
  • silicon: 0.67 ppm
  • bromide: < 0.010 ppm
  • iodide: < 1 ppm
  • flouride: 0.97 ppm (0.71 – 1.21)
  • ammonium ion: 0.20 ppm (0.04 – 0.048)

posting delay; homebrew recipes and the semantic web

Sorry for the long delay in posting. A few months ago, the girlfriend and I bought a sinkhole of time and materials generally referred to as a house. This one was particularly large in that it needed some serious internal rennovation before being habitable. Between that and a desire to focus this less on just talking and more on talking about production, I’ve not had much time or much to write about…

For the past couple of years, I’ve been pretty interested in the semantic web technologies, and in RDF. I came to the space looking for rigor about how to use XML to model data. What I found was really what I was looking for: both the recognition of XML’s true nature as well as a better way to actually do the data-modeling I was seeking… or so I thought.

Like many others, I feel into the trap of “RDF for everything!”, neglecting to remember that it is the (web) Resource Description Format. While many things are, or can be (and should be), Resources, the framwork is setup more for the wide-scale exchange of information about those resources, not as a general-purpose “small-scale” data-modeling framework. However, many things can be resources…

One project I started just about a year ago, when I started homebrewing, was keeping my homebrewing journal in a file. I knew that I wanted to keep notes in a good bit of structured detail, but I didn’t want the pain associated with authoring them in XML. I decided to author them in RDF, in N3, along a loose ontology that I would create and extend as I authored.

The main aspects to record in such a journal are the following:

  • the recipe itself
  • grains, adjuncts, hop-schedule, and notes
  • instances of brewing the recipe
  • deviations from the recipe
  • notes about the brewing process (e.g., tempatures reached, times of boiling, &c.)
  • readings of the specific gravity of the wort at the beginning and end of the fermentation.

As such, I ended up with notes that look like:

<#maplePorter> a hb:Recipe ; rdf:label “Maple Porter” ; dc:created “2005-04-02-05:00″^^xsd:date ; foaf:maker <#jsled> ; hb:ingredient [ a hb:Grain; hb:amt [ rdf:value 4.75; hb:units hb:lb ]; rdf:label “pale extract” ]

,[ a hb:Adjunct; hb:amt [ rdf:value 2.5; hb:units hb:lb ] ,[ rdf:value 43; hb:units hb:oz ]; rdf:label “Vermont maple syrup; unblended @ 20 min remaining” ] ,[ a hb:Hop; hb:amt [ rdf:value 2.0; hb:units hb:oz ]; rdf:label “Goldings”; hb:hopTime [ rdf:value 60; hb:units hb:min ] ]

. <#aprMaplePorter> a hb:BrewInstance ; hb:recipe <#maplePorter> ; dc:created “2005-04-18-05:00″^^xsd:date ; foaf:maker <#jsled> ; hb:journal [ a hb:OriginalGravityReading; dc:created "2005-04-18-05:00"^^xsd:date; rdf:value 1.042; hb:temp [ rdf:value 60; hb:units hb:F ]] ,[ dc:created "2005-04-18-05:00"^^xsd:date; rdf:label "Used Wyeast 1318 (London Ale Yeast III), cultured Feb 15 2005, incubated for 2 days" ]


For quite a while I’ve been hoping to publish the collection of data, autogenerated from the RDF into HTML. I finally set down and have done it. contains both.

The pipeline is the “standard” one: cwm plus a bit of inference into XML, then XSLT into HTML. contains a bit of the detail. It’d be nicer to use a RDF-specific path/templating/transformation language, but xsltproc is widely available and simple.

A major part of the process was cleaning up and making-consistent the journal data. In doing this a couple of issues with the ad-hoc formating I’d naturally done in the RDF were made much more clear:

  1. The relation between a recipe and an ingredient should really be made quite regular through the use of a URI for the ingredient, rather than simple a value string. While true in recipes generally, this is particularly true in beer recipes, which draw from a small, fixed set of ingredients and vary primarily in the n-ary relations of those ingredients to the recipe.

  2. What are currently n3-file-local fragments should really be first-order resources at<filename>. One thing that RDF makes particularly painful is the seamless transition from a very informal organization into a concrete one.

mead and ESB

On the new-year’s eve, Odd and I brewed a mead. We’ll likely try some over the summer, but for the real spoils we will wait until the new year’s eve next, when we will enjoy it fully while brewing another batch.

More recently, I’ve used the London Ale liquid yeast I received for Chistmas to brew an ESB. I took the recipe – as I did for Ale Hallow’s Eve – from looking at a bunch of recipes and averaging a few together. I tried some while racking it over into a carboy, and it’s quite good! Better than most of the brews I’ve done so far … I attribute that primarily to the liquid yeast. I’m next going to do a bock or dopplebock with the other liquid yeast I got for Christmas; if there’s as-noticeable a quality improvement, I might not go back.

Anyways, since I still haven’t created a better publishing framework for these little structured-data tidbits, yet … and against all sense since it’s not even appropriate mime-typed, let alone easily seperable from the page-content or GRDDL’ed or anything …. here’s the data for both the mead and the ESB:

@prefix : @prefix hb: :. @prefix rdf: @prefix rdfs: @prefix foaf: @prefix dc: @prefix xsd: @prefix cc:

<#jsled> a foaf:Person ; foaf:nick “jsled” ; rdfs:seeAlso .

<#newYearsGingerMead> a hb:Recipe ; rdf:label “A ginger mead recipe based on Papazian.” ; hb:derivativeOf <#papazian_melimigueleniumGingerMead> ; foaf:maker <#jsled> ; hb:ingredient [ :amt [ rdf:value 15; hb:units "lbs" ]; rdf:label “local honey, clover” ] ,[ :amt [ rdf:value 0.5; hb:units "lbs" ]; rdf:label “grated ginger root” ] ,[ :amt [ rdf:value 2 ]; rdf:label “Red Star champagne yeast [yellow packet].” ] .

<#newYearsMead2004_5> a hb:BrewInstance ; hb:recipe <#newYearsGingerMead> ; dc:created “2004-12-31-05:00″^^xsd:date ; foaf:maker <#jsled>, <#oddmonster> ; hb:journal [ dc:created "2004-12-31-05:00"^^xsd:date; rdfs:label "OG 1.094 @~70 deg F; very sugary, very tasty!" ] ,[ dc:created "2005-01-06-05:00"^^xsd:date; rdfs:label "has been bubbling quite consistently every few seconds for days, now. No significant fermentation spike." ] ,[ dc:created "2005-01-10-05:00"^^xsd:date; rdfs:label "bloop'ing every ~10 seconds at 2-ripples-full" ] .

<#averagedEsb> a hb:Recipe ; rdf:label “A basic ESB recipe.” ; dc:created “2005-01-10-05:00″^^xsd:date ; foaf:maker <#jsled> ; hb:ingredient [ :amt [ rdf:value 4; hb:units "lbs" ]; rdf:label “light extract” ] ,[ :amt [ rdf:value 3; hb:units "lbs" ]; rdf:label “amber extract” ] ,[ :amt [ rdf:value 1; hb:units "lbs" ]; rdf:label “local honey, clover” ] ,[ :amt [ rdf:value 2; hb:units "oz" ]; rdf:label “Fuggles @ 60 min” ] ,[ :amt [ rdf:value 1.5; hb:units "oz" ]; rdf:label “Fuggles @ 15 min” ] ,[ :amt [ rdf:value 0.5; hb:units "oz" ]; rdf:label “Fuggles @ 3 min” ] ,[ :amt [ rdf:value 2; hb:units "tsp" ]; rdf:label “gypsum” ] ,[ :amt [ rdf:value 1 ]; rdf:label “ale yeast” ] .

<#averagedEsb2005> a hb:BrewInstance ; hb:recipe <#averagedEsb> ; dc:created “2005-01-27T19:11:00-05:00″^^xsd:dateTime ; foaf:maker <#jsled> ; hb:journal [ dc:created "2005-01-10-05:00"^^xsd:date; rdf:label "Used liquid Wyeast 1968 from Nov 2004, incubated 2 days. OG: 1.056 @ 60 deg F." ] ,[ dc:created "2005-01-22-05:00"^^xsd:date; rdf:label "racked to glass carboy; FG: 0.016 @ 60 deg F; tastes good, though still a bit sharp. The hops didn't come throuhg nearly as strongly as expected. I'm looking forward to more maturation." ] .

Winooski, VT water mineral content / analysis

When brewing beer, certain styles are partially characterized by the water quality. Since beer is some 90% water, it’s composition can weigh heavily on the output. As well certain styles have come to be popular or even viable because of historical local ground-water conditions; particular English bitter institutions such as Bass are that way because of the super-hard water of Burton-on-Trent … Czech pilsners aren’t the same unless they’re devoid of minerals, as the groundwater of Pilzen is.

Thus, it’s useful to get an anlysis of ones ground water if you’re trying to brew certain styles; this may be posted on-line if we were in Palo Alto or San Francisco, but the Winooski water dept. has some catching up to do.

In any case, this is as of 2003, I believe, so it should still be basically valid for a few years:

  • Calcium as CaCO3 (Calcium Carbonate): 45 ppm
  • Chloride: 17 ppm
  • Iron: < 0.01 pm
  • Manganese: 0.007-0.012 ppm
  • Sodium: 7.5 ppm
  • Potassium: 1.31 ppm
  • Sulfate: 15 ppm
  • Silica: 1.4 ppm
  • Silicon: 0.67 ppm

ale hallows eve, 2004

Time for a pumpkin beer…

As I didn’t get around to doing it until All Hallows Eve itself, Odd’ suggested the one-day-appropriate name Ale Hallows Eve.

@prefix : @prefix hb: :. @prefix rdfs: @prefix foaf: @prefix dc: @prefix cc:

<#jsled> a foaf:Person ; foaf:nick “jsled” ; rdfs:seeAlso .

<#aleHallowsEve2004> a hb:Recipe ; foaf:maker <#jsled> ; a cc:Work ; cc:license cc:PublicDomain ; hb:ingredient [ :amt "3 lbs"; dc:description "light malt extract" ] ,[ :amt "3 lbs"; dc:description "amber malt extract" ] ,[ :amt "1 lb" ; dc:description "Crystal 60L grain; crushed." ] ,[ :amt "1 oz" ; dc:description "Styrian Goldings [pellet]” ] ,[ :amt "0.5 oz"; dc:description "Fuggles [pellet]” ] ,[ :amt "1 tbs" ; dc:description "cinnamon" ] ,[ :amt "1/2 tbs"; dc:description "nutmeg" ] ,[ :amt "1 tbs"; dc:description "clove" ] ,[ :amt "dashes"; dc:description "coriander seed, ground" ] ,[ :amt "1 tsp"; dc:description "ginger, ground" ] ,[ :amt "1.5 tsp"; dc:description "Irish Moss" ] ,[ :amt "6g"; dc:description "Munton's Ale yeast, dried" ] ,[ :amt "3"; dc:description "pie pumpkins" ] ; hb:instruction “Combine spices together.” ,”Cut pumpkins open, remove strings and seeds. Reserve stringy innards.” ,”Bake on cookie sheets in oven, meat-side up, dusted with 1/2 of the spices. Bake at 350 deg F for ~1 hour.” ,”Remove skin; cut into 1/2-inch cubes.” ,”Steep grains + stringy innards in ~2 quarts water @ 170-160 deg F for 1 hour.” ,”Remove grains + pumpkin. Heat wort to boil.” ,”Add malt extract, bring to boil.” ,”At 60 minutes, add Goldings and dust surface of wort with spice mixture.” ,”At 30 minutes, add pumpkin and dust surface of wort with spice mixture. Add Fuggles.” ,”At 20 minutes, add Irish moss.” ,”At 15 minutes, add remaining spices.” ,”At 0 minutes, remove from heat and chill.” ,”During chilling, remove pumpkin pieces from cooling wort to cold water in fermenter.” ,”Transfer into fermenter.” ,”Pitch yeast.” .

<#aleHallowsEve2004inst> a hb:BrewInstance ; hb:recipe <#aleHallowsEve2004> ; dc:date “2004.10.31″ ; foaf:maker <#jsled> ; hb:journal [ a hb:JournalEntry; dc:created "2004.10.31T19:27-0500" ; dc:description "OG 1.046 ~ 84 deg F" ] .

tasting Old Growler

As I mentioned before, I had my second homebrewing experience not too long ago, and the first one to bear fruit … or … beer, as it were.

There was a good 8 days of fermentation, which was actually longer than necessary, and a semi-hectic evening of bottling. 10 days later — Friday the 24th — I opened the first bottle.

It was about as the earlier taste-tests I’d made, though with a bit less … noise in it. It’s not stellar, but it easily better than many commercial beers I’ve had.


Mmmmm. Beer.

second brew

The oddmonster was kind enough to get me a homebrew starter kit approximately a year ago, though it took me nearly 9 months to actually sit down and use the thing.

I pitched the original yeast which came with the starter kit, but certainly after 9 months of being uncared for there was no hope that a bag of dead yeast would do anything. As well, no matter how many times I read and re-read the directions before and during the process, a measuring mistake or two was introduced. I noticed that there was no fermentation the next day, and went down to the local homebrew supply place to get some fresh/real yeast, and re-pitched the next evening. Strangely, there was still no fermentation activity the next morning, and hopes were not high.

After a continued lack of activity and more than enough time, I removed the 1-gallon cubitainer from the basement and poured out a sample. Not only had no fermentation occurred, but it was pretty nasty. Into the sink it went.

Just after the Oddmo’ got me the kit, a friend was kind enough — in the midst of cleaning house — to donate to me both various homebrew accoutrements and a plethora of text, which I’d been studiously reading for the past year…

Mr. Whyte runs the homebrew supply store just down the road, and was kind enough to be working on Labor Day … from the looks of it, a good day to be conducting commerce. I was able to jump in and get kitted out … a food-grade fermentation bucket, airlock, hydrometer, siphon, &c…. and the ingredients of the recipe for 5 gallons (19L) of Old Growler

Homebrewing is a pretty simple art: water + malted-barley + grain + sugar + yeast + wait = beer. The amazing thing is just how long it takes to heat and cool 2 gallons of water…

After starting at 7pm, at 11:40pm last night, I happily closed the lid on my second batch.

It was exciting to check in on it all day, and watch copious bubbles of CO2 come through the airlock.

Now to figure out how to do all my record keeping in RDF…

Old Growler

For 5g/19L ; 1.040-1.050 OG; ~4.4% ABV ; 10g dry ale yeast.

Grains: * 4 oz Victory * 8 oz Crystal 60 * 4 oz Chocolate * 4 oz Wheat

Extracts: * 5 lb Light Malt Extract * 8 oz Light Brown Sugar

Hopping Schedule: * 1 oz Fuggles – 60 min * 0.5 oz Fuggles – 30 min * 0.5 oz Fuggles – 5 min