Began versioning
[cookbook.git] / cookbook / mom.py
1 # Copyright
2
3 """Hack parser for standardizing my Mom's cookbook text.
4 """
5
6 from .cookbook import (
7     Cookbook, Recipe, IngredientBlock, Ingredient, Amount, Directions)
8
9
10 class MomParser (object):
11     def parse(self, filename):
12         c = Cookbook()
13         consecutive_blanks = 100
14         recipe_lines = None
15         for line in file(filename, 'r'):
16             line = line.strip().decode('utf-8')
17             if line == '':
18                 if recipe_lines != None and consecutive_blanks == 0:
19                     recipe_lines.append('')
20                 consecutive_blanks += 1
21                 continue
22             if consecutive_blanks >= 2:
23                 if recipe_lines != None:
24                     c.append(self._parse_recipe(recipe_lines))
25                 recipe_lines = [line]
26             else:
27                 recipe_lines.append(line)
28             consecutive_blanks = 0
29         return c
30
31     def _parse_recipe(self, lines):
32         name = lines.pop(0)
33         yield_,author,source,url,lines = self._parse_yield_line(lines)
34         ingredient_blocks,lines = self._parse_ingredient_blocks(lines)
35         directions,lines = self._parse_directions(lines)
36         assert len(lines) == 0, lines
37         return Recipe(
38             name=name,
39             ingredient_blocks=ingredient_blocks,
40             directions=directions,
41             yield_=yield_,
42             author=author,
43             source=source,
44             url=url)
45
46     def _parse_yield_line(self, lines):
47         while len(lines) > 0 and lines[0] == '':
48             lines.pop(0)
49         fields = ['yield', 'from', 'source', 'url']
50         yield_ = author = source = url = None
51         matching_line = False
52         for field in fields:
53             if field in lines[0].lower():
54                 matching_line = True
55                 break
56         if matching_line == True:
57             bits = lines.pop(0).split('\t')
58             for bit in bits:
59                 for field in fields:
60                     if bit.lower().startswith(field+':'):
61                         value = bit[len(field+':'):].strip()
62                         if field == 'yield':
63                             yield_ = value.replace('Serving', 'serving')
64                         elif field == 'from':
65                             author = value
66                         elif field == 'source':
67                             source = value
68                         elif field == 'url':
69                             url = value
70                         break
71         return (yield_, author, source, url, lines)
72
73     def _parse_ingredient_blocks(self, lines):
74         ingredient_blocks = []
75         first_block = True
76         while True:
77             while len(lines) > 0 and lines[0] == '': # scroll past blanks
78                 lines.pop(0)
79             if (len(lines) == 0
80                 or not (first_block == True
81                         or lines[0].endswith(':'))):
82                 break
83             if lines[0].endswith(':'):
84                 line = lines.pop(0)
85                 name = line[:-1].strip()
86             else:
87                 name = None
88             block = IngredientBlock(name)
89             while len(lines) > 0 and lines[0] != '':
90                 block.append(self._parse_ingredient_line(lines.pop(0)))
91             ingredient_blocks.append(block)
92             first_block = False
93         return (ingredient_blocks, lines)
94
95     def _parse_ingredient_line(self, line):
96         if line.lower().startswith('1 red'):
97             line = '1 # red'+line[len('1 red'):]
98         try:
99             value,units,name = line.split(' ', 2)
100         except ValueError:
101             print line,
102             raise
103         if units == '#':
104             units = None
105         elif units == 'Large':
106             units = 'large'
107         elif units == 'Cloves':
108             units = 'cloves'
109         return Ingredient(name, Amount(value, units))
110
111     def _parse_directions(self, lines):
112         directions = Directions()
113         paragraph = []
114         for line in lines:
115             if line == '':
116                 if len(paragraph) > 0:
117                     directions.append('\n'.join(paragraph))
118                     paragraph = []
119             else:
120                 paragraph.append(line)
121         return (directions, [])