Create a gist now

Instantly share code, notes, and snippets.

@sli /
Last active Dec 29, 2015

A parser for eJuice Me Up recipes.
class Recipe(object):
def __init__(self, recipe):
self.__data = {
recipe = recipe.split('\n')
self.__data['nicotine_base'] = int(recipe[0])
self.__data['nicotine_target'] = int(recipe[1])
self.__data['amount'] = int(recipe[2])
self.__data['cut'] = int(recipe[3])
self.__data['target_pg'] = int(recipe[14])
self.__data['target_vg'] = int(recipe[15])
self.__data['unknown_a'] = int(recipe[16])
self.__data['unknown_b'] = int(recipe[17])
self.__data['base_pg_percent'] = int(recipe[18])
self.__data['base_vg_percent'] = int(recipe[19])
self.__data['drops_per_ml'] = int(recipe[30])
self.__data['notes'] = '\n'.join(recipe[46:]).rstrip()
self.__flavors = FlavorList(recipe)
def __getattr__(self, name):
if name == 'flavors':
return self.__flavors
elif name in self.__data.keys():
return self.__data[name]
raise AttributeError
def __repr__(self):
return '<Recipe>'
def __dir__(self):
return sorted(set(dir(super(Recipe, self)) + self.__data.keys()))
# Handles a list of Flavor objects
class FlavorList(object):
def __init__(self, recipe):
self.__flavors = (
Flavor((recipe[9], recipe[4], recipe[20], recipe[25], recipe[31])),
Flavor((recipe[10], recipe[5], recipe[21], recipe[26], recipe[32])),
Flavor((recipe[11], recipe[6], recipe[22], recipe[27], recipe[33])),
Flavor((recipe[12], recipe[7], recipe[23], recipe[28], recipe[34])),
Flavor((recipe[13], recipe[8], recipe[24], recipe[29], recipe[35])),
Flavor((recipe[37], recipe[36], recipe[38], recipe[39], recipe[40])),
Flavor((recipe[42], recipe[41], recipe[43], recipe[44], recipe[45])),
def __getitem__(self, index):
return self.__flavors[index]
# Holds the data for a single flavor
class Flavor(object):
def __init__(self, flavor):
self.__data = {
self.__data['name'] = flavor[0]
self.__data['percent'] = int(flavor[1])
self.__data['pg_percent'] = int(flavor[2])
self.__data['vg_percent'] = int(flavor[3])
self.__data['flavorless'] = bool(int(flavor[4]))
def __getattr__(self, name):
return self.__data[name]
def __dir__(self):
return sorted(set(dir(super(Flavor, self)) + self.__data.keys()))
# Load up a recipe and return a Recipe object
def load_recipe(fn):
fcons = open(fn, 'r').read()
return Recipe(fcons)
if __name__ == '__main__':
import sys
if len(sys.argv) < 2:
print 'Usage: <recipe filename>'
r = load_recipe(sys.argv[1])
print r

Line 18: Line separator is '\r\n' (0x0d 0x0a), maybe this doesn't matter in Python?

Line 30: Notes are multi-line

$ cat -n White\ Rabbit2.rec
    42  0
    43  by: Brenda
    44  100
    45  0
    46  0
    47  Line 1 of Notes
    48  Line 2 of Notes
    49  Line 3 of Notes

Line 74: 'flavorless' is '0 PG / 0 VG' flag (to turn off the PG+VG=100 constraint)


Maybe in the array of flavors, prune out the '0%' entries, if there's text in the flavor name field and the percentage is 0, then move it to notes. This breaks round-trip-ability but makes for a better recipe (IMHO, not having 0% 'empty' flavors and having the 'by: Somebody' or 'Menthol to taste' type of things moved to Notes is a good thing).

sli commented Dec 4, 2013

You're right about the multiline notes thing. I actually wrote this, then updated eJuice to the most recent version. The one I developed against didn't support notes at all, so it was a bit of an afterthought. I'll fix that.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment