Added branded food DB
This commit is contained in:
parent
532c9372ea
commit
2e86208476
@ -54,6 +54,7 @@
|
|||||||
|
|
||||||
valueAttribute: String,
|
valueAttribute: String,
|
||||||
labelAttribute: String,
|
labelAttribute: String,
|
||||||
|
keyAttribute: String,
|
||||||
|
|
||||||
onGetOptions: Function,
|
onGetOptions: Function,
|
||||||
searchOptions: Array
|
searchOptions: Array
|
||||||
@ -107,7 +108,9 @@
|
|||||||
},
|
},
|
||||||
|
|
||||||
optionKey(opt) {
|
optionKey(opt) {
|
||||||
if (this.valueAttribute) {
|
if (this.keyAttribute) {
|
||||||
|
return opt[this.keyAttribute]
|
||||||
|
} else if (this.valueAttribute) {
|
||||||
return opt[this.valueAttribute];
|
return opt[this.valueAttribute];
|
||||||
} else {
|
} else {
|
||||||
return opt.toString();
|
return opt.toString();
|
||||||
@ -115,7 +118,11 @@
|
|||||||
},
|
},
|
||||||
|
|
||||||
optionValue(opt) {
|
optionValue(opt) {
|
||||||
return this.optionKey(opt);
|
if (this.valueAttribute) {
|
||||||
|
return opt[this.valueAttribute];
|
||||||
|
} else {
|
||||||
|
return opt.toString();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
optionLabel(opt) {
|
optionLabel(opt) {
|
||||||
|
@ -25,6 +25,7 @@
|
|||||||
:minLength="2"
|
:minLength="2"
|
||||||
valueAttribute="name"
|
valueAttribute="name"
|
||||||
labelAttribute="description"
|
labelAttribute="description"
|
||||||
|
key-attribute="ndbn"
|
||||||
placeholder=""
|
placeholder=""
|
||||||
@optionSelected="searchItemSelected"
|
@optionSelected="searchItemSelected"
|
||||||
:onGetOptions="updateSearchItems"
|
:onGetOptions="updateSearchItems"
|
||||||
@ -138,6 +139,7 @@
|
|||||||
<script>
|
<script>
|
||||||
|
|
||||||
import api from "../lib/Api";
|
import api from "../lib/Api";
|
||||||
|
import { mapState } from "vuex";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: {
|
props: {
|
||||||
@ -159,38 +161,15 @@
|
|||||||
|
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
nutrients: {
|
|
||||||
kcal: { label: "Calories", unit: "kcal" },
|
|
||||||
protein: { label: "Protein", unit: "g" },
|
|
||||||
lipids: { label: "Fat", unit: "g" },
|
|
||||||
carbohydrates: { label: "Carbohydrates", unit: "g" },
|
|
||||||
water: { label: "Water", unit: "g" },
|
|
||||||
sugar: { label: "Sugar", unit: "g" },
|
|
||||||
fiber: { label: "Fiber", unit: "g" },
|
|
||||||
cholesterol: { label: "Cholesterol", unit: "mg" },
|
|
||||||
sodium: { label: "Sodium", unit: "mg" },
|
|
||||||
calcium: { label: "Calcium", unit: "mg" },
|
|
||||||
iron: { label: "Iron", unit: "mg" },
|
|
||||||
magnesium: { label: "Magnesium", unit: "mg" },
|
|
||||||
phosphorus: { label: "Phosphorus", unit: "mg" },
|
|
||||||
potassium: { label: "Potassium", unit: "mg" },
|
|
||||||
zinc: { label: "Zinc", unit: "mg" },
|
|
||||||
copper: { label: "Copper", unit: "mg" },
|
|
||||||
manganese: { label: "Manganese", unit: "mg" },
|
|
||||||
vit_a: { label: "Vitamin A", unit: "μg" },
|
|
||||||
vit_b6: { label: "Vitamin B6", unit: "mg" },
|
|
||||||
vit_b12: { label: "Vitamin B12", unit: "μg" },
|
|
||||||
vit_c: { label: "Vitamin C", unit: "mg" },
|
|
||||||
vit_d: { label: "Vitamin D", unit: "μg" },
|
|
||||||
vit_e: { label: "Vitamin E", unit: "mg" },
|
|
||||||
vit_k: { label: "Vitamin K", unit: "μg" },
|
|
||||||
ash: { label: "ash", unit: "g" }
|
|
||||||
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
computed: {
|
computed: {
|
||||||
|
...mapState({
|
||||||
|
nutrients: 'nutrientList'
|
||||||
|
}),
|
||||||
|
|
||||||
visibleFoodUnits() {
|
visibleFoodUnits() {
|
||||||
return this.food.food_units.filter(iu => iu._destroy !== true);
|
return this.food.food_units.filter(iu => iu._destroy !== true);
|
||||||
},
|
},
|
||||||
|
@ -4,18 +4,71 @@
|
|||||||
{{food.name}}
|
{{food.name}}
|
||||||
</h3>
|
</h3>
|
||||||
|
|
||||||
|
<div class="message" v-if="food.ndbn">
|
||||||
|
<div class="message-header">
|
||||||
|
<span>USDA NDBN #{{ food.ndbn }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="message-body">
|
||||||
|
<a :href="'https://ndb.nal.usda.gov/ndb/foods/show/' + food.ndbn">USDA DB Entry</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="message">
|
||||||
|
<div class="message-header">
|
||||||
|
Custom Units
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="message-body">
|
||||||
|
<ul>
|
||||||
|
<li v-for="fu in food.food_units" :key="fu.id">
|
||||||
|
{{fu.name}}: {{fu.gram_weight}} grams
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="message">
|
||||||
|
<div class="message-header">
|
||||||
|
Nutrition per 100 grams
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="message-body">
|
||||||
|
<div class="columns is-mobile is-multiline">
|
||||||
|
<div v-for="(nutrient, name) in nutrients" :key="name" class="column is-half-mobile is-one-third-tablet">
|
||||||
|
<label class="label is-small-mobile">{{nutrient.label}}</label>
|
||||||
|
<div class="field has-addons">
|
||||||
|
<div class="control is-expanded">
|
||||||
|
<input type="text" class="input is-small-mobile" disabled="true" v-model="food[name]">
|
||||||
|
</div>
|
||||||
|
<div class="control">
|
||||||
|
<button type="button" tabindex="-1" class="unit-label button is-static is-small-mobile">{{nutrient.unit}}</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
|
||||||
|
import { mapState } from "vuex";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
props: {
|
props: {
|
||||||
food: {
|
food: {
|
||||||
required: true,
|
required: true,
|
||||||
type: Object
|
type: Object
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
...mapState({
|
||||||
|
nutrients: 'nutrientList'
|
||||||
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,6 +29,35 @@ export default new Vuex.Store({
|
|||||||
widescreen: false,
|
widescreen: false,
|
||||||
widescreenOnly: false,
|
widescreenOnly: false,
|
||||||
fullhd: false
|
fullhd: false
|
||||||
|
},
|
||||||
|
|
||||||
|
nutrientList: {
|
||||||
|
kcal: { label: "Calories", unit: "kcal" },
|
||||||
|
protein: { label: "Protein", unit: "g" },
|
||||||
|
lipids: { label: "Fat", unit: "g" },
|
||||||
|
carbohydrates: { label: "Carbohydrates", unit: "g" },
|
||||||
|
water: { label: "Water", unit: "g" },
|
||||||
|
sugar: { label: "Sugar", unit: "g" },
|
||||||
|
fiber: { label: "Fiber", unit: "g" },
|
||||||
|
cholesterol: { label: "Cholesterol", unit: "mg" },
|
||||||
|
sodium: { label: "Sodium", unit: "mg" },
|
||||||
|
calcium: { label: "Calcium", unit: "mg" },
|
||||||
|
iron: { label: "Iron", unit: "mg" },
|
||||||
|
magnesium: { label: "Magnesium", unit: "mg" },
|
||||||
|
phosphorus: { label: "Phosphorus", unit: "mg" },
|
||||||
|
potassium: { label: "Potassium", unit: "mg" },
|
||||||
|
zinc: { label: "Zinc", unit: "mg" },
|
||||||
|
copper: { label: "Copper", unit: "mg" },
|
||||||
|
manganese: { label: "Manganese", unit: "mg" },
|
||||||
|
vit_a: { label: "Vitamin A", unit: "μg" },
|
||||||
|
vit_b6: { label: "Vitamin B6", unit: "mg" },
|
||||||
|
vit_b12: { label: "Vitamin B12", unit: "μg" },
|
||||||
|
vit_c: { label: "Vitamin C", unit: "mg" },
|
||||||
|
vit_d: { label: "Vitamin D", unit: "μg" },
|
||||||
|
vit_e: { label: "Vitamin E", unit: "mg" },
|
||||||
|
vit_k: { label: "Vitamin K", unit: "μg" },
|
||||||
|
ash: { label: "ash", unit: "g" }
|
||||||
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
getters: {
|
getters: {
|
||||||
|
17
db/migrate/20180913204958_update_usda_foods.rb
Normal file
17
db/migrate/20180913204958_update_usda_foods.rb
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
class UpdateUsdaFoods < ActiveRecord::Migration[5.2]
|
||||||
|
|
||||||
|
class UsdaFoodMigrate < ActiveRecord::Base
|
||||||
|
self.table_name = :usda_foods
|
||||||
|
end
|
||||||
|
|
||||||
|
def change
|
||||||
|
add_column :usda_foods, :source, :string, index: true
|
||||||
|
add_column :usda_foods, :manufacturer, :string
|
||||||
|
add_column :usda_foods, :ingredients, :text
|
||||||
|
add_column :usda_foods, :nutrient_unit, :string
|
||||||
|
|
||||||
|
UsdaFoodMigrate.reset_column_information
|
||||||
|
|
||||||
|
UsdaFoodMigrate.update_all(nutrient_unit: '100 grams')
|
||||||
|
end
|
||||||
|
end
|
@ -10,7 +10,7 @@
|
|||||||
#
|
#
|
||||||
# It's strongly recommended that you check this file into your version control system.
|
# It's strongly recommended that you check this file into your version control system.
|
||||||
|
|
||||||
ActiveRecord::Schema.define(version: 2018_09_12_172758) do
|
ActiveRecord::Schema.define(version: 2018_09_13_204958) do
|
||||||
|
|
||||||
create_table "food_units", force: :cascade do |t|
|
create_table "food_units", force: :cascade do |t|
|
||||||
t.integer "food_id", null: false
|
t.integer "food_id", null: false
|
||||||
@ -194,6 +194,10 @@ ActiveRecord::Schema.define(version: 2018_09_12_172758) do
|
|||||||
t.decimal "vit_d", precision: 10, scale: 1
|
t.decimal "vit_d", precision: 10, scale: 1
|
||||||
t.decimal "vit_k", precision: 10, scale: 1
|
t.decimal "vit_k", precision: 10, scale: 1
|
||||||
t.decimal "cholesterol", precision: 10, scale: 3
|
t.decimal "cholesterol", precision: 10, scale: 3
|
||||||
|
t.string "source"
|
||||||
|
t.string "manufacturer"
|
||||||
|
t.text "ingredients"
|
||||||
|
t.string "nutrient_unit"
|
||||||
t.index ["long_description"], name: "index_usda_foods_on_long_description"
|
t.index ["long_description"], name: "index_usda_foods_on_long_description"
|
||||||
t.index ["ndbn"], name: "index_usda_foods_on_ndbn"
|
t.index ["ndbn"], name: "index_usda_foods_on_ndbn"
|
||||||
end
|
end
|
||||||
|
@ -1,5 +1,33 @@
|
|||||||
namespace :usda do
|
namespace :usda do
|
||||||
|
|
||||||
|
task :list_branded_nutrients do
|
||||||
|
require 'csv'
|
||||||
|
|
||||||
|
NutrientCollector = Struct.new(:name, :total_count, :unit_frequencies) do
|
||||||
|
def record(name, unit)
|
||||||
|
self.name = name
|
||||||
|
self.total_count = (self.total_count || 0) + 1
|
||||||
|
self.unit_frequencies ||= Hash.new { |h, k| h[k] = 0 }
|
||||||
|
self.unit_frequencies[unit] += 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
totals = Hash.new { |h, k| h[k] = NutrientCollector.new() }
|
||||||
|
|
||||||
|
f = CSV.open(Rails.root.join('vendor', 'data', 'usda_branded', 'Nutrients.csv'), 'r:iso-8859-1:utf-8')
|
||||||
|
f.each do |row|
|
||||||
|
code = row[1]
|
||||||
|
name = row[2]
|
||||||
|
unit = row[5]
|
||||||
|
totals[code].record(name, unit)
|
||||||
|
end
|
||||||
|
|
||||||
|
totals.each do |k, v|
|
||||||
|
puts "#{k}, #{v.name}: #{v.total_count} #{v.unit_frequencies.map { |k,v| "#{k}: #{v}" }.join('; ')}"
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
desc 'Empties usda_foods table, imports all data, and then updates any linked ingredients'
|
desc 'Empties usda_foods table, imports all data, and then updates any linked ingredients'
|
||||||
task import: :environment do
|
task import: :environment do
|
||||||
require 'usda_importer'
|
require 'usda_importer'
|
||||||
|
@ -45,7 +45,7 @@ module UnitConversion
|
|||||||
def compatible?(unit_str)
|
def compatible?(unit_str)
|
||||||
begin
|
begin
|
||||||
unitwise.compatible_with? Unitwise(1, unit_str)
|
unitwise.compatible_with? Unitwise(1, unit_str)
|
||||||
rescue TypeError, Unitwise::ConversionError, Unitwise::ExpressionError
|
rescue UnknownUnitError, TypeError, Unitwise::ConversionError, Unitwise::ExpressionError
|
||||||
false
|
false
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -2,12 +2,137 @@ require 'csv'
|
|||||||
|
|
||||||
class UsdaImporter
|
class UsdaImporter
|
||||||
|
|
||||||
|
BRANDED_NUTRIENTS = {
|
||||||
|
208 => {
|
||||||
|
col: 'kcal',
|
||||||
|
unit: 'kcal'
|
||||||
|
},
|
||||||
|
|
||||||
|
203 => {
|
||||||
|
col: 'protein',
|
||||||
|
unit: 'g'
|
||||||
|
},
|
||||||
|
|
||||||
|
255 => {
|
||||||
|
col: 'water',
|
||||||
|
unit: 'g'
|
||||||
|
},
|
||||||
|
|
||||||
|
204 => {
|
||||||
|
col: 'lipid',
|
||||||
|
unit: 'g'
|
||||||
|
},
|
||||||
|
|
||||||
|
207 => {
|
||||||
|
col: 'ash',
|
||||||
|
unit: 'g'
|
||||||
|
},
|
||||||
|
|
||||||
|
205 => {
|
||||||
|
col: 'carbohydrates',
|
||||||
|
unit: 'g'
|
||||||
|
},
|
||||||
|
|
||||||
|
291 => {
|
||||||
|
col: 'fiber',
|
||||||
|
unit: 'g'
|
||||||
|
},
|
||||||
|
|
||||||
|
269 => {
|
||||||
|
col: 'sugar',
|
||||||
|
unit: 'g'
|
||||||
|
},
|
||||||
|
|
||||||
|
301 => {
|
||||||
|
col: 'calcium',
|
||||||
|
unit: 'mg'
|
||||||
|
},
|
||||||
|
|
||||||
|
303 => {
|
||||||
|
col: 'iron',
|
||||||
|
unit: 'mg'
|
||||||
|
},
|
||||||
|
|
||||||
|
304 => {
|
||||||
|
col: 'magnesium',
|
||||||
|
unit: 'mg'
|
||||||
|
},
|
||||||
|
|
||||||
|
305 => {
|
||||||
|
col: 'phosphorus',
|
||||||
|
unit: 'mg'
|
||||||
|
},
|
||||||
|
|
||||||
|
306 => {
|
||||||
|
col: 'potassium',
|
||||||
|
unit: 'mg'
|
||||||
|
},
|
||||||
|
|
||||||
|
307 => {
|
||||||
|
col: 'sodium',
|
||||||
|
unit: 'mg'
|
||||||
|
},
|
||||||
|
|
||||||
|
309 => {
|
||||||
|
col: 'zinc',
|
||||||
|
unit: 'mg'
|
||||||
|
},
|
||||||
|
|
||||||
|
312 => {
|
||||||
|
col: 'copper',
|
||||||
|
unit: 'mg'
|
||||||
|
},
|
||||||
|
|
||||||
|
315 => {
|
||||||
|
col: 'manganese',
|
||||||
|
unit: 'mg'
|
||||||
|
},
|
||||||
|
|
||||||
|
401 => {
|
||||||
|
col: 'vit_c',
|
||||||
|
unit: 'mg'
|
||||||
|
},
|
||||||
|
|
||||||
|
415 => {
|
||||||
|
col: 'vit_b6',
|
||||||
|
unit: 'mg'
|
||||||
|
},
|
||||||
|
|
||||||
|
418 => {
|
||||||
|
col: 'vit_b12',
|
||||||
|
unit: 'mcg'
|
||||||
|
},
|
||||||
|
|
||||||
|
318 => {
|
||||||
|
col: 'vit_a',
|
||||||
|
unit: 'IU',
|
||||||
|
convert: ->(x) { x.to_f * 0.3 }
|
||||||
|
},
|
||||||
|
|
||||||
|
324 => {
|
||||||
|
col: 'vit_d',
|
||||||
|
unit: 'IU',
|
||||||
|
convert: ->(x) { x.to_f / 40.0 }
|
||||||
|
},
|
||||||
|
|
||||||
|
430 => {
|
||||||
|
col: 'vit_k',
|
||||||
|
unit: 'mcg'
|
||||||
|
},
|
||||||
|
|
||||||
|
601 => {
|
||||||
|
col: 'cholesterol',
|
||||||
|
unit: 'mg'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
FILES = {
|
FILES = {
|
||||||
abbreviated: {
|
abbreviated: {
|
||||||
filename: 'ABBREV.txt',
|
filename: 'ABBREV.txt',
|
||||||
|
key_column: 'NDB_No',
|
||||||
columns: [
|
columns: [
|
||||||
'NDB_No',
|
'NDB_No',
|
||||||
'Shrt_Desc',
|
'Shrirt_Desc',
|
||||||
'Water',
|
'Water',
|
||||||
'Energ_Kcal',
|
'Energ_Kcal',
|
||||||
'Protein',
|
'Protein',
|
||||||
@ -93,11 +218,16 @@ class UsdaImporter
|
|||||||
vit_d: 'Vit_D_mcg',
|
vit_d: 'Vit_D_mcg',
|
||||||
vit_k: 'Vit_K',
|
vit_k: 'Vit_K',
|
||||||
cholesterol: 'Cholestrl'
|
cholesterol: 'Cholestrl'
|
||||||
|
},
|
||||||
|
static: {
|
||||||
|
source: 'sr',
|
||||||
|
nutrient_unit: '100 grams'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
food_data: {
|
food_data: {
|
||||||
filename: 'FOOD_DES.txt',
|
filename: 'FOOD_DES.txt',
|
||||||
|
key_column: 'NDB_No',
|
||||||
columns: [
|
columns: [
|
||||||
'NDB_No',
|
'NDB_No',
|
||||||
'FdGrp_Cd',
|
'FdGrp_Cd',
|
||||||
@ -123,6 +253,7 @@ class UsdaImporter
|
|||||||
|
|
||||||
weights: {
|
weights: {
|
||||||
filename: 'WEIGHT.txt',
|
filename: 'WEIGHT.txt',
|
||||||
|
key_column: 'NDB_No',
|
||||||
map_into: 'usda_food_weights',
|
map_into: 'usda_food_weights',
|
||||||
columns: [
|
columns: [
|
||||||
'NDB_No',
|
'NDB_No',
|
||||||
@ -138,6 +269,73 @@ class UsdaImporter
|
|||||||
description: 'Msre_Desc',
|
description: 'Msre_Desc',
|
||||||
gram_weight: 'Gm_Wgt'
|
gram_weight: 'Gm_Wgt'
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
#Branded Food DB files
|
||||||
|
products: {
|
||||||
|
filename: 'branded/Products.csv',
|
||||||
|
key_column: 'NDB_Number',
|
||||||
|
csv: true,
|
||||||
|
columns: [
|
||||||
|
"NDB_Number",
|
||||||
|
"long_name",
|
||||||
|
"data_source",
|
||||||
|
"gtin_upc",
|
||||||
|
"manufacturer",
|
||||||
|
"date_modified",
|
||||||
|
"date_available",
|
||||||
|
"ingredients_english"
|
||||||
|
],
|
||||||
|
map: {
|
||||||
|
ndbn: 'NDB_Number',
|
||||||
|
long_description: 'long_name',
|
||||||
|
manufacturer: 'manufacturer',
|
||||||
|
ingredients: 'ingredients_english'
|
||||||
|
},
|
||||||
|
static: {
|
||||||
|
source: 'bf',
|
||||||
|
nutrient_unit: '100 g'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
nutrients: {
|
||||||
|
filename: 'branded/Nutrients.csv',
|
||||||
|
key_column: 'NDB_No',
|
||||||
|
csv: true,
|
||||||
|
columns: [
|
||||||
|
"NDB_No",
|
||||||
|
"Nutrient_Code",
|
||||||
|
"Nutrient_name",
|
||||||
|
"Derivation_Code",
|
||||||
|
"Output_value",
|
||||||
|
"Output_uom"
|
||||||
|
],
|
||||||
|
map_function: ->(obj, row) do
|
||||||
|
map = BRANDED_NUTRIENTS[row['Nutrient_Code'].to_i]
|
||||||
|
if map && map[:unit] == row['Output_uom']
|
||||||
|
obj.send("#{map[:col]}=".to_sym, row['Output_value'])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
},
|
||||||
|
|
||||||
|
serving_sizes: {
|
||||||
|
filename: 'branded/Serving_size.csv',
|
||||||
|
key_column: 'NDB_No',
|
||||||
|
csv: true,
|
||||||
|
map_into: 'usda_food_weights',
|
||||||
|
columns: [
|
||||||
|
"NDB_No",
|
||||||
|
"Serving_Size",
|
||||||
|
"Serving_Size_UOM",
|
||||||
|
"Household_Serving_Size",
|
||||||
|
"Household_Serving_Size_UOM",
|
||||||
|
"Preparation_State"
|
||||||
|
],
|
||||||
|
map: {
|
||||||
|
amount: 'Household_Serving_Size',
|
||||||
|
description: 'Household_Serving_Size_UOM',
|
||||||
|
gram_weight: 'Serving_Size'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -157,11 +355,16 @@ class UsdaImporter
|
|||||||
FILES.each do |name, data|
|
FILES.each do |name, data|
|
||||||
filename = File.join(@directory, data[:filename])
|
filename = File.join(@directory, data[:filename])
|
||||||
sorted_filename = "#{filename}.sorted"
|
sorted_filename = "#{filename}.sorted"
|
||||||
idx = data[:columns].index('NDB_No')
|
idx = data[:columns].index(data[:key_column])
|
||||||
|
|
||||||
if idx
|
if idx
|
||||||
idx += 1
|
idx += 1
|
||||||
`sort -n -t'^' -k#{idx}.2,#{idx}.6 #{filename} > #{sorted_filename}`
|
if data[:csv]
|
||||||
|
`head -n 1 #{filename} > #{sorted_filename}`
|
||||||
|
`tail -n +2 #{filename} | sort -n -t',' -k#{idx} - >> #{sorted_filename}`
|
||||||
|
else
|
||||||
|
`sort -n -t'^' -k#{idx}.2,#{idx}.6 #{filename} > #{sorted_filename}`
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
sorted_files[name] = sorted_filename
|
sorted_files[name] = sorted_filename
|
||||||
@ -171,7 +374,7 @@ class UsdaImporter
|
|||||||
|
|
||||||
sorted_files.each do |name, filename|
|
sorted_files.each do |name, filename|
|
||||||
data = FILES[name]
|
data = FILES[name]
|
||||||
opened_files[name] = CSV.open(filename, 'r:iso-8859-1:utf-8', csv_options(data[:columns]))
|
opened_files[name] = CSV.open(filename, 'r:iso-8859-1:utf-8', csv_options(data))
|
||||||
end
|
end
|
||||||
|
|
||||||
build_enumerator(opened_files).each_slice(500) do |slice|
|
build_enumerator(opened_files).each_slice(500) do |slice|
|
||||||
@ -189,8 +392,18 @@ class UsdaImporter
|
|||||||
obj = food.send(file_info[:map_into]).build
|
obj = food.send(file_info[:map_into]).build
|
||||||
end
|
end
|
||||||
|
|
||||||
file_info[:map].each do |db, col|
|
if file_info[:static]
|
||||||
obj.send("#{db}=", row[col])
|
file_info[:static].each do |k, v|
|
||||||
|
obj.send("#{k}=", v)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if file_info[:map_function]
|
||||||
|
file_info[:map_function].call(obj, row)
|
||||||
|
else
|
||||||
|
file_info[:map].each do |db, col|
|
||||||
|
obj.send("#{db}=", row[col])
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -217,11 +430,13 @@ class UsdaImporter
|
|||||||
def build_enumerator(opened_files)
|
def build_enumerator(opened_files)
|
||||||
enumerate_data = {}
|
enumerate_data = {}
|
||||||
opened_files.each do |name, csv|
|
opened_files.each do |name, csv|
|
||||||
|
file_data = FILES[name]
|
||||||
csv_enumerator = csv.each
|
csv_enumerator = csv.each
|
||||||
enumerate_data[name] = {
|
enumerate_data[name] = {
|
||||||
enumerator: csv_enumerator,
|
enumerator: csv_enumerator,
|
||||||
done: false,
|
done: false,
|
||||||
next_ndbn: csv_enumerator.peek['NDB_No']
|
next_ndbn: csv_enumerator.peek[file_data[:key_column]],
|
||||||
|
peek_next_ndbn: -> { csv_enumerator.peek[file_data[:key_column]] }
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -229,7 +444,7 @@ class UsdaImporter
|
|||||||
loop do
|
loop do
|
||||||
break if enumerate_data.values.all? { |d| d[:done] }
|
break if enumerate_data.values.all? { |d| d[:done] }
|
||||||
|
|
||||||
current_ndbn = enumerate_data.values.map { |d| d[:next_ndbn] }.min
|
current_ndbn = enumerate_data.select { |_, d| !d[:done] }.values.map { |d| d[:next_ndbn] }.min
|
||||||
results = Hash.new { |hash, key| hash[key] = [] }
|
results = Hash.new { |hash, key| hash[key] = [] }
|
||||||
|
|
||||||
enumerate_data.each do |name, data|
|
enumerate_data.each do |name, data|
|
||||||
@ -237,7 +452,7 @@ class UsdaImporter
|
|||||||
begin
|
begin
|
||||||
while data[:next_ndbn] == current_ndbn
|
while data[:next_ndbn] == current_ndbn
|
||||||
results[name] << data[:enumerator].next
|
results[name] << data[:enumerator].next
|
||||||
data[:next_ndbn] = data[:enumerator].peek['NDB_No']
|
data[:next_ndbn] = data[:peek_next_ndbn].call
|
||||||
end
|
end
|
||||||
rescue StopIteration
|
rescue StopIteration
|
||||||
data[:done] = true
|
data[:done] = true
|
||||||
@ -252,8 +467,12 @@ class UsdaImporter
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def csv_options(headers)
|
def csv_options(data)
|
||||||
{ col_sep: '^', quote_char: '~', headers: headers }
|
if data[:csv]
|
||||||
|
{ headers: true }
|
||||||
|
else
|
||||||
|
{ col_sep: '^', quote_char: '~', headers: data[:columns] }
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
@ -7,10 +7,15 @@ RSpec.describe UsdaImporter do
|
|||||||
i = UsdaImporter.new(Rails.root.join('spec', 'test_data'))
|
i = UsdaImporter.new(Rails.root.join('spec', 'test_data'))
|
||||||
i.import
|
i.import
|
||||||
|
|
||||||
expect(UsdaFood.count).to eq 2
|
expect(UsdaFood.count).to eq 5
|
||||||
butter = UsdaFood.where(ndbn: '01001').first
|
butter = UsdaFood.where(ndbn: '01001').first
|
||||||
expect(butter).not_to be_nil
|
expect(butter).not_to be_nil
|
||||||
expect(butter.usda_food_weights.count).to eq 4
|
expect(butter.usda_food_weights.count).to eq 4
|
||||||
|
|
||||||
|
clif_bar = UsdaFood.where(ndbn: '45042066').first
|
||||||
|
expect(clif_bar).not_to be_nil
|
||||||
|
expect(clif_bar.usda_food_weights.count).to eq 1
|
||||||
|
expect(clif_bar.kcal).to eq 368
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
55
spec/test_data/branded/Nutrients.csv
Normal file
55
spec/test_data/branded/Nutrients.csv
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
"NDB_No","Nutrient_Code","Nutrient_name","Derivation_Code","Output_value","Output_uom"
|
||||||
|
"45042066","203","Protein","LCCS","13.24","g"
|
||||||
|
"45042066","204","Total lipid (fat)","LCCS","7.35","g"
|
||||||
|
"45042066","205","Carbohydrate, by difference","LCCS","63.24","g"
|
||||||
|
"45042066","208","Energy","LCCS","368.0","kcal"
|
||||||
|
"45042066","269","Sugars, total","LCCS","32.35","g"
|
||||||
|
"45042066","284","Carbohydrate, other","LCCS","24.0","g"
|
||||||
|
"45042066","291","Fiber, total dietary","LCCS","7.4","g"
|
||||||
|
"45042066","301","Calcium, Ca","LCCD","294.0","mg"
|
||||||
|
"45042066","303","Iron, Fe","LCCD","3.97","mg"
|
||||||
|
"45042066","304","Magnesium, Mg","LCCD","118.0","mg"
|
||||||
|
"45042066","306","Potassium, K","LCCS","397.0","mg"
|
||||||
|
"45042066","307","Sodium, Na","LCCS","265.0","mg"
|
||||||
|
"45042066","318","Vitamin A, IU","LCCD","735.0","IU"
|
||||||
|
"45042066","324","Vitamin D","LCCD","88.0","IU"
|
||||||
|
"45042066","401","Vitamin C, total ascorbic acid","LCCD","8.8","mg"
|
||||||
|
"45042066","404","Thiamin","LCCD","0.0","mg"
|
||||||
|
"45042066","405","Riboflavin","LCCD","0.25","mg"
|
||||||
|
"45042066","406","Niacin","LCCD","2.941","mg"
|
||||||
|
"45042066","415","Vitamin B-6","LCCD","0.294","mg"
|
||||||
|
"45042066","418","Vitamin B-12","LCCD","0.88","mcg"
|
||||||
|
"45042066","601","Cholesterol","LCCD","0.0","mg"
|
||||||
|
"45042066","605","Fatty acids, total trans","LCCS","0.0","g"
|
||||||
|
"45042066","606","Fatty acids, total saturated","LCCS","2.21","g"
|
||||||
|
"45042066","645","Fatty acids, total monounsaturated","LCCS","2.21","g"
|
||||||
|
"45042066","646","Fatty acids, total polyunsaturated","LCCS","2.94","g"
|
||||||
|
"45001597","203","Protein","LCCS","7.14","g"
|
||||||
|
"45001597","204","Total lipid (fat)","LCCS","14.29","g"
|
||||||
|
"45001597","205","Carbohydrate, by difference","LCCS","21.43","g"
|
||||||
|
"45001597","208","Energy","LCCS","250.0","kcal"
|
||||||
|
"45001597","269","Sugars, total","LCSL","3.57","g"
|
||||||
|
"45001597","291","Fiber, total dietary","LCCS","3.6","g"
|
||||||
|
"45001597","301","Calcium, Ca","LCCD","0.0","mg"
|
||||||
|
"45001597","303","Iron, Fe","LCCD","2.57","mg"
|
||||||
|
"45001597","307","Sodium, Na","LCCS","339.0","mg"
|
||||||
|
"45001597","318","Vitamin A, IU","LCCD","0.0","IU"
|
||||||
|
"45001597","401","Vitamin C, total ascorbic acid","LCCD","4.3","mg"
|
||||||
|
"45001597","601","Cholesterol","LCCD","0.0","mg"
|
||||||
|
"45001597","605","Fatty acids, total trans","LCCS","0.0","g"
|
||||||
|
"45001597","606","Fatty acids, total saturated","LCCS","1.79","g"
|
||||||
|
"45371436","203","Protein","LCCS","0.0","g"
|
||||||
|
"45371436","204","Total lipid (fat)","LCCD","0.0","g"
|
||||||
|
"45371436","205","Carbohydrate, by difference","LCCS","36.11","g"
|
||||||
|
"45371436","208","Energy","LCCS","167.0","kcal"
|
||||||
|
"45371436","269","Sugars, total","LCCS","25.0","g"
|
||||||
|
"45371436","291","Fiber, total dietary","LCCD","0.0","g"
|
||||||
|
"45371436","301","Calcium, Ca","LCCD","56.0","mg"
|
||||||
|
"45371436","303","Iron, Fe","LCCD","3.0","mg"
|
||||||
|
"45371436","306","Potassium, K","LCCD","194.0","mg"
|
||||||
|
"45371436","307","Sodium, Na","LCCS","417.0","mg"
|
||||||
|
"45371436","324","Vitamin D","LCCD","0.0","IU"
|
||||||
|
"45371436","539","Sugars, added","LCCS","22.2","g"
|
||||||
|
"45371436","601","Cholesterol","LCCD","0.0","mg"
|
||||||
|
"45371436","605","Fatty acids, total trans","LCCS","0.0","g"
|
||||||
|
"45371436","606","Fatty acids, total saturated","LCCD","0.0","g"
|
|
4
spec/test_data/branded/Products.csv
Normal file
4
spec/test_data/branded/Products.csv
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
"NDB_Number","long_name","data_source","gtin_upc","manufacturer","date_modified","date_available","ingredients_english"
|
||||||
|
"45042066","CLIF BAR, ENERGY BAR, COOL MINT CHOCOLATE","LI","722252102003","Clif Bar and Company","2017-07-14 19:55:15","2017-07-14 19:55:15","ORGANIC BROWN RICE SYRUP, ORGANIC ROLLED OATS, ORGANIC CANE SYRUP, ORGANIC ROASTED SOYBEANS, SOY PROTEIN ISOLATE, RICE FLOUR, ALKALIZED COCOA, ORGANIC SOY FLOUR, ORGANIC FIG PASTE, ORGANIC OAT FIBER, ORGANIC OAT FLOUR, ORGANIC DRIED CANE SYRUP, ORGANIC SOYBEAN OIL, NATURAL FLAVORS, COCOA BUTTER, SUNFLOWER OIL, SALT, BARLEY MALT EXTRACT, SOY FLOUR, GREEN TEA EXTRACT (CONTAINS CAFFEINE), UNSWEETENED CHOCOLATE, SOY LECITHIN, BAKING SODA, ORGANIC VANILLA EXTRACT. VITAMINS & MINERALS: DICALCIUM PHOSPHATE, MAGNESIUM OXIDE, ASCORBIC ACID (VIT. C), DL-ALPHA TOCOPHERYL ACETATE (VIT. E), BETA CAROTENE (VIT. A), NIACINAMIDE (VIT. B3), ERGOCALCIFEROL (VIT. D2), THIAMINE MONONITRATE (VIT. B1), PYRIDOXINE HYDROCHLORIDE (VIT. B6), RIBOFLAVIN (VIT. B2), CYANOCOBALAMIN (VIT. B12)."
|
||||||
|
"45371436","CRAFT BBQ SAUCE","LI","011110882462","The Kroger Co.","2018-05-25 22:12:37","2018-05-25 22:12:37","WATER, BROWN SUGAR, GLUCOSE, APPLE CIDER VINEGAR, TOMATO PASTE, BEER (WATER, MALT [BARLEY, WHEAT], YEAST, HOPS), BLACKSTRAP MOLASSES, MODIFIED CORN STARCH, CANOLA OIL, SALT, LEMON JUICE CONCENTRATE, GREEN BELL PEPPERS, DRIED CHIPOTLE PEPPER, MUSTARD FLOUR, SPICE, DEHYDRATED ONION, DEHYDRATED GARLIC, SMOKE FLAVOR, NATURAL FLAVOR, XANTHAN GUM, SODIUM BENZOATE (PRESERVATIVE), CITRIC ACID, DRIED ANCHO PEPPER."
|
||||||
|
"45001597","ROASTED GARLIC HUMMUS","LI","085239233498","Target Stores","2018-03-11 02:27:03","2018-03-11 02:27:03","CHICKPEAS, WATER, SESAME TAHINI, GARLIC, CANOLA/OLIVE OIL BLEND, PIMIENTOS, SALT, CITRIC ACID, CAPERS, SPICES, VINEGAR, POTASSIUM SORBATE AND SODIUM BENZOATE (TO MAINTAIN FRESHNESS)."
|
|
4
spec/test_data/branded/Serving_size.csv
Normal file
4
spec/test_data/branded/Serving_size.csv
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
"NDB_No","Serving_Size","Serving_Size_UOM","Household_Serving_Size","Household_Serving_Size_UOM","Preparation_State"
|
||||||
|
"45001597","28.0","g","2.0","Tbsp",""
|
||||||
|
"45042066","68.0","g","1.0","BAR",""
|
||||||
|
"45371436","36.0","g","2.0","Tbsp",""
|
|
BIN
vendor/data/usda/branded/BFPD_Doc.pdf
vendored
Normal file
BIN
vendor/data/usda/branded/BFPD_Doc.pdf
vendored
Normal file
Binary file not shown.
9
vendor/data/usda/branded/Derivation_Code_Description.csv
vendored
Normal file
9
vendor/data/usda/branded/Derivation_Code_Description.csv
vendored
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
"LCCD","Calculated from a daily value percentage per serving size mea"
|
||||||
|
"LCCS","Calculated from value per serving size measure"
|
||||||
|
"LCGA","Given by information provider as an 'approximate' value per 100 unit measur"
|
||||||
|
"LCGE","Given by information provider as an 'exact' value per 100 unit measure"
|
||||||
|
"LCGL","Given by information provider as a 'less than' value per 100 unit measure"
|
||||||
|
"LCSA","Calculated from an 'approximate' value per serving size measure"
|
||||||
|
"LCSE","Calculated from an 'exact' value per serving size measure"
|
||||||
|
"LCSG","Calculated from a 'greater than' value per serving size measure"
|
||||||
|
"LCSL","Calculated from a 'less than' value per serving size measure"
|
|
3231489
vendor/data/usda/branded/Nutrients.csv
vendored
Normal file
3231489
vendor/data/usda/branded/Nutrients.csv
vendored
Normal file
File diff suppressed because it is too large
Load Diff
239090
vendor/data/usda/branded/Products.csv
vendored
Normal file
239090
vendor/data/usda/branded/Products.csv
vendored
Normal file
File diff suppressed because it is too large
Load Diff
237911
vendor/data/usda/branded/Serving_size.csv
vendored
Normal file
237911
vendor/data/usda/branded/Serving_size.csv
vendored
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user