public
Last active

A parser for eJuice Me Up recipes.

  • Download Gist
ejuice.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
class Recipe(object):
def __init__(self, recipe):
self.__data = {
'nicotine_base':0,
'nicotine_target':0,
'amount':0,
'cut':0,
'target_pg':0,
'target_vg':0,
'unknown_a':0,
'unknown_b':0,
'base_pg_percent':0,
'base_vg_percent':0,
'drops_per_ml':0,
'notes':''
}
 
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]
else:
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 = {
'name':'',
'percent':0,
'pg_percent':0,
'vg_percent':0,
'flavorless':False
}
 
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: ejuice.py <recipe filename>'
sys.exit(1)
else:
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).

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.

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.