~vorlon/ubuntu/saucy/gourmet/trunk

« back to all changes in this revision

Viewing changes to src/lib/plugins/nutritional_information/old_nutrition/nutrition.py

  • Committer: Bazaar Package Importer
  • Author(s): Rolf Leggewie
  • Date: 2009-01-12 23:03:28 UTC
  • mfrom: (2.1.3 sid)
  • Revision ID: james.westby@ubuntu.com-20090112230328-wnuqqte22uk3981b
Tags: 0.14.3-2
Conflict with python-pysqlite >= 2.5 which when installed crashes 
gourmet at startup. (Closes: #507382) and python-sqlalchemy >= 0.5.
Both restrictions should eventually be relaxed when upstream has
made the code compatible with the newer libs.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
import re, string
2
 
import sys
3
 
from parser_data import SUMMABLE_FIELDS
4
 
 
5
 
# Our basic module for interaction with our nutritional information DB
6
 
 
7
 
class NutritionData:
8
 
 
9
 
    """Handle all interactions with our nutrition database.
10
 
 
11
 
    We provide methods to set up equivalences between our
12
 
    ingredient-keys and our nutritional data.
13
 
    """
14
 
    
15
 
    def __init__ (self, db, conv):
16
 
        self.db = db
17
 
        self.conv = conv
18
 
        self.conv.density_table
19
 
        self.gramwght_regexp = re.compile("([0-9.]+)?( ?([^,]+))?(, (.*))?")
20
 
        self.wght_breaker = re.compile('([^ ,]+)([, ]+\(?(.*)\)?)?$')
21
 
 
22
 
    def set_key (self, key, row):
23
 
        """Create an automatic equivalence for ingredient key 'key' and nutritional DB row ROW
24
 
        """        
25
 
        if not row: row = self._get_key(key)
26
 
        #density=self.get_density(key,row)
27
 
        if row: self.row.ndbno=row.ndbno
28
 
        else:
29
 
            self.db.do_add(self.db.nutritionaliases_table,
30
 
                           {'ndbno':row.ndbno,
31
 
                            'ingkey':key})
32
 
 
33
 
    def set_density_for_key (self, key, density_equivalent):
34
 
        self.db.update(
35
 
            self.db.nutritionaliases_table,
36
 
            {'ingkey':key},
37
 
            {'density_equivalent':density_equivalent}
38
 
            )
39
 
 
40
 
    def set_key_from_ndbno (self, key, ndbno):
41
 
        """Create an automatic equivalence between ingredient key 'key' and ndbno
42
 
        ndbno is our nutritional database number."""
43
 
        if type(ndbno)!=int:
44
 
            ndbno = int(ndbno)
45
 
        prev_association = self.db.fetch_one(self.db.nutritionaliases_table,ingkey=key)
46
 
        if prev_association:
47
 
            self.db.do_modify(self.db.nutritionaliases_table,
48
 
                              prev_association,
49
 
                              {'ndbno':ndbno})
50
 
        else:
51
 
            self.db.do_add(self.db.nutritionaliases_table,{'ndbno':ndbno,
52
 
                                                 'ingkey':key}
53
 
                           )
54
 
 
55
 
    def set_conversion (self, key, unit, factor):
56
 
        """Set conversion for ingredient key.
57
 
 
58
 
        factor is the amount we multiply by to get from unit to grams.
59
 
        """
60
 
        if self.conv.unit_dict.has_key(unit):
61
 
            unit = self.conv.unit_dict[unit]
62
 
        prev_entry = self.db.fetch_one(self.db.nutritionconversions_table,
63
 
                                       **{'ingkey':key,'unit':unit})
64
 
        if prev_entry:
65
 
            self.db.do_modify(self.db.nutritionconversions_table,
66
 
                               prev_entry,
67
 
                               {'factor':factor})
68
 
        else:
69
 
            self.db.do_add(self.db.nutritionconversions_table,{'ingkey':key,'unit':unit,'factor':factor})
70
 
 
71
 
    def get_matches (self, key, max=50):
72
 
        """Handed a string, get a list of likely USDA database matches.
73
 
 
74
 
        We return a list of lists:
75
 
        [[description, nutritional-database-number],...]
76
 
 
77
 
        If max is not none, we cut our list off at max items (and hope our
78
 
        sorting algorithm succeeded in picking out the good matches!).
79
 
        """
80
 
        words=re.split("\W",key)
81
 
        words = filter(lambda w: w and not w in ['in','or','and','with'], words)
82
 
        #words += ['raw']
83
 
        result =  self.db.search_nutrition(words)
84
 
        while not result and len(words)>1:
85
 
            words = words[:-1]
86
 
            result = self.db.search_nutrition(words)
87
 
        if result:
88
 
            return [(r.desc,r.ndbno) for r in result]
89
 
        else:
90
 
            return None
91
 
            
92
 
    def _get_key (self, key):
93
 
        """Handed an ingredient key, get our nutritional Database equivalent
94
 
        if one exists."""
95
 
        row=self.db.fetch_one(self.db.nutritionaliases_table,**{'ingkey':str(key)})
96
 
        return row
97
 
 
98
 
    def get_nutinfo_for_ing (self, ing, rd=None):
99
 
        """A convenience function that grabs the requisite items from
100
 
        an ingredient.
101
 
 
102
 
        As a side effect we set the ingObject attribute on the object we return
103
 
        """
104
 
        rd = self.db
105
 
        if hasattr(ing,'refid') and ing.refid:
106
 
            subrec = rd.get_referenced_rec(ing)
107
 
            nutinfo = self.get_nutinfo_for_inglist(rd.get_ings(subrec),rd)
108
 
            nutinfo.ingObject = ing
109
 
            print '(recref) Set nutinfo.ingObject to ',ing
110
 
            return nutinfo
111
 
        if hasattr(ing,'rangeamount') and ing.rangeamount:
112
 
            # just average our amounts
113
 
            amount = (ing.rangeamount + ing.amount)/2
114
 
        else:
115
 
            amount = ing.amount
116
 
        if not amount: amount=1
117
 
        nutinfo =  self.get_nutinfo_for_item(ing.ingkey,amount,ing.unit)
118
 
        print 'Setting ingObject to',ing
119
 
        nutinfo.ingObject = ing
120
 
        return nutinfo
121
 
 
122
 
    def get_nutinfo_for_inglist (self, inglist, rd):
123
 
        """A convenience function to get NutritionInfoList for a list of
124
 
        ingredients.
125
 
        """
126
 
        print 'get_nutinfo_for_inglist',inglist,rd
127
 
        return NutritionInfoList([self.get_nutinfo_for_ing(i,rd) for i in inglist])
128
 
 
129
 
    def get_nutinfo_for_item (self, key, amt, unit):
130
 
        """Handed a key, amount and unit, get out nutritional Database object.
131
 
        """
132
 
        ni=self.get_nutinfo(key)
133
 
        if not amt:
134
 
            amt = 1
135
 
        if ni:
136
 
            c=self.get_conversion_for_amt(amt,unit,key)
137
 
            if c:
138
 
                return NutritionInfo(ni,mult=c)
139
 
        return NutritionVapor(self,key,
140
 
                              rowref=ni,
141
 
                              amount=amt,
142
 
                              unit=unit)
143
 
 
144
 
    def get_nutinfo (self, key):
145
 
        """Get our nutritional information for ingredient key 'key'
146
 
        We return an object interfacing with our DB whose attributes
147
 
        will be nutritional values.
148
 
        """
149
 
        aliasrow = self._get_key(key)
150
 
        if aliasrow:
151
 
            nvrow=self.db.fetch_one(self.db.nutrition_table,**{'ndbno':aliasrow.ndbno})
152
 
            if nvrow: return NutritionInfo(nvrow)
153
 
        # if we don't have a nutritional db row, return a
154
 
        # NutritionVapor instance which remembers our query and allows
155
 
        # us to redo it.  The idea here is that our callers will get
156
 
        # an object that can guarantee them the latest nutritional
157
 
        # information for a given item.
158
 
        return NutritionVapor(self,key)
159
 
 
160
 
    def get_ndbno (self, key):
161
 
        aliasrow = self._get_key(key)
162
 
        if aliasrow: return aliasrow.ndbno
163
 
        else: return None
164
 
 
165
 
    def convert_to_grams (self, amt, unit, key, row=None):
166
 
        conv = self.get_conversion_for_amt(amt,unit,key,row)
167
 
        if conv: return conv*100
168
 
        else:
169
 
            return None
170
 
 
171
 
    def get_conversion_for_amt (self, amt, unit, key, row=None, fudge=True):
172
 
        """Get a conversion for amount amt of unit 'unit' to USDA standard.
173
 
 
174
 
        Multiplying our standard numbers (/100g) will get us the appropriate
175
 
        calories, etc.
176
 
 
177
 
        get_conversion_for_amt(amt,unit,key) * 100 will give us the
178
 
        number of grams this AMOUNT converts to.
179
 
        """
180
 
        # our default is 100g
181
 
        cnv=self.conv.converter('g',unit)
182
 
        if not row: row=self.get_nutinfo(key)
183
 
        if not cnv:
184
 
            cnv = self.conv.converter('g',unit,
185
 
                                      density=self.get_density(key,row,fudge=fudge)
186
 
                                      )
187
 
        if not cnv:
188
 
            # Check our weights tables...
189
 
            extra_conversions = self.get_conversions(key,row)[1]
190
 
            if extra_conversions.has_key(unit):
191
 
                cnv = extra_conversions[unit]
192
 
            elif unit and extra_conversions.has_key(unit.lower()):
193
 
                cnv = extra_conversions[unit.lower()]
194
 
        if not cnv:
195
 
            # lookup in our custom nutrition-related conversion table
196
 
            if self.conv.unit_dict.has_key(unit):
197
 
                unit = self.conv.unit_dict[unit]
198
 
            elif not unit:
199
 
                unit = ''
200
 
            lookup = self.db.fetch_one(self.db.nutritionconversions_table,ingkey=key,unit=unit)
201
 
            if lookup:
202
 
                cnv = lookup.factor
203
 
            else:
204
 
                # otherwise, cycle through any units we have and see
205
 
                # if we can get a conversion via those units...
206
 
                for conv in self.db.fetch_all(self.db.nutritionconversions_table,ingkey=key):
207
 
                    factor = self.conv.converter(unit,conv.unit)
208
 
                    if factor:
209
 
                        cnv = conv.factor*factor
210
 
        if cnv:
211
 
            return (0.01*amt)/cnv
212
 
 
213
 
    def get_conversions (self, key=None, row=None):
214
 
        """Handed an ingredient key or a row of the nutrition database,
215
 
        we return two dictionaries, one with Unit Conversions and the other
216
 
        with densities. Our return dictionaries look like this:
217
 
        ({'chopped':1.03, #density dic
218
 
          'melted':1.09},
219
 
         {'piece':27,
220
 
          'leg':48,} # unit : grams
221
 
          )"""
222
 
        if not row: row=self.get_nutinfo(key)
223
 
        if not row: return {},{}
224
 
        units = {}
225
 
        densities = {}
226
 
        for gd,gw in self.get_gramweights(row).items():
227
 
            a,u,e=gd
228
 
            if a:
229
 
                convfactor = self.conv.converter(u,'ml')
230
 
                if convfactor: #if we are a volume
231
 
                    # divide mass by volume converted to mililiters
232
 
                    # (since gramwts are in grams!)
233
 
                    density = float(gw) / (a * convfactor)
234
 
                    densities[e]=density
235
 
                    continue
236
 
            # if we can't get a density from this amount, we're going to treat it as a unit!
237
 
            if e: u = u + ", " + e
238
 
            if a: gw = float(gw)/a
239
 
            else:
240
 
                gw = float(gw)
241
 
            if u: units[u]=gw
242
 
        return densities,units
243
 
            
244
 
    def get_densities (self,key=None,row=None):
245
 
        """Handed key or nutrow, return dictionary with densities."""
246
 
        if not row: row = self._get_key(key)
247
 
        if not row: return {}
248
 
        if self.conv.density_table.has_key(key):
249
 
            return {'':self.conv.density_table[key]}
250
 
        else:
251
 
            densities = {}       
252
 
            for gd,gw in self.get_gramweights(row).items():
253
 
                a,u,e = gd
254
 
                if not a:
255
 
                    continue
256
 
                convfactor=self.conv.converter(u,'ml')
257
 
                if convfactor: # if we are a volume
258
 
                    # divide mass by volume converted to milileters
259
 
                    # (gramwts are in grams)
260
 
                    density = float(gw) / (a * convfactor)
261
 
                    densities[e]=density
262
 
            return densities
263
 
 
264
 
    def get_gramweights (self,row):
265
 
        """Return a dictionary with gram weights.
266
 
        """
267
 
        ret = {}
268
 
        nutweights = self.db.fetch_all(self.db.usda_weights_table,**{'ndbno':row.ndbno})
269
 
        for nw in nutweights:
270
 
            mtch = self.wght_breaker.match(nw.unit)
271
 
            if not mtch:
272
 
                unit = nw.unit
273
 
                extra = None
274
 
            else:
275
 
                unit = mtch.groups()[0]
276
 
                extra = mtch.groups()[2]
277
 
            ret[(nw.amount,unit,extra)]=nw.gramwt
278
 
        return ret
279
 
    
280
 
    def get_density (self,key=None,row=None, fudge=True):
281
 
        densities = self.get_densities(key,row)
282
 
        if densities.has_key(''): densities[None]=densities['']
283
 
        if key: keyrow=self._get_key(key)        
284
 
        if densities:
285
 
            if key and keyrow.density_equivalent and densities.has_key(keyrow.density_equivalent):
286
 
                return densities[keyrow.density_equivalent]
287
 
            elif densities.has_key(None):
288
 
                self.conv.density_table[key]=densities[None]
289
 
                return densities[None]
290
 
            elif len(densities)==1:
291
 
                return densities.values()[0]
292
 
            elif fudge:
293
 
                return sum(densities.values())/len(densities)
294
 
            else:
295
 
                return None
296
 
 
297
 
    def parse_gramweight_measure (self, txt):
298
 
        m=self.gramwght_regexp.match(txt)
299
 
        if m:
300
 
            groups=m.groups()
301
 
            amt = groups[0]
302
 
            if amt: amt = float(amt)
303
 
            unit = groups[2]
304
 
            extra = groups[4]
305
 
            return amt,unit,extra
306
 
 
307
 
    def add_custom_nutrition_info (self, nutrition_dictionary):
308
 
        """Add custom nutritional information."""
309
 
        #new_ndbno = self.db.increment_field(self.db.nutrition_table,'ndbno')
310
 
        #if new_ndbno: nutrition_dictionary['ndbno']=new_ndbno
311
 
        return self.db.do_add_nutrition(nutrition_dictionary).ndbno
312
 
        
313
 
                    
314
 
class NutritionInfo:
315
 
    """A multipliable way to reference an object.
316
 
 
317
 
    Any attribute of object that can be mutiplied, will be returned
318
 
    multiplied by mult.
319
 
 
320
 
    We can also support various mathematical operators
321
 
    n = NutritionInfo(obj, mult=2)
322
 
    n * 2 -> NutritionInfo(obj,mult=4)
323
 
    n2 = NutritionInfo(obj2, mult=3)
324
 
    n2 + n -> NutritionInfoList([n2,n])
325
 
 
326
 
    The result is that addition and multiplication 'makes sense' for
327
 
    properties. For example, if we have nutrition info for 1 carrot,
328
 
    we can multiply it or add it to the nutrition info for an
329
 
    eggplant. The resulting object will reflect the appropriate
330
 
    cumulative values.
331
 
 
332
 
    Carrot = NutritionInfo(CarrotNutritionRow)
333
 
    Eggplant = NutritionInfo(EggplantNutritionRow)
334
 
 
335
 
    Carrot.kcal => 41
336
 
    Eggplant.kcal => 24
337
 
    (Carrot + Eggplant).kcal => 65
338
 
    (Carrot * 3 + Eggplant).kcal => 147
339
 
 
340
 
    This will be true for all numeric properties.
341
 
 
342
 
    Non numeric properties return a somewhat not-useful string:
343
 
    
344
 
    (Carrot + Eggplant).desc => 'CARROTS,RAW, EGGPLANT,RAW'
345
 
    """
346
 
    def __init__ (self,rowref, mult=1, fudged=False):
347
 
        self.__rowref__ = rowref
348
 
        self.__mult__ = mult
349
 
        self.__fudged__ = fudged
350
 
 
351
 
    def __getattr__ (self, attr):
352
 
        if attr[0]!='_':
353
 
            ret = getattr(self.__rowref__, attr)
354
 
            try:
355
 
                if attr in SUMMABLE_FIELDS:
356
 
                    return (ret or 0) * self.__mult__
357
 
                else:
358
 
                    return ret
359
 
            except:
360
 
                raise
361
 
        else:
362
 
            # somehow this magically gets us standard
363
 
            # attribute handling...
364
 
            raise AttributeError, attr
365
 
 
366
 
    def __add__ (self, obj):
367
 
        if isinstance(obj,NutritionInfo):
368
 
            return NutritionInfoList([self,obj])
369
 
        elif isinstance(obj,NutritionInfoList):
370
 
            return NutritionInfoList([self]+obj.__nutinfos__)
371
 
 
372
 
    def __mul__ (self, n):
373
 
        return NutritionInfo(self.__rowref__, self.__mult__ * n)
374
 
 
375
 
KEY_VAPOR = 0 # when we don't have a key
376
 
UNIT_VAPOR = 1 # when we can't interpret the unit
377
 
DENSITY_VAPOR = 2 # when we don't have a density
378
 
AMOUNT_VAPOR = 3 # when there is no amount, leaving us quite confused
379
 
 
380
 
class NutritionVapor (NutritionInfo):
381
 
    """An object to hold our nutritional information before we know it.
382
 
 
383
 
    Basically, we have to behave like a NutritionInfo class that doesn't
384
 
    actually return any data.
385
 
 
386
 
    We also can return information about why we're still vapor
387
 
    (whether we need density info, key info or what...).
388
 
    """
389
 
    def __init__ (self, nd, key,
390
 
                  rowref=None,
391
 
                  mult=None,
392
 
                  amount=None,
393
 
                  unit=None,):
394
 
        self.__nd__ = nd
395
 
        self.__rowref__ = rowref
396
 
        self.__key__ = key
397
 
        self.__mult__ = mult
398
 
        self.__amt__ = amount
399
 
        self.__unit__ = unit
400
 
 
401
 
    def _reset (self):
402
 
        """Try to create matter from vapor and return it.
403
 
 
404
 
        If we fail we return more vapor."""
405
 
        if not self.__rowref__:
406
 
            if self.__mult__:
407
 
                ni = self.__nd__.get_nutinfo(self.__key__)
408
 
                if not isinstance(ni,NutritionVapor): return ni * self.__mult__
409
 
                else: return self
410
 
            else:
411
 
                return self.__nd__.get_nutinfo_for_item(self.__key__,
412
 
                                                        self.__amt__,
413
 
                                                        self.__unit__)
414
 
        elif self.__amt__:
415
 
            c=self.__nd__.get_conversion_for_amt(self.__amt__,self.__unit__,self.__key__,fudge=False)
416
 
            if c:
417
 
                self.__mult__ = c
418
 
                return NutritionInfo(self.__rowref__,
419
 
                                     self.__mult__)
420
 
            else:
421
 
                c=self.__nd__.get_conversion_for_amt(self.__amt__,self.__unit__,self.__key__,fudge=True)
422
 
                if c:
423
 
                    self.__mult__ = c
424
 
                    return NutritionInfo(self.__rowref__,
425
 
                                         self.__mult__)
426
 
                else:
427
 
                    return self
428
 
        else: return self.__nd__.get_nutinfo_for_item(self.__key__,self.__amt__,self.__unit__)
429
 
 
430
 
    def __getattr__ (self,attr):
431
 
        """Return 0 for any requests for a non _ prefixed attribute."""
432
 
        if attr[0]!='_':
433
 
            return 0
434
 
        else:
435
 
            raise AttributeError,attr
436
 
 
437
 
    def __repr__ (self):
438
 
        return '<NutritionVapor %s>'%self.__key__
439
 
    
440
 
    def __nonzero__ (self):
441
 
        """Vapor is always False."""
442
 
        return False
443
 
 
444
 
    def _wheres_the_vapor (self):
445
 
        """Return a key as to why we're vapor."""
446
 
        if not self.__rowref__: return KEY_VAPOR
447
 
        elif not self.__amt__: return AMOUNT_VAPOR
448
 
        else: return UNIT_VAPOR
449
 
    
450
 
class NutritionInfoList (list, NutritionInfo):
451
 
    """A summable list of objects.
452
 
 
453
 
    When we ask for numeric attributes of our members, we get the sum.
454
 
    """
455
 
    def __init__ (self,nutinfos, mult=1):
456
 
        self.__nutinfos__ = nutinfos
457
 
        #self.__len__ = self.__nutinfos__.__len__
458
 
        #self.__getitem__ = self.__nutinfos__.__getitem__
459
 
        self.__mult__ = 1
460
 
 
461
 
    def __getattr__ (self, attr):
462
 
        if attr[0]!='_':
463
 
            alist = [getattr(ni,attr) for ni in self.__nutinfos__]
464
 
            if attr in SUMMABLE_FIELDS:
465
 
                if self.__mult__: alist = [n * self.__mult__ for n in alist]
466
 
                return sum(alist)
467
 
            else:
468
 
                return ", ".join(map(str,alist))
469
 
        else:
470
 
            # somehow this magically gets us standard
471
 
            # attribute handling...
472
 
            raise AttributeError, attr
473
 
 
474
 
    def _reset (self):
475
 
        """See if we can turn any of our vapor into matter."""
476
 
        for i in range(len(self.__nutinfos__)):
477
 
            obj = self.__nutinfos__[i]
478
 
            if isinstance(obj,NutritionVapor):
479
 
                # try resetting
480
 
                self.__nutinfos__[i]=obj._reset()
481
 
 
482
 
    def _get_vapor (self):
483
 
        """Return a list of nutritionVapor if there is any
484
 
 
485
 
        In other words, tell us whether we are missing any nutritional
486
 
        information."""
487
 
        ret = []
488
 
        for i in self.__nutinfos__:
489
 
            if isinstance(i,NutritionVapor): ret.append(i)
490
 
            if isinstance(i,NutritionInfoList):
491
 
                ret.extend(i._get_vapor())
492
 
        return ret
493
 
 
494
 
    def _get_fudge (self):
495
 
        """Return a list of fudged items
496
 
        """
497
 
        ret = []
498
 
        for i in self.__nutinfos__:
499
 
            if hasattr(i,'__fudged__') and i.__fudged__:
500
 
                ret.append(i)
501
 
        return ret
502
 
        
503
 
    def __add__ (self, obj):
504
 
        if isinstance(obj,NutritionInfo):
505
 
            return NutritionInfoList(self.__nutinfos__ + [obj])
506
 
        elif isinstance(obj,NutritionInfoList):
507
 
            return NutritionInfoList(self.__nutinfos__ + obj.__nutinfos__)
508
 
 
509
 
    def __sub__ (self, obj):
510
 
        copy = self.__nutinfos__[0:]
511
 
        copy.remove(obj)
512
 
        return NutritionInfoList(copy)
513
 
 
514
 
    def __getslice__ (self, a, b):
515
 
        return NutritionInfoList(self.__nutinfos__[a:b])
516
 
 
517
 
    def __len__ (self): return len(self.__nutinfos__)
518
 
    def __getitem__ (self,x): return self.__nutinfos__[x]
519
 
 
520
 
    def __repr__ (self):
521
 
        return '<NutritionInfoList>'
522
 
 
523
 
    def recursive_length (self):
524
 
        """Return number of contained nutrition info objects, recursing any embedded lists.
525
 
        """
526
 
        n = 0
527
 
        for x in range(len(self)):
528
 
            obj = self[x]
529
 
            if isinstance(obj,NutritionInfoList):
530
 
                n += obj.recursive_length()
531
 
            else:
532
 
                n += 1
533
 
        return n
534
 
            
535
 
if __name__ == '__main__':
536
 
    import sys
537
 
    sys.path.append('/usr/share/')
538
 
    import gourmet.recipeManager as rm
539
 
    db=rm.RecipeManager(**rm.dbargs)
540
 
    import gourmet.convert
541
 
    conv = gourmet.convert.converter()
542
 
    import nutritionGrabberGui
543
 
    nutritionGrabberGui.check_for_db(db)
544
 
    nd=NutritionData(db,conv)
545
 
 
546
 
def foo ():
547
 
    from gourmet import convert
548
 
    class SimpleInterface:
549
 
        
550
 
        def __init__ (self, nd):
551
 
            self.ACTIONS = {'Add ingredient':self.add_ingredient,
552
 
                       'Add key info':self.add_key,
553
 
                       'Print info':self.print_info,
554
 
                       'Exit' : self.exit
555
 
                       }
556
 
            self.nd = nd
557
 
            self.ings = []
558
 
 
559
 
        def run (self):
560
 
            choices = self.ACTIONS.keys()
561
 
            for n,a in enumerate(choices):
562
 
                print n,a
563
 
            choice = None
564
 
            while not choice:
565
 
                choice = raw_input('Enter number of choice: ')
566
 
                choice = int(choice)
567
 
                if choice < len(choices): choice = self.ACTIONS[choices[choice]]
568
 
                else: choice = None
569
 
            try:
570
 
                choice()
571
 
            except:
572
 
                raise
573
 
            else:
574
 
                self.run()
575
 
                
576
 
 
577
 
        def add_ingredient (self):
578
 
            key=raw_input('Enter ingredient key: ')
579
 
            amt = convert.frac_to_float(raw_input('Enter amount: '))
580
 
            unit = raw_input('Enter unit: ')
581
 
            if not self.ings:
582
 
                self.ings = NutritionInfoList([self.nd.get_nutinfo_for_item(key,amt,unit)])
583
 
            else:
584
 
                self.ings = self.ings + self.nd.get_nutinfo_for_item(key,amt,unit)
585
 
 
586
 
        def add_key (self):
587
 
            key=raw_input('Enter key for which we add info: ')
588
 
            matches = self.nd.get_matches(key,10)
589
 
            for n,m in enumerate(matches):
590
 
                print n,'. ',m[0]
591
 
            choice = None
592
 
            while not choice:
593
 
                choice = raw_input('Enter number of choice: ')
594
 
                choice = int(choice)
595
 
                if choice < len(matches): choice = matches[choice][1]
596
 
                else: choice = None
597
 
            self.nd.set_key_from_ndbno(key,choice)
598
 
            self.ings._reset()
599
 
 
600
 
        def print_info (self):
601
 
            att = raw_input('What information would you like (e.g. kcal): ')
602
 
            while not hasattr(self.ings,att):
603
 
                print "I'm sorry, there is no information about ",att
604
 
                att = raw_input('What information would you like (e.g. kcal): ')
605
 
            print att,":",getattr(self.ings,att)
606
 
            vv = self.ings._get_vapor()
607
 
            if vv:
608
 
                print '(but we have some vapor)'
609
 
                for v in vv:
610
 
                    explanation = v._wheres_the_vapor()
611
 
                    print 'Vapor for ',v.__key__
612
 
                    if explanation==KEY_VAPOR: print 'No key'
613
 
                    if explanation==UNIT_VAPOR: print "Can't handle unit ",v.__unit__
614
 
                    if explanation==AMOUNT_VAPOR: print "What am I to do with the amount ",v.__amt__
615
 
                
616
 
 
617
 
        def exit (self):
618
 
            import sys
619
 
            sys.exit()
620
 
    si = SimpleInterface(nd)
621
 
    si.run()
622
 
    #import random
623
 
    #fake_key = "0"
624
 
    #while raw_input('Get another density?'):
625
 
    #    row=random.choice(db.nutrition_table)
626
 
    #    print 'Information: ',row.desc, nd.get_conversions(row=row)
627
 
    #    #print 'Gramweights: ',nd.get_gramweights(row)
628
 
    #    #print 'Density of ',row.desc,' = ',nd.get_densities(row)