dietary_dsl 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,184 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DietaryDsl
4
+ # Módulo que agrupa las constantes y funciones útiles respecto a la base de datos BEDCA
5
+ module BEDCA
6
+ ATTRIBUTES = {
7
+ # Identificador
8
+ # 12
9
+ 'f_id' => :id,
10
+ # Nombre en español
11
+ # Arroz
12
+ 'f_ori_name' => :nombre,
13
+ # Nombre en inglés
14
+ # Rice
15
+ 'f_eng_name' => :name,
16
+ # Nombre científico
17
+ # Oryza sativa
18
+ 'sci_name' => :scientific_name,
19
+ # A0696 A0730 A0814 A0960 A1240 A1290 B1322 C0155 E0131 F0003 G0003 H0003 H0398 J0003 K0004 M0128 N0036 P0024...
20
+ 'langual' => :langual,
21
+ # Código en la base de datos EFSA, http://iufost.org/databases, https://www.efsa.europa.eu/en/efsajournal/pub/1970
22
+ # A001D
23
+ 'foodexcode' => :foodexcode,
24
+ # 11
25
+ 'mainlevelcode' => :mainlevelcode,
26
+ # Código de primer nivel de FoodEx
27
+ # A000J
28
+ 'codlevel1' => :codlevel1,
29
+ # Nombre del primer nivel de FoodEx
30
+ # Grains and grain-based products
31
+ 'namelevel1' => :namelevel1,
32
+ # 29
33
+ 'codsublevel' => :codsublevel,
34
+ # Código de segundo nivel de FoodEx
35
+ # A000K
36
+ 'codlevel2' => :codlevel2,
37
+ # Nombre del primer nivel de FoodEx
38
+ # Grains and grain-based products
39
+ 'namelevel2' => :namelevel2,
40
+ # ???
41
+ # null
42
+ 'f_des_esp' => :f_des_esp,
43
+ # ???
44
+ # null
45
+ 'f_des_ing' => :f_des_ing,
46
+ # ???
47
+ # URL de la imagen
48
+ 'photo' => :photo,
49
+ # ???
50
+ # null
51
+ 'edible_portion' => :edible_portion,
52
+ # Origen de la información
53
+ # BEDCA2
54
+ 'f_origen' => :f_origen,
55
+ # Valores nutricionales del alimento
56
+ # Array de componentes
57
+ 'foodvalue' => :foodvalue,
58
+
59
+ # =======================================================================
60
+ # Components
61
+ # =======================================================================
62
+ # Identificador
63
+ # 53
64
+ 'c_id' => :id,
65
+ # Nombre en español
66
+ # carbohidratos
67
+ 'c_ori_name' => :nombre,
68
+ # Nombre en inglés
69
+ # carbohydrate
70
+ 'c_eng_name' => :name,
71
+ # ???
72
+ # CHO
73
+ 'eur_name' => :eur_name,
74
+ # ???
75
+ # 2
76
+ 'componentgroup_id' => :componentgroup_id,
77
+ # Glosario del componente en español
78
+ # Este componente es uno de losmacronutrientes e incluye todos los hidratos de carbono
79
+ 'glos_esp' => :glos_esp,
80
+ # Glosario del componente en español
81
+ # null
82
+ 'glos_ing' => :glos_ing,
83
+ # Descripción del grupo de componentes en español
84
+ # Hidratos de carbono
85
+ 'cg_descripcion' => :categoria,
86
+ # Descripción del grupo de componentes en inglés
87
+ # Carbohydrate components
88
+ 'cg_description' => :cg_description,
89
+ # Cantidad de materia que contiene el elemento consultado
90
+ # 86
91
+ 'best_location' => :cantidad,
92
+ # Unidad de medida
93
+ # g
94
+ 'v_unit' => :unidad,
95
+ # ???
96
+ # W
97
+ 'moex' => :moex,
98
+ # ???
99
+ # null
100
+ 'stdv' => :stdv,
101
+ # ???
102
+ # null
103
+ 'min' => :min,
104
+ # ???
105
+ # null
106
+ 'max' => :max,
107
+ # ???
108
+ # null
109
+ 'v_n' => :v_n,
110
+ # Identificador de unidad de medición
111
+ # g
112
+ 'u_id' => :u_id,
113
+ # Descripción de la unidad de medición en español
114
+ # gramos
115
+ 'u_descripcion' => :u_descripcion,
116
+ # Descripción de la unidad de medición en inglés
117
+ # gram
118
+ 'u_description' => :u_description,
119
+ # ???
120
+ # AR
121
+ 'value_type' => :value_type,
122
+ # Descripción del tipo de valor en español
123
+ # como aparece en la fuente
124
+ 'vt_descripcion' => :vt_descripcion,
125
+ # Descripción del tipo de valor en inglés
126
+ # as reported
127
+ 'vt_description' => :vt_description,
128
+ # ???
129
+ # W
130
+ 'mu_id' => :mu_id,
131
+ # ???
132
+ # por 100 g de porción comestible
133
+ 'mu_descripcion' => :mu_descripcion,
134
+ # ???
135
+ # per 100 g edible portion
136
+ 'mu_description' => :mu_description,
137
+ # ???
138
+ # 258
139
+ 'ref_id' => :ref_id,
140
+ # ???
141
+ # null
142
+ 'citation' => :citation,
143
+ # ???
144
+ # tabla de composición de alimentos
145
+ 'at_descripcion' => :at_descripcion,
146
+ # ???
147
+ # food composition table
148
+ 'at_description' => :at_description,
149
+ # ???
150
+ # libro
151
+ 'pt_descripcion' => :pt_descripcion,
152
+ # ???
153
+ # book
154
+ 'pt_description' => :pt_description,
155
+ # ???
156
+ # 344
157
+ 'method_id' => :method_id,
158
+ # ???
159
+ # tipo de método no conocido
160
+ 'mt_descripcion' => :mt_descripcion,
161
+ # ???
162
+ # method type not known
163
+ 'mt_description' => :mt_description,
164
+ # ???
165
+ # Extraccion: Alicuotas del alimento (entre 5 y 30 g) dependiendo del color son...
166
+ 'm_descripcion' => :m_descripcion,
167
+ # ???
168
+ # null
169
+ 'm_description' => :m_description,
170
+ # ???
171
+ # Método no conocido (Moreiras)
172
+ 'm_nom_esp' => :m_nom_esp,
173
+ # ???
174
+ # Unknown method (Moreiras)
175
+ 'm_nom_ing' => :m_nom_ing,
176
+ # ???
177
+ # Metodo analitico o de cálculo no conocido
178
+ 'mhd_descripcion' => :mhd_descripcion,
179
+ # ???
180
+ # Analytical or calculation method not known
181
+ 'mhd_description' => :mhd_description
182
+ }.freeze
183
+ end
184
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DietaryDsl
4
+ # Módulo que agrupa las constantes y funciones útiles respecto a la base de datos BEDCA
5
+ module BEDCA
6
+ CATEGORIES = {
7
+ 'Hidratos de Carbono' => :hidratos_de_carbono,
8
+ 'Vitaminas' => :vitaminas,
9
+ 'Grasas' => :grasas,
10
+ 'Minerales' => :minerales,
11
+ 'Proximales' => :proximales
12
+ }.freeze
13
+ end
14
+ end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DietaryDsl
4
+ # Módulo que agrupa las constantes y funciones útiles respecto a la base de datos BEDCA
5
+ module BEDCA
6
+ COMPONENTS = {
7
+ '53' => :carbohidratos,
8
+ '67' => :carotenoides_totales,
9
+ '88' => :retinol,
10
+ '100' => :vitamina_a,
11
+ '102' => :vitamina_d,
12
+ '103' => :viamina_e,
13
+ '123' => :acido_graso_miristico,
14
+ '136' => :acido_graso_palmitico,
15
+ '157' => :acido_graso_estearico,
16
+ '172' => :acido_graso_oleico,
17
+ '189' => :acido_graso,
18
+ '199' => :acido_graso_alt,
19
+ '230' => :acido_graso_araquidonico,
20
+ '231' => :acido_graso_eicosapentaenoico,
21
+ '260' => :acido_graso_docosahexaenoico,
22
+ '282' => :acidos_grasos_monoinsaturados_totales,
23
+ '287' => :acidos_grasos_poliinsaturados_totales,
24
+ '299' => :acidos_grasos_saturados_totales,
25
+ '307' => :fibra_dietetica_total,
26
+ '317' => :calcio,
27
+ '319' => :hierro_total,
28
+ '321' => :potasio,
29
+ '322' => :magnesio,
30
+ '323' => :sodio,
31
+ '326' => :fosforo,
32
+ '404' => :alcohol,
33
+ '409' => :energia_total,
34
+ '410' => :grasa_total,
35
+ '416' => :proteina_total,
36
+ '417' => :agua,
37
+ '433' => :colesterol,
38
+ '456' => :ioduro,
39
+ '462' => :selenio_total,
40
+ '464' => :zinc,
41
+ '472' => :folato,
42
+ '475' => :equivalentes_de_niacina_totales,
43
+ '482' => :riboflavina,
44
+ '483' => :tiamina,
45
+ '484' => :vitamina_b_12,
46
+ '485' => :vitamina_b_6,
47
+ '486' => :vitamina_c,
48
+ '487' => :acido_folico,
49
+ 'alcohol (etanol)' => :alcohol,
50
+ 'grasa, total (lipidos totales)' => :grasa_total,
51
+ 'proteina, total' => :proteina_total,
52
+ 'carbohidratos' => :carbohidratos
53
+ }.freeze
54
+ end
55
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DietaryDsl
4
+ # Módulo que agrupa las constantes y funciones útiles respecto a la base de datos BEDCA
5
+ module BEDCA
6
+ RELATIONS = {
7
+ equal: 'EQUAL',
8
+ like: 'LIKE'
9
+ }.freeze
10
+ end
11
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DietaryDsl
4
+ # Módulo que agrupa las constantes y funciones útiles respecto a la base de datos BEDCA
5
+ module BEDCA
6
+ XML_HEADER = '<?xml version="1.0" encoding="UTF-8"?>'
7
+ URL = URI 'http://www.bedca.net/bdpub/procquery.php'
8
+ SVG_REGEX = %r{<text.+?>(?<value>.+?)</\s?text>.+?<text.+?>(?<label>.+?)</\s?text>}
9
+
10
+ def rehash(data, map = ATTRIBUTES)
11
+ data.each_with_object({}) do |(key, value), hash|
12
+ hash[map[key]] = value
13
+ hash
14
+ end
15
+ end
16
+
17
+ def arr_to_hash(data, map)
18
+ data.each_with_object({}) do |item, hash|
19
+ a = yield item
20
+ hash[map[a]] = item
21
+ hash
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'dietary_dsl/bedca_api/constants/attributes'
4
+ require 'dietary_dsl/bedca_api/constants/components'
5
+ require 'dietary_dsl/bedca_api/constants/relations'
6
+ require 'dietary_dsl/bedca_api/constants/categories'
7
+ require 'dietary_dsl/bedca_api/constants/utils'
@@ -0,0 +1,145 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'uri'
4
+ require 'net/http'
5
+ require 'builder'
6
+ require 'active_support/all'
7
+ require 'dietary_dsl/bedca_api/constants'
8
+ require 'dietary_dsl/bedca_api/food_value'
9
+ require 'dietary_dsl/bedca_api/food_values'
10
+ require 'pp'
11
+
12
+ module DietaryDsl
13
+ # Class that wraps Bedca database API
14
+ class Food
15
+ attr_reader :raw, :nombre, :name, :values
16
+
17
+ include BEDCA
18
+
19
+ @http = Net::HTTP.new(URL.host, URL.port)
20
+
21
+ class << self
22
+ attr_accessor :http
23
+ end
24
+
25
+ def self.request(xml)
26
+ response = @http.request_post URL, XML_HEADER + xml, 'Content-Type' => 'text/xml'
27
+ body = response.read_body
28
+ return Hash.from_xml body
29
+ rescue
30
+ raise 'Hay un problema con la conexión'
31
+ end
32
+
33
+ def self.sanitize_response(response)
34
+ response.dig 'foodresponse', 'food'
35
+ end
36
+
37
+ def self.build_attributes(b, attributes)
38
+ attributes
39
+ .reject { |(key)| key == 'foodvalue' }
40
+ .each do |(key)|
41
+ b.atribute name: key
42
+ end
43
+ end
44
+
45
+ def self.query(attributes = ATTRIBUTES)
46
+ Builder::XmlMarkup.new.foodquery do |b|
47
+ b.type level: 2
48
+ b.selection do
49
+ build_attributes(b, attributes)
50
+ end
51
+ yield b
52
+ end
53
+ end
54
+
55
+ def self.where(b, field, relation, value)
56
+ b.condition do
57
+ b.cond1 do
58
+ b.atribute1(name: ATTRIBUTES.key(field))
59
+ end
60
+ b.relation(type: relation)
61
+ b.cond3(value)
62
+ end
63
+ end
64
+
65
+ def self.find_by_and_relation(data, relation)
66
+ xml = query do |b|
67
+ data.each do |(key), value|
68
+ where(b, key, relation, value)
69
+ end
70
+ end
71
+
72
+ data = sanitize_response request xml
73
+ return nil if data.nil?
74
+
75
+ Food.new(data)
76
+ end
77
+
78
+ def self.find(id)
79
+ find_by(id: id)
80
+ end
81
+
82
+ def self.find_by(data)
83
+ find_by_and_relation(data, RELATIONS[:equal])
84
+ end
85
+
86
+ def self.find_by_like(data)
87
+ find_by_and_relation(data, RELATIONS[:like])
88
+ end
89
+
90
+ def initialize(values)
91
+ @data = rehash(values)
92
+ @groups = @data[:foodvalue]
93
+ .group_by { |value| value['cg_descripcion'] }
94
+ .each_with_object({}) do |(key, vals), hash|
95
+ hash[CATEGORIES[key]] = FoodValues.new(vals.first['cg_descripcion'], vals)
96
+ end
97
+
98
+ @data.delete(:foodvalue)
99
+ @data[:groups] = @groups
100
+
101
+ @general_values = download_general_values
102
+ end
103
+
104
+ def [](key)
105
+ @data[key]
106
+ end
107
+
108
+ def download_general_values
109
+ body = (Food.http.request_get "http://www.bedca.net/cocoon/svg/response#{self[:id]}langes.svg").read_body
110
+ parse_svg body
111
+ end
112
+
113
+ def parse_svg(svg)
114
+ (svg.scan SVG_REGEX).each_with_object({}) do |value, hash|
115
+ hash[COMPONENTS[value.last]] = value.first.delete(' ')
116
+ hash
117
+ end
118
+ end
119
+
120
+ def to_s
121
+ tables = @data[:groups]
122
+ .map do |(_key, group)|
123
+ group.to_table
124
+ end
125
+ .join("\n")
126
+
127
+ <<~TO_MARKDOWN
128
+ # #{self[:nombre]}
129
+
130
+ > #{self[:scientific_name]}
131
+
132
+ ## Distribución de la Energía total
133
+
134
+ Proteínas: #{@general_values[:proteina_total]}
135
+ Grasas: #{@general_values[:grasa_total]}
136
+ Carbohidratos: #{@general_values[:carbohidratos]}
137
+ Alcohol: #{@general_values[:alcohol]}
138
+
139
+ ## Información de composición (por 100 g de porción comestible)
140
+
141
+ #{tables}
142
+ TO_MARKDOWN
143
+ end
144
+ end
145
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'dietary_dsl/bedca_api/constants'
4
+
5
+ module DietaryDsl
6
+ # Clase que representa un tipo de valor nutricional y sus datos
7
+ class FoodValue
8
+ include BEDCA
9
+
10
+ def initialize(values)
11
+ data = rehash(values)
12
+
13
+ @data = data
14
+ end
15
+
16
+ def [](key)
17
+ return cantidad if key == :cantidad
18
+ @data[key]
19
+ end
20
+
21
+ def cantidad
22
+ @data[:cantidad] || @data[:vt_descripcion]
23
+ end
24
+
25
+ def to_s
26
+ "#{self[:cantidad]}#{self[:unidad]}"
27
+ end
28
+
29
+ def to_table
30
+ "| #{self[:nombre]} | #{self[:cantidad]} | #{self[:unidad]} |"
31
+ end
32
+
33
+ def inspect
34
+ to_s
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'dietary_dsl/bedca_api/constants'
4
+
5
+ module DietaryDsl
6
+ # Clase que representa un tipo de valor nutricional y sus datos
7
+ class FoodValues
8
+ include BEDCA
9
+ include Enumerable
10
+
11
+ def initialize(title, values)
12
+ @title = title
13
+ @data = arr_to_hash(parse_values(values), COMPONENTS) { |value| value[:id] }
14
+ end
15
+
16
+ def [](key)
17
+ @data[key]
18
+ end
19
+
20
+ def each
21
+ @data.each { |i| yield i }
22
+ end
23
+
24
+ def parse_values(values)
25
+ values
26
+ .map { |value| FoodValue.new(value) }
27
+ .uniq { |value| value[:id] }
28
+ end
29
+
30
+ def to_s
31
+ JSON.pretty_generate(@data).gsub(':', ' =>') + "\n"
32
+ end
33
+
34
+ def inspect
35
+ to_s
36
+ end
37
+
38
+ def to_table
39
+ <<~TO_TABLE
40
+ ### #{@title}
41
+
42
+ | Componente | Valor | Unidad |
43
+ |------------|-------|--------|
44
+ #{@data.map { |_key, value| value.to_table }.join("\n")}
45
+ TO_TABLE
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'dietary_dsl/bedca_api/food'
4
+
5
+ module DietaryDsl
6
+ # Clase que representa un menú. Un menú se compone de distintos platos (entrantes, primeros platos, postres...)
7
+ class Menu
8
+ def initialize(title, &block)
9
+ @title = title
10
+ @platos = []
11
+ instance_eval(&block)
12
+ end
13
+
14
+ def [](key)
15
+ @platos[key]
16
+ end
17
+
18
+ def plato(datos)
19
+ @platos = @platos.push datos
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'dietary_dsl/bedca_api/food'
4
+
5
+ module DietaryDsl
6
+ # Clase que representa un plato. Un plato es una comida específica, como pulpo a la gallega, o un potaje
7
+ class Plato
8
+ def initialize(title, &block)
9
+ @title = title
10
+ @alimentos = []
11
+ instance_eval(&block)
12
+ end
13
+
14
+ def [](key)
15
+ @alimentos[key]
16
+ end
17
+
18
+ def alimento(datos)
19
+ alimento = DietaryDsl::Food.find_by_like(nombre: datos[:food])
20
+ raise "El alimento #{datos[:food]} no existe" if alimento.nil?
21
+ cantidad = datos[:cantidad]
22
+ @alimentos = @alimentos.push food: alimento, cantidad: cantidad
23
+ end
24
+
25
+ def to_s
26
+ alimentos = @alimentos
27
+ .map { |data| "* #{data[:food][:nombre]}" }
28
+ .join("\n")
29
+
30
+ <<~OUTPUT
31
+ #{@title}
32
+ #{alimentos}
33
+ OUTPUT
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,77 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'dietary_dsl/measures/measure'
4
+
5
+ module DietaryDsl
6
+ # Esta clase nos permite representar unidades de masa, como gramos o kilogramos.
7
+ class Masa < Measure
8
+ def initialize(en_gramos)
9
+ super(en_gramos)
10
+ end
11
+
12
+ # Métodos de clase para instanciarlo
13
+
14
+ def self.kg(number)
15
+ DietaryDsl::Masa.new(number * 1000)
16
+ end
17
+
18
+ def self.g(number)
19
+ DietaryDsl::Masa.new(number)
20
+ end
21
+
22
+ # Métodos de conversión
23
+
24
+ def g
25
+ @cantidad
26
+ end
27
+
28
+ def gramos
29
+ g
30
+ end
31
+
32
+ def kg
33
+ @cantidad / 1000
34
+ end
35
+
36
+ def kilogramos
37
+ kg
38
+ end
39
+
40
+ # Métodos para multiplicar por un número, p.e. 3 * 10.g
41
+
42
+ def coerce(arg)
43
+ [Masa.new(arg), self]
44
+ end
45
+
46
+ def *(other)
47
+ Masa.new(@cantidad * other.cantidad)
48
+ end
49
+ end
50
+ end
51
+
52
+ # Se expande la clase Numeric con los nuevos métodos para la masa
53
+ class Numeric
54
+ def g
55
+ DietaryDsl::Masa.g(self)
56
+ end
57
+
58
+ def gramo
59
+ g
60
+ end
61
+
62
+ def gramos
63
+ g
64
+ end
65
+
66
+ def kg
67
+ DietaryDsl::Masa.kg(self)
68
+ end
69
+
70
+ def kilogramo
71
+ kg
72
+ end
73
+
74
+ def kilogramos
75
+ kg
76
+ end
77
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module DietaryDsl
4
+ # Esta clase nos permite representar unidades de masa, como gramos o kilogramos.
5
+ class Measure
6
+ attr_reader :cantidad
7
+
8
+ def initialize(cantidad)
9
+ @cantidad = cantidad.to_f
10
+ end
11
+
12
+ def to
13
+ self
14
+ end
15
+ end
16
+ end