~vorlon/ubuntu/saucy/gourmet/trunk

« back to all changes in this revision

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

  • Committer: Bazaar Package Importer
  • Author(s): Rolf Leggewie
  • Date: 2008-07-26 13:29:41 UTC
  • Revision ID: james.westby@ubuntu.com-20080726132941-6ldd73qmacrzz0bn
Tags: upstream-0.14.0
ImportĀ upstreamĀ versionĀ 0.14.0

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_by_criteria(
 
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):
 
99
        """A convenience function that grabs the requisite items from
 
100
        an ingredient."""
 
101
        if hasattr(ing,'refid') and ing.refid:
 
102
            subrec = rd.get_referenced_rec(ing)
 
103
            return self.get_nutinfo_for_inglist(rd.get_ings(subrec),rd,ingObject=ing)
 
104
        if hasattr(ing,'rangeamount') and ing.rangeamount:
 
105
            # just average our amounts
 
106
            amount = (ing.rangeamount + ing.amount)/2
 
107
        else:
 
108
            amount = ing.amount
 
109
        if not amount: amount=1
 
110
        return  self.get_nutinfo_for_item(ing.ingkey,amount,ing.unit,ingObject=ing)
 
111
 
 
112
    def get_nutinfo_for_inglist (self, inglist, rd, ingObject=None):
 
113
        """A convenience function to get NutritionInfoList for a list of
 
114
        ingredients.
 
115
        """
 
116
        return NutritionInfoList([self.get_nutinfo_for_ing(i,rd) for i in inglist],
 
117
                                 ingObject=ingObject)
 
118
 
 
119
    def get_nutinfo_for_item (self, key, amt, unit, ingObject=None):
 
120
        """Handed a key, amount and unit, get out nutritional Database object.
 
121
        """
 
122
        ni=self.get_nutinfo(key)
 
123
        if not amt:
 
124
            amt = 1
 
125
        if ni:
 
126
            c=self.get_conversion_for_amt(amt,unit,key)
 
127
            if c:
 
128
                return NutritionInfo(ni,mult=c,ingObject=ingObject)
 
129
        return NutritionVapor(self,key,
 
130
                              rowref=ni,
 
131
                              amount=amt,
 
132
                              unit=unit,
 
133
                              ingObject=ingObject)
 
134
 
 
135
    def get_nutinfo (self, key):
 
136
        """Get our nutritional information for ingredient key 'key'
 
137
        We return an object interfacing with our DB whose attributes
 
138
        will be nutritional values.
 
139
        """
 
140
        aliasrow = self._get_key(key)
 
141
        if aliasrow:
 
142
            nvrow=self.db.fetch_one(self.db.nutrition_table,**{'ndbno':aliasrow.ndbno})
 
143
            if nvrow: return NutritionInfo(nvrow)
 
144
        # if we don't have a nutritional db row, return a
 
145
        # NutritionVapor instance which remembers our query and allows
 
146
        # us to redo it.  The idea here is that our callers will get
 
147
        # an object that can guarantee them the latest nutritional
 
148
        # information for a given item.
 
149
        return NutritionVapor(self,key)
 
150
 
 
151
    def get_ndbno (self, key):
 
152
        aliasrow = self._get_key(key)
 
153
        if aliasrow: return aliasrow.ndbno
 
154
        else: return None
 
155
 
 
156
    def convert_to_grams (self, amt, unit, key, row=None):
 
157
        conv = self.get_conversion_for_amt(amt,unit,key,row)
 
158
        if conv: return conv*100
 
159
        else:
 
160
            return None
 
161
 
 
162
    def get_conversion_for_amt (self, amt, unit, key, row=None, fudge=True):
 
163
        """Get a conversion for amount amt of unit 'unit' to USDA standard.
 
164
 
 
165
        Multiplying our standard numbers (/100g) will get us the appropriate
 
166
        calories, etc.
 
167
 
 
168
        get_conversion_for_amt(amt,unit,key) * 100 will give us the
 
169
        number of grams this AMOUNT converts to.
 
170
        """
 
171
        # our default is 100g
 
172
        cnv=self.conv.converter('g',unit)
 
173
        if not row: row=self.get_nutinfo(key)
 
174
        if not cnv:
 
175
            cnv = self.conv.converter('g',unit,
 
176
                                      density=self.get_density(key,row,fudge=fudge)
 
177
                                      )
 
178
        if not cnv:
 
179
            # Check our weights tables...
 
180
            extra_conversions = self.get_conversions(key,row)[1]
 
181
            if extra_conversions.has_key(unit):
 
182
                cnv = extra_conversions[unit]
 
183
            elif unit and extra_conversions.has_key(unit.lower()):
 
184
                cnv = extra_conversions[unit.lower()]
 
185
        if not cnv:
 
186
            # lookup in our custom nutrition-related conversion table
 
187
            if self.conv.unit_dict.has_key(unit):
 
188
                unit = self.conv.unit_dict[unit]
 
189
            elif not unit:
 
190
                unit = ''
 
191
            lookup = self.db.fetch_one(self.db.nutritionconversions_table,ingkey=key,unit=unit)
 
192
            if lookup:
 
193
                cnv = lookup.factor
 
194
            else:
 
195
                # otherwise, cycle through any units we have and see
 
196
                # if we can get a conversion via those units...
 
197
                for conv in self.db.fetch_all(self.db.nutritionconversions_table,ingkey=key):
 
198
                    factor = self.conv.converter(unit,conv.unit)
 
199
                    if factor:
 
200
                        cnv = conv.factor*factor
 
201
        if cnv:
 
202
            return (0.01*amt)/cnv
 
203
 
 
204
    def get_conversions (self, key=None, row=None):
 
205
        """Handed an ingredient key or a row of the nutrition database,
 
206
        we return two dictionaries, one with Unit Conversions and the other
 
207
        with densities. Our return dictionaries look like this:
 
208
        ({'chopped':1.03, #density dic
 
209
          'melted':1.09},
 
210
         {'piece':27,
 
211
          'leg':48,} # unit : grams
 
212
          )"""
 
213
        if not row: row=self.get_nutinfo(key)
 
214
        if not row: return {},{}
 
215
        units = {}
 
216
        densities = {}
 
217
        for gd,gw in self.get_gramweights(row).items():
 
218
            a,u,e=gd
 
219
            if a:
 
220
                convfactor = self.conv.converter(u,'ml')
 
221
                if convfactor: #if we are a volume
 
222
                    # divide mass by volume converted to mililiters
 
223
                    # (since gramwts are in grams!)
 
224
                    density = float(gw) / (a * convfactor)
 
225
                    densities[e]=density
 
226
                    continue
 
227
            # if we can't get a density from this amount, we're going to treat it as a unit!
 
228
            if e: u = u + ", " + e
 
229
            if a: gw = float(gw)/a
 
230
            else:
 
231
                gw = float(gw)
 
232
            if u: units[u]=gw
 
233
        return densities,units
 
234
            
 
235
    def get_densities (self,key=None,row=None):
 
236
        """Handed key or nutrow, return dictionary with densities."""
 
237
        if not row: row = self._get_key(key)
 
238
        if not row: return {}
 
239
        if self.conv.density_table.has_key(key):
 
240
            return {'':self.conv.density_table[key]}
 
241
        else:
 
242
            densities = {}       
 
243
            for gd,gw in self.get_gramweights(row).items():
 
244
                a,u,e = gd
 
245
                if not a:
 
246
                    continue
 
247
                convfactor=self.conv.converter(u,'ml')
 
248
                if convfactor: # if we are a volume
 
249
                    # divide mass by volume converted to milileters
 
250
                    # (gramwts are in grams)
 
251
                    density = float(gw) / (a * convfactor)
 
252
                    densities[e]=density
 
253
            return densities
 
254
 
 
255
    def get_gramweights (self,row):
 
256
        """Return a dictionary with gram weights.
 
257
        """
 
258
        ret = {}
 
259
        nutweights = self.db.fetch_all(self.db.usda_weights_table,**{'ndbno':row.ndbno})
 
260
        for nw in nutweights:
 
261
            mtch = self.wght_breaker.match(nw.unit)
 
262
            if not mtch:
 
263
                unit = nw.unit
 
264
                extra = None
 
265
            else:
 
266
                unit = mtch.groups()[0]
 
267
                extra = mtch.groups()[2]
 
268
            ret[(nw.amount,unit,extra)]=nw.gramwt
 
269
        return ret
 
270
    
 
271
    def get_density (self,key=None,row=None, fudge=True):
 
272
        densities = self.get_densities(key,row)
 
273
        if densities.has_key(''): densities[None]=densities['']
 
274
        if key: keyrow=self._get_key(key)        
 
275
        if densities:
 
276
            if key and keyrow.density_equivalent and densities.has_key(keyrow.density_equivalent):
 
277
                return densities[keyrow.density_equivalent]
 
278
            elif densities.has_key(None):
 
279
                self.conv.density_table[key]=densities[None]
 
280
                return densities[None]
 
281
            elif len(densities)==1:
 
282
                return densities.values()[0]
 
283
            elif fudge:
 
284
                return sum(densities.values())/len(densities)
 
285
            else:
 
286
                return None
 
287
 
 
288
    def parse_gramweight_measure (self, txt):
 
289
        m=self.gramwght_regexp.match(txt)
 
290
        if m:
 
291
            groups=m.groups()
 
292
            amt = groups[0]
 
293
            if amt: amt = float(amt)
 
294
            unit = groups[2]
 
295
            extra = groups[4]
 
296
            return amt,unit,extra
 
297
 
 
298
    def add_custom_nutrition_info (self, nutrition_dictionary):
 
299
        """Add custom nutritional information."""
 
300
        #new_ndbno = self.db.increment_field(self.db.nutrition_table,'ndbno')
 
301
        #if new_ndbno: nutrition_dictionary['ndbno']=new_ndbno
 
302
        return self.db.do_add_nutrition(nutrition_dictionary).ndbno
 
303
        
 
304
                    
 
305
class NutritionInfo:
 
306
    """A multipliable way to reference an object.
 
307
 
 
308
    Any attribute of object that can be mutiplied, will be returned
 
309
    multiplied by mult.
 
310
 
 
311
    We can also support various mathematical operators
 
312
    n = NutritionInfo(obj, mult=2)
 
313
    n * 2 -> NutritionInfo(obj,mult=4)
 
314
    n2 = NutritionInfo(obj2, mult=3)
 
315
    n2 + n -> NutritionInfoList([n2,n])
 
316
 
 
317
    The result is that addition and multiplication 'makes sense' for
 
318
    properties. For example, if we have nutrition info for 1 carrot,
 
319
    we can multiply it or add it to the nutrition info for an
 
320
    eggplant. The resulting object will reflect the appropriate
 
321
    cumulative values.
 
322
 
 
323
    Carrot = NutritionInfo(CarrotNutritionRow)
 
324
    Eggplant = NutritionInfo(EggplantNutritionRow)
 
325
 
 
326
    Carrot.kcal => 41
 
327
    Eggplant.kcal => 24
 
328
    (Carrot + Eggplant).kcal => 65
 
329
    (Carrot * 3 + Eggplant).kcal => 147
 
330
 
 
331
    This will be true for all numeric properties.
 
332
 
 
333
    Non numeric properties return a somewhat not-useful string:
 
334
    
 
335
    (Carrot + Eggplant).desc => 'CARROTS,RAW, EGGPLANT,RAW'
 
336
    """
 
337
    def __init__ (self,rowref, mult=1, fudged=False, ingObject=None):
 
338
        self.__rowref__ = rowref
 
339
        self.__mult__ = mult
 
340
        self.__fudged__ = fudged
 
341
        self.__ingobject__ = ingObject
 
342
 
 
343
    def __getattr__ (self, attr):
 
344
        if attr[0]!='_':
 
345
            ret = getattr(self.__rowref__, attr)
 
346
            try:
 
347
                if attr in SUMMABLE_FIELDS:
 
348
                    return (ret or 0) * self.__mult__
 
349
                else:
 
350
                    return ret
 
351
            except:
 
352
                raise
 
353
        else:
 
354
            # somehow this magically gets us standard
 
355
            # attribute handling...
 
356
            raise AttributeError, attr
 
357
 
 
358
    def __add__ (self, obj):
 
359
        if isinstance(obj,NutritionInfo):
 
360
            return NutritionInfoList([self,obj])
 
361
        elif isinstance(obj,NutritionInfoList):
 
362
            return NutritionInfoList([self]+obj.__nutinfos__)
 
363
 
 
364
    def __mul__ (self, n):
 
365
        return NutritionInfo(self.__rowref__, mult=self.__mult__ * n,
 
366
                             fudged=self.__fudged__,ingObject=self.__ingobject__)
 
367
 
 
368
KEY_VAPOR = 0 # when we don't have a key
 
369
UNIT_VAPOR = 1 # when we can't interpret the unit
 
370
DENSITY_VAPOR = 2 # when we don't have a density
 
371
AMOUNT_VAPOR = 3 # when there is no amount, leaving us quite confused
 
372
 
 
373
class NutritionVapor (NutritionInfo):
 
374
    """An object to hold our nutritional information before we know it.
 
375
 
 
376
    Basically, we have to behave like a NutritionInfo class that doesn't
 
377
    actually return any data.
 
378
 
 
379
    We also can return information about why we're still vapor
 
380
    (whether we need density info, key info or what...).
 
381
    """
 
382
    def __init__ (self, nd, key,
 
383
                  rowref=None,
 
384
                  mult=None,
 
385
                  amount=None,
 
386
                  unit=None,
 
387
                  ingObject=None):
 
388
        self.__nd__ = nd
 
389
        self.__rowref__ = rowref
 
390
        self.__key__ = key
 
391
        self.__mult__ = mult
 
392
        self.__amt__ = amount
 
393
        self.__unit__ = unit
 
394
        self.__ingobject__ = ingObject
 
395
 
 
396
    def _reset (self):
 
397
        """Try to create matter from vapor and return it.
 
398
 
 
399
        If we fail we return more vapor."""
 
400
        if not self.__rowref__:
 
401
            if self.__mult__:
 
402
                ni = self.__nd__.get_nutinfo(self.__key__)
 
403
                if not isinstance(ni,NutritionVapor): return ni * self.__mult__
 
404
                else: return self
 
405
            else:
 
406
                return self.__nd__.get_nutinfo_for_item(self.__key__,
 
407
                                                        self.__amt__,
 
408
                                                        self.__unit__,
 
409
                                                        ingObject=self.__ingobject__
 
410
                                                        )
 
411
        elif self.__amt__:
 
412
            c=self.__nd__.get_conversion_for_amt(self.__amt__,self.__unit__,self.__key__,fudge=False)
 
413
            if c:
 
414
                self.__mult__ = c
 
415
                return NutritionInfo(self.__rowref__,
 
416
                                     self.__mult__)
 
417
            else:
 
418
                c=self.__nd__.get_conversion_for_amt(self.__amt__,self.__unit__,self.__key__,fudge=True)
 
419
                if c:
 
420
                    self.__mult__ = c
 
421
                    return NutritionInfo(self.__rowref__,
 
422
                                         self.__mult__,
 
423
                                         ingObject=self.__ingobject__
 
424
                                         )
 
425
                else:
 
426
                    return self
 
427
        else: return self.__nd__.get_nutinfo_for_item(self.__key__,self.__amt__,self.__unit__,ingObject=self.__ingobject__)
 
428
 
 
429
    def __getattr__ (self,attr):
 
430
        """Return 0 for any requests for a non _ prefixed attribute."""
 
431
        if attr[0]!='_':
 
432
            return 0
 
433
        else:
 
434
            raise AttributeError,attr
 
435
 
 
436
    def __repr__ (self):
 
437
        return '<NutritionVapor %s>'%self.__key__
 
438
    
 
439
    def __nonzero__ (self):
 
440
        """Vapor is always False."""
 
441
        return False
 
442
 
 
443
    def _wheres_the_vapor (self):
 
444
        """Return a key as to why we're vapor."""
 
445
        if not self.__rowref__: return KEY_VAPOR
 
446
        elif not self.__amt__: return AMOUNT_VAPOR
 
447
        else: return UNIT_VAPOR
 
448
    
 
449
class NutritionInfoList (list, NutritionInfo):
 
450
    """A summable list of objects.
 
451
 
 
452
    When we ask for numeric attributes of our members, we get the sum.
 
453
    """
 
454
    def __init__ (self,nutinfos, mult=1,ingObject=None):
 
455
        self.__nutinfos__ = nutinfos
 
456
        #self.__len__ = self.__nutinfos__.__len__
 
457
        #self.__getitem__ = self.__nutinfos__.__getitem__
 
458
        self.__mult__ = 1
 
459
        self.__ingobject__ = ingObject
 
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 __iter__ (self):
 
524
        for i in self.__nutinfos__: yield i
 
525
 
 
526
    def recursive_length (self):
 
527
        """Return number of contained nutrition info objects, recursing any embedded lists.
 
528
        """
 
529
        n = 0
 
530
        for x in range(len(self)):
 
531
            obj = self[x]
 
532
            if isinstance(obj,NutritionInfoList):
 
533
                n += obj.recursive_length()
 
534
            else:
 
535
                n += 1
 
536
        return n
 
537
            
 
538
if __name__ == '__main__':
 
539
    import sys
 
540
    sys.path.append('/usr/share/')
 
541
    import gourmet.recipeManager as rm
 
542
    db=rm.RecipeManager(**rm.dbargs)
 
543
    import gourmet.convert
 
544
    conv = gourmet.convert.converter()
 
545
    import nutritionGrabberGui
 
546
    nutritionGrabberGui.check_for_db(db)
 
547
    nd=NutritionData(db,conv)
 
548
 
 
549
def foo ():
 
550
    from gourmet import convert
 
551
    class SimpleInterface:
 
552
        
 
553
        def __init__ (self, nd):
 
554
            self.ACTIONS = {'Add ingredient':self.add_ingredient,
 
555
                       'Add key info':self.add_key,
 
556
                       'Print info':self.print_info,
 
557
                       'Exit' : self.exit
 
558
                       }
 
559
            self.nd = nd
 
560
            self.ings = []
 
561
 
 
562
        def run (self):
 
563
            choices = self.ACTIONS.keys()
 
564
            for n,a in enumerate(choices):
 
565
                print n,a
 
566
            choice = None
 
567
            while not choice:
 
568
                choice = raw_input('Enter number of choice: ')
 
569
                choice = int(choice)
 
570
                if choice < len(choices): choice = self.ACTIONS[choices[choice]]
 
571
                else: choice = None
 
572
            try:
 
573
                choice()
 
574
            except:
 
575
                raise
 
576
            else:
 
577
                self.run()
 
578
                
 
579
 
 
580
        def add_ingredient (self):
 
581
            key=raw_input('Enter ingredient key: ')
 
582
            amt = convert.frac_to_float(raw_input('Enter amount: '))
 
583
            unit = raw_input('Enter unit: ')
 
584
            if not self.ings:
 
585
                self.ings = NutritionInfoList([self.nd.get_nutinfo_for_item(key,amt,unit)])
 
586
            else:
 
587
                self.ings = self.ings + self.nd.get_nutinfo_for_item(key,amt,unit)
 
588
 
 
589
        def add_key (self):
 
590
            key=raw_input('Enter key for which we add info: ')
 
591
            matches = self.nd.get_matches(key,10)
 
592
            for n,m in enumerate(matches):
 
593
                print n,'. ',m[0]
 
594
            choice = None
 
595
            while not choice:
 
596
                choice = raw_input('Enter number of choice: ')
 
597
                choice = int(choice)
 
598
                if choice < len(matches): choice = matches[choice][1]
 
599
                else: choice = None
 
600
            self.nd.set_key_from_ndbno(key,choice)
 
601
            self.ings._reset()
 
602
 
 
603
        def print_info (self):
 
604
            att = raw_input('What information would you like (e.g. kcal): ')
 
605
            while not hasattr(self.ings,att):
 
606
                print "I'm sorry, there is no information about ",att
 
607
                att = raw_input('What information would you like (e.g. kcal): ')
 
608
            print att,":",getattr(self.ings,att)
 
609
            vv = self.ings._get_vapor()
 
610
            if vv:
 
611
                print '(but we have some vapor)'
 
612
                for v in vv:
 
613
                    explanation = v._wheres_the_vapor()
 
614
                    print 'Vapor for ',v.__key__
 
615
                    if explanation==KEY_VAPOR: print 'No key'
 
616
                    if explanation==UNIT_VAPOR: print "Can't handle unit ",v.__unit__
 
617
                    if explanation==AMOUNT_VAPOR: print "What am I to do with the amount ",v.__amt__
 
618
                
 
619
 
 
620
        def exit (self):
 
621
            import sys
 
622
            sys.exit()
 
623
    si = SimpleInterface(nd)
 
624
    si.run()
 
625
    #import random
 
626
    #fake_key = "0"
 
627
    #while raw_input('Get another density?'):
 
628
    #    row=random.choice(db.nutrition_table)
 
629
    #    print 'Information: ',row.desc, nd.get_conversions(row=row)
 
630
    #    #print 'Gramweights: ',nd.get_gramweights(row)
 
631
    #    #print 'Density of ',row.desc,' = ',nd.get_densities(row)