Преглед на файлове

Fixed unit conversion between non-same-typed units.

Thomas Flucke преди 7 години
родител
ревизия
cb59676d34

+ 3 - 2
src/name/tflucke/ieat2/controllers/WeightController.java

@@ -33,8 +33,9 @@ public class WeightController extends AbstractController<Weight> {
     
     //@Override
     @GetMapping("/usda/weight/{ndbno}")
-    public List<Weight> list(@PathVariable("ndbno") final String ndbno) {
-        return db.find(Weight.class).field("ndb_no").equal(ndbno).asList();
+    public List<Weight> list(@PathVariable("ndbno") final int ndbno) {
+        return db.find(Weight.class).field("ndb_no")
+            .equal(String.format("%05d", ndbno)).asList();
     }
 
     @Override

+ 2 - 2
src/name/tflucke/ieat2/models/Food.java

@@ -9,12 +9,12 @@ public abstract class Food extends DBObject {
     public String name;
     //public Map<String, float> nutrients;
     @JsonProperty("unit_type")
-    public Unit.Type unitType;
+    public Unit.Type unitType = Unit.Type.mass;
     public Long calories_p_100;
     @JsonProperty("food_group")
     public String foodGroup;
     public boolean dry = unitType != Unit.Type.volume;
-    public float density = Float.NaN;
+    public float density = 0;
 
     public String getType() {
         return getClass().getSimpleName();

+ 1 - 0
src/name/tflucke/ieat2/models/Unit.java

@@ -39,6 +39,7 @@ public class Unit extends DBObject {
     public String symbol;
     public double conversion;
     public Type type;
+    public String[] aliases = {};
 
     /**
      * Converts a value measured in this unit to the primary unit for this type.

+ 1 - 0
web/WEB-INF/tags/template.tag

@@ -18,6 +18,7 @@
     <script type="text/javascript" src="static/angularjs/angular-resource.js"></script>
     <script type="text/javascript" src="static/angular-ui-bootstrap/ui-bootstrap.min.js"></script>
     <script type="text/javascript" src="static/angular-ui-bootstrap/ui-bootstrap-tpls.min.js"></script>
+    <script type="text/javascript" src="static/ieat-ui.js"></script>
     <script type="text/javascript" src="static/Food.js"></script>
     <jsp:invoke fragment="head"/>
   </head>

+ 32 - 1
web/js/basicFoodEditor.js

@@ -12,7 +12,8 @@
             food: '<'
         },
         controller: [
-            '$scope', 'NDBList', 'Unit', function($scope, NDBList, Unit) {
+            '$scope', '$q', 'NDBList', 'NDBWeight', 'Unit',
+            function($scope, $q, NDBList, NDBWeight, Unit) {
                 var self = this;
                 $scope.units_symbol = {};
                 Unit.primary(function(units) {
@@ -25,6 +26,36 @@
                         key: self.ndbKey,
                         type: "g"
                     });
+
+                    var getCanidateUnit = function(weight, units) {
+                        return units.find(function(u) {
+                            return u.type == "volume" &&
+                                (weight.msre_desc.localeCompare(u.symbol) == 0
+                                 || u.aliases.some(function(a) {
+                                     return weight.msre_desc.localeCompare(a) == 0;
+                                 }));
+                        });
+                    };
+                    
+                    if (!self.food.density) {
+                        $q.all([
+                            NDBWeight.query({ndbno: self.food.ndbno}).$promise,
+                            Unit.query().$promise
+                        ]).then(function(res) {
+                            for (var i = 0; i < res[0].length; i++) {
+                                var w = res[0][i];
+                                var unit = getCanidateUnit(w, res[1]);
+                                if (unit) {
+                                    self.food.density =
+                                        w.gm_wgt / (w.amount * unit.conversion);
+                                    return;
+                                }
+                            }
+                        }, function() {
+                            // TODO: Error handling
+                            console.err("Failed to get weight information.")
+                        });
+                    }
                     if (!self.food.unit_type) {
                         self.food.unit_type = "Mass";
                         self.food.dry = true;

+ 15 - 0
web/js/ieat-ui.js

@@ -0,0 +1,15 @@
+(function() {
+        try {return angular.module('ieat.ui')}
+        catch {return angular.module('ieat.ui', [])}}
+)().directive('myOnEnter', function () {
+    return function (scope, element, attrs) {
+        element.bind("keydown keypress", function (event) {
+            if(event.which === 13) {
+                scope.$apply(function (){
+                    scope.$eval(attrs.myOnEnter);
+                });
+                event.preventDefault();
+            }
+        });
+    };
+});

+ 15 - 3
web/js/ndbDatabase.js

@@ -11,14 +11,16 @@
             method: "GET",
             isArray: true,
             transformRequest: function(data, headers) {
-                return angular.extend({}, headers, {'Content-Type': 'application/json'});
+                return angular.extend({}, headers,
+                                      {'Content-Type': 'application/json'});
             },
             headers: {
                 'Content-Type': 'application/json'
             },
             transformResponse: function(data, headersGetter, status) {
                 var json = angular.fromJson(data);
-                return status < 400 && json.list? json.list.item : json.errors.error;
+                return status < 400 && json.list?
+                    json.list.item : json.errors.error;
             }
         }
     };
@@ -36,7 +38,8 @@
             },
             transformResponse: function(data, headersGetter, status) {
                 var json = angular.fromJson(data);
-                return status < 400 && json.report.food? json.report.food : json.errors.error;
+                return status < 400 && json.report.food?
+                    json.report.food : json.errors.error;
             }
         }
     };
@@ -104,5 +107,14 @@
         .factory('NDBNutrients', ['$resource', function($resource) {
             return $resource('https://api.nal.usda.gov/ndb/nutrients?'+
                              'api_key=:key&nbno=:nums&nutrients=:nutrients&fg=:groups&max=:max&offset=:offset&sort=:sort', {}, listMethod);
+        }])
+    /*
+     * Gets the weight information about a food.
+     * 
+     * @param ndbno Food id number
+     * @returns A list of matching the ndb item.
+     */
+        .factory('NDBWeight', ['$resource', function($resource) {
+            return $resource('usda/weight/:ndbno');
         }]);
 })();

+ 13 - 4
web/js/templates/basicFoodEditor.html

@@ -19,6 +19,8 @@
                  data-ng-model="$ctrl.food.ndbno"
                  readonly="readonly" />
         </td>
+      </tr>
+      <tr>
         <td>
           <label for="calories">
             Calories/100{{units_symbol[$ctrl.food.unit_type]}}:
@@ -28,6 +30,13 @@
                  type="number"
                  data-ng-model="$ctrl.food.calories_p_100" />
         </td>
+        <td>
+          <label for="density">Density (g/ml):</label>
+          <input id="density"
+                 class="form-control"
+                 type="number"
+                 data-ng-model="$ctrl.food.density" />
+        </td>
       </tr>
       <tr>
         <td>
@@ -35,10 +44,10 @@
           <select id="unit"
                   class="form-control"
                   data-ng-model="$ctrl.food.unit_type"
-                  data-ng-change="$ctrl.food.dry = $ctrl.food.unit_type!='Volume'">
-            <option>Mass</option>
-            <option>Volume</option>
-            <option>Count</option>
+                  data-ng-change="$ctrl.food.dry = $ctrl.food.unit_type!='volume'">
+            <option value="mass">Mass</option>
+            <option value="volume">Volume</option>
+            <option value="count">Count</option>
           </select>
         </td>
         <td>

+ 25 - 2
web/js/templates/unitEditor.html

@@ -29,11 +29,34 @@
       <tr>
         <td colspan="2">
           <label for="type">Type:</label>
-          <select id="type" class="form-control" data-ng-model="$ctrl.unit.type">
-            <option data-ng-repeat="type in unitTypes">{{type}}</option>
+          <select id="type" class="form-control"
+                  data-ng-model="$ctrl.unit.type"
+                  data-ng-options="type.toLowerCase() as type for type in unitTypes">
           </select>
         </td>
       </tr>
+      <tr>
+        <table class="table table-hover table-responsive">
+          <tr>
+            <td colspan="2">
+              <label for="newAlias">Aliases:</label>
+              <input id="newAlias" class="form-control" type="text"
+                     data-ng-model="newAlias"
+                     data-my-on-enter="addAlias(newAlias);" />
+            </td>
+          </tr>
+          <tr data-ng-repeat="alias in $ctrl.unit.aliases">
+            <td style="width: 100%; padding-left: 1em;">
+              <span>{{alias}}</span>
+            </td>
+            <td>
+              <button class="btn btn-danger" data-ng-click="deleteAlias($index)">
+                Delete
+              </button>
+            </td>
+          </tr>
+        </table>
+      </tr>
     </table>
   </div>
 </div>

+ 7 - 0
web/js/unitEditor.js

@@ -11,7 +11,14 @@
             unit: '<'
         },
         controller: ['$scope', function($scope) {
+            var self = this;
             $scope.unitTypes = ["Mass", "Volume", "Count"];
+            $scope.addAlias = function(alias) {
+                self.unit.aliases.push(alias);
+            };
+            $scope.deleteAlias = function(index) {
+                self.unit.aliases.splice(index, 1);
+            };
         }]
     });
 })();

+ 47 - 2
web/js/units.js

@@ -17,6 +17,38 @@
             return res;
         };
     };
+
+    function normalizeFromMass(amountG, food) {
+        switch (food.unit_type) {
+        case "volume":
+            return amountG / food.density;
+        case "count":
+            throw "Not yet implemented!";
+            //return amountG / food.mass_p_unit;
+        }
+    }
+
+    function normalizeFromVolume(amountMl, food) {
+        switch (food.unit_type) {
+        case "mass":
+            return amountMl * food.density;
+        case "count":
+            throw "Not yet implemented!";
+            //return amountMl * food.density / food.mass_p_unit;
+        }
+    }
+
+    function normalizeFromCount(amountUnit, food) {
+        switch (food.unit_type) {
+        case "mass":
+            throw "Not yet implemented!";
+            //return amountUnit * food.mass_p_unit;
+        case "volume":
+            throw "Not yet implemented!";
+            //return amountUnit * food.mass_p_unit / food.density;
+        }
+    }
+    
     angular.module('Units', ['ngResource'])
     /*
      * Collection of unit apis.
@@ -44,8 +76,21 @@
                     url: "unit/primary/:type"
                 }
             });
-            res.prototype.normalize = function(amount) {
-                return amount * this.conversion;
+            res.prototype.normalize = function(food, amount) {
+                var normalizedToType = amount * this.conversion;
+                if (food.unit_type == this.type) {
+                    return normalizedToType;
+                }
+                else {
+                    switch (this.type) {
+                    case "mass":
+                        return normalizeFromMass(normalizedToType, food);
+                    case "volume":
+                        return normalizeFromVolume(normalizedToType, food);
+                    case "count":
+                        return normalizeFromCount(normalizedToType, food);
+                    }
+                }
             };
             return res;
         }]);

+ 9 - 17
web/views/addRecipe.jsp

@@ -3,6 +3,7 @@
 <t:template>
   <jsp:attribute name="title">Add Recipe</jsp:attribute>
   <jsp:attribute name="head">
+    <script type="text/javascript" src="static/ieat-ui.js"></script>
     <script type="text/javascript" src="static/ndbDatabase.js"></script>
     <script type="text/javascript" src="static/basicFoodEditor.js"></script>
     <script type="text/javascript" src="static/Food.js"></script>
@@ -10,19 +11,8 @@
     <script type="text/javascript" src="static/searchBar.js"></script>
     <script type="text/javascript">
       var app = angular.module('recipe',
-                               ['ui.bootstrap', 'Food', 'ieat.ui.editors']);
-      app.directive('myOnEnter', function () {
-          return function (scope, element, attrs) {
-              element.bind("keydown keypress", function (event) {
-                  if(event.which === 13) {
-                      scope.$apply(function (){
-                          scope.$eval(attrs.myOnEnter);
-                      });
-                      event.preventDefault();
-                  }
-              });
-          };
-      });
+                               ['ui.bootstrap', 'Food', 'ieat.ui',
+                                'ieat.ui.editors']);
       app.controller('SearchController',
                      ['$scope', '$uibModal', 'Food', 'Recipe', 'Unit',
          function($scope, $uibModal, Food, Recipe, Unit) {
@@ -44,7 +34,7 @@
              var newIngredientCtrl = [
                  '$scope', '$uibModalInstance', 'food', 'units',
                  function($scope, $uibModalInstance, food, units) {
-                     $scope.food = food.name;
+                     $scope.food = food;
                      $scope.units = units;
                      $scope.unit = primeUnits[food.unit_type];
                      $scope.submit = $uibModalInstance.close;
@@ -112,7 +102,7 @@
           <td style="width: 50%;" colspan="2">
             <ul>
               <li data-ng-repeat="ingredient in recipe.ingredients">
-                {{::ingredient.amount}}
+                {{::ingredient.amount | number:0}}
                 {{primeUnits[ingredient.item.unit_type].symbol}}
                 {{::ingredient.item.name}}
                 <span class="glyphicon glyphicon-remove"
@@ -149,7 +139,7 @@
       </table>
       <script type="text/ng-template" id="newIngredient">
         <div style="width: 100%; text-align: center; padding: 0 1em 1em 0;">
-          <span style="font-weight: bold;">{{::food}}: </span>
+          <span style="font-weight: bold;">{{::food.name}}: </span>
           <input type="number" min="0" required="required"
                  data-ng-model="amount" />
           <select required="required" data-ng-model="unit"
@@ -157,7 +147,9 @@
           </select>
           <button type="button"
                   class="btn btn-success"
-                  data-ng-click="submit(unit.normalize(amount));">Submit</button>
+                  data-ng-click="submit(unit.normalize(food, amount));">
+            Submit
+          </button>
           <button type="button" class="btn" data-ng-click="dismiss();">
             Cancel
           </button>

+ 1 - 1
web/views/units.jsp

@@ -35,7 +35,7 @@
                       unit.$save(function(resp) {
                           loadUnitList(
                               $scope.types.find(function(x) {
-                                  return x.name == resp.type
+                                  return x.name.localeCompare(resp.type);
                               })
                           );
                       }, function(err) {