openfoodfacts 0.2.2 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +28 -2
- data/lib/openfoodfacts.rb +17 -0
- data/lib/openfoodfacts/additive.rb +51 -0
- data/lib/openfoodfacts/allergen.rb +33 -0
- data/lib/openfoodfacts/brand.rb +33 -0
- data/lib/openfoodfacts/category.rb +33 -0
- data/lib/openfoodfacts/city.rb +33 -0
- data/lib/openfoodfacts/contributor.rb +33 -0
- data/lib/openfoodfacts/country.rb +33 -0
- data/lib/openfoodfacts/faq.rb +50 -0
- data/lib/openfoodfacts/ingredient_that_may_be_from_palm_oil.rb +33 -0
- data/lib/openfoodfacts/label.rb +33 -0
- data/lib/openfoodfacts/manufacturing_place.rb +33 -0
- data/lib/openfoodfacts/origin.rb +33 -0
- data/lib/openfoodfacts/packager_code.rb +33 -0
- data/lib/openfoodfacts/packaging.rb +33 -0
- data/lib/openfoodfacts/press.rb +56 -0
- data/lib/openfoodfacts/product.rb +63 -1
- data/lib/openfoodfacts/product_state.rb +12 -28
- data/lib/openfoodfacts/purchase_place.rb +33 -0
- data/lib/openfoodfacts/store.rb +33 -0
- data/lib/openfoodfacts/trace.rb +33 -0
- data/lib/openfoodfacts/version.rb +1 -1
- data/test/fixtures/additives.yml +1767 -0
- data/test/fixtures/additives_locale.yml +1767 -0
- data/test/fixtures/brands.yml +24231 -0
- data/test/fixtures/brands_locale.yml +13592 -0
- data/test/fixtures/faq.yml +1025 -0
- data/test/fixtures/press.yml +2459 -0
- data/test/fixtures/product_states_locale.yml +948 -0
- data/test/fixtures/products_for_brand.yml +4171 -0
- data/test/fixtures/products_with_additive.yml +1834 -0
- data/test/test_openfoodfacts.rb +78 -0
- metadata +29 -2
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'hashie'
|
2
|
+
|
3
|
+
module Openfoodfacts
|
4
|
+
class ManufacturingPlace < Hashie::Mash
|
5
|
+
|
6
|
+
# TODO: Add more locales
|
7
|
+
LOCALE_PATHS = {
|
8
|
+
'fr' => 'lieux-de-fabrication',
|
9
|
+
'uk' => 'manufacturing-places',
|
10
|
+
'us' => 'manufacturing-places',
|
11
|
+
'world' => 'manufacturing-places'
|
12
|
+
}
|
13
|
+
|
14
|
+
class << self
|
15
|
+
|
16
|
+
# Get manufacturing places
|
17
|
+
#
|
18
|
+
def all(locale: Openfoodfacts::DEFAULT_LOCALE)
|
19
|
+
if path = LOCALE_PATHS[locale]
|
20
|
+
Product.tags_from_page(self, "http://#{locale}.openfoodfacts.org/#{path}")
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
# Get products from manufacturing place
|
27
|
+
#
|
28
|
+
def products(page: -1)
|
29
|
+
Product.from_website_page(url, page: page, products_count: products_count) if url
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'hashie'
|
2
|
+
|
3
|
+
module Openfoodfacts
|
4
|
+
class Origin < Hashie::Mash
|
5
|
+
|
6
|
+
# TODO: Add more locales
|
7
|
+
LOCALE_PATHS = {
|
8
|
+
'fr' => 'origines',
|
9
|
+
'uk' => 'origins',
|
10
|
+
'us' => 'origins',
|
11
|
+
'world' => 'origins'
|
12
|
+
}
|
13
|
+
|
14
|
+
class << self
|
15
|
+
|
16
|
+
# Get origins
|
17
|
+
#
|
18
|
+
def all(locale: Openfoodfacts::DEFAULT_LOCALE)
|
19
|
+
if path = LOCALE_PATHS[locale]
|
20
|
+
Product.tags_from_page(self, "http://#{locale}.openfoodfacts.org/#{path}")
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
# Get products with origin
|
27
|
+
#
|
28
|
+
def products(page: -1)
|
29
|
+
Product.from_website_page(url, page: page, products_count: products_count) if url
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'hashie'
|
2
|
+
|
3
|
+
module Openfoodfacts
|
4
|
+
class PackagerCode < Hashie::Mash
|
5
|
+
|
6
|
+
# TODO: Add more locales
|
7
|
+
LOCALE_PATHS = {
|
8
|
+
'fr' => 'codes-emballeurs',
|
9
|
+
'uk' => 'packager-codes',
|
10
|
+
'us' => 'packager-codes',
|
11
|
+
'world' => 'packager-codes'
|
12
|
+
}
|
13
|
+
|
14
|
+
class << self
|
15
|
+
|
16
|
+
# Get packager codes
|
17
|
+
#
|
18
|
+
def all(locale: Openfoodfacts::DEFAULT_LOCALE)
|
19
|
+
if path = LOCALE_PATHS[locale]
|
20
|
+
Product.tags_from_page(self, "http://#{locale}.openfoodfacts.org/#{path}")
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
# Get products with packager code
|
27
|
+
#
|
28
|
+
def products(page: -1)
|
29
|
+
Product.from_website_page(url, page: page, products_count: products_count) if url
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'hashie'
|
2
|
+
|
3
|
+
module Openfoodfacts
|
4
|
+
class Packaging < Hashie::Mash
|
5
|
+
|
6
|
+
# TODO: Add more locales
|
7
|
+
LOCALE_PATHS = {
|
8
|
+
'fr' => 'conditionnements',
|
9
|
+
'uk' => 'packaging',
|
10
|
+
'us' => 'packaging',
|
11
|
+
'world' => 'packaging'
|
12
|
+
}
|
13
|
+
|
14
|
+
class << self
|
15
|
+
|
16
|
+
# Get packagings
|
17
|
+
#
|
18
|
+
def all(locale: Openfoodfacts::DEFAULT_LOCALE)
|
19
|
+
if path = LOCALE_PATHS[locale]
|
20
|
+
Product.tags_from_page(self, "http://#{locale}.openfoodfacts.org/#{path}")
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
# Get products with packaging
|
27
|
+
#
|
28
|
+
def products(page: -1)
|
29
|
+
Product.from_website_page(url, page: page, products_count: products_count) if url
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'hashie'
|
2
|
+
require 'nokogiri'
|
3
|
+
require 'open-uri'
|
4
|
+
require 'time'
|
5
|
+
|
6
|
+
module Openfoodfacts
|
7
|
+
class Press < Hashie::Mash
|
8
|
+
|
9
|
+
# TODO: Add more locales
|
10
|
+
LOCALE_PATHS = {
|
11
|
+
'fr' => 'presse',
|
12
|
+
'uk' => 'press',
|
13
|
+
'us' => 'press',
|
14
|
+
'world' => 'press'
|
15
|
+
}
|
16
|
+
|
17
|
+
LOCALE_DATE_FORMATS = {
|
18
|
+
'fr' => '%d/%m/%Y',
|
19
|
+
'uk' => '%m/%d/%Y',
|
20
|
+
'us' => '%m/%d/%Y',
|
21
|
+
'world' => '%m/%d/%Y'
|
22
|
+
}
|
23
|
+
|
24
|
+
class << self
|
25
|
+
def items(locale: Openfoodfacts::DEFAULT_LOCALE)
|
26
|
+
if path = LOCALE_PATHS[locale]
|
27
|
+
html = open("http://#{locale}.openfoodfacts.org/#{path}").read
|
28
|
+
dom = Nokogiri::HTML.fragment(html)
|
29
|
+
|
30
|
+
titles = dom.css('#main_column li')
|
31
|
+
titles.each_with_index.map do |item, index|
|
32
|
+
data = item.inner_html.split(' - ')
|
33
|
+
|
34
|
+
link = Nokogiri::HTML.fragment(data.first).css('a')
|
35
|
+
attributes = {
|
36
|
+
"title" => link.text.strip,
|
37
|
+
"url" => link.attr('href').value
|
38
|
+
}
|
39
|
+
|
40
|
+
last = Nokogiri::HTML.fragment(data.last)
|
41
|
+
if date_format = LOCALE_DATE_FORMATS[locale] and date = last.text.strip[/\d+\/\d+\/\d+\z/, 0]
|
42
|
+
attributes["date"] = DateTime.strptime(date, date_format)
|
43
|
+
end
|
44
|
+
|
45
|
+
if data.length >= 3
|
46
|
+
attributes["source"] = Nokogiri::HTML.fragment(data[-2]).text.strip
|
47
|
+
end
|
48
|
+
|
49
|
+
new(attributes)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
end
|
@@ -5,6 +5,14 @@ require 'open-uri'
|
|
5
5
|
|
6
6
|
module Openfoodfacts
|
7
7
|
class Product < Hashie::Mash
|
8
|
+
|
9
|
+
# TODO: Add more locales
|
10
|
+
LOCALE_WEBURL_PREFIXES = {
|
11
|
+
'fr' => 'produit',
|
12
|
+
'uk' => 'product',
|
13
|
+
'us' => 'product',
|
14
|
+
'world' => 'product'
|
15
|
+
}
|
8
16
|
|
9
17
|
class << self
|
10
18
|
|
@@ -24,12 +32,13 @@ module Openfoodfacts
|
|
24
32
|
# Return product API URL
|
25
33
|
#
|
26
34
|
def url(code, locale: Openfoodfacts::DEFAULT_LOCALE)
|
27
|
-
"http://#{locale}.openfoodfacts.org/api/v0/produit/#{code}.json"
|
35
|
+
"http://#{locale}.openfoodfacts.org/api/v0/produit/#{code}.json" if code
|
28
36
|
end
|
29
37
|
|
30
38
|
# Search products
|
31
39
|
#
|
32
40
|
def search(terms, locale: Openfoodfacts::DEFAULT_LOCALE, page: 1, page_size: 20, sort_by: 'unique_scans_n')
|
41
|
+
terms = URI::encode(terms)
|
33
42
|
url = "http://#{locale}.openfoodfacts.org/cgi/search.pl?search_terms=#{terms}&jqm=1&page=#{page}&page_size=#{page_size}&sort_by=#{sort_by}"
|
34
43
|
json = open(url).read
|
35
44
|
hash = JSON.parse(json)
|
@@ -71,6 +80,49 @@ module Openfoodfacts
|
|
71
80
|
from_html_list(html, 'ul.products li', /\/(\d+)[\/|\Z]/i)
|
72
81
|
end
|
73
82
|
|
83
|
+
# page -1 to fetch all pages
|
84
|
+
def from_website_page(page_url, page: -1, products_count: nil)
|
85
|
+
if page == -1
|
86
|
+
if products_count # Avoid one call
|
87
|
+
pages_count = (products_count.to_f / 20).ceil
|
88
|
+
(1..pages_count).map { |page| from_website_page(page_url, page: page) }.flatten
|
89
|
+
else
|
90
|
+
products = []
|
91
|
+
|
92
|
+
page = 1
|
93
|
+
begin
|
94
|
+
products_on_page = from_website_page(page_url, page: page)
|
95
|
+
products += products_on_page
|
96
|
+
page += 1
|
97
|
+
end while products_on_page.any?
|
98
|
+
|
99
|
+
products
|
100
|
+
end
|
101
|
+
else
|
102
|
+
html = open("#{page_url}/#{page}").read
|
103
|
+
from_website_list(html)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def tags_from_page(_klass, page_url, &custom_tag_parsing)
|
108
|
+
html = open(page_url).read
|
109
|
+
dom = Nokogiri::HTML.fragment(html)
|
110
|
+
|
111
|
+
dom.css('table#tagstable tbody tr').map do |tag|
|
112
|
+
if custom_tag_parsing
|
113
|
+
custom_tag_parsing.call(tag)
|
114
|
+
else
|
115
|
+
link = tag.css('a').first
|
116
|
+
|
117
|
+
_klass.new({
|
118
|
+
"name" => link.text.strip,
|
119
|
+
"url" => URI.join(page_url, link.attr('href')).to_s,
|
120
|
+
"products_count" => tag.css('td')[1].text.to_i
|
121
|
+
})
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
74
126
|
end
|
75
127
|
|
76
128
|
# Fetch product
|
@@ -111,5 +163,15 @@ module Openfoodfacts
|
|
111
163
|
self.class.url(self.code, locale: locale)
|
112
164
|
end
|
113
165
|
|
166
|
+
# Return Product web URL according to locale
|
167
|
+
#
|
168
|
+
def weburl(locale: nil)
|
169
|
+
locale ||= self.lc || Openfoodfacts::DEFAULT_LOCALE
|
170
|
+
|
171
|
+
if self.code && prefix = LOCALE_WEBURL_PREFIXES[locale]
|
172
|
+
"http://#{locale}.openfoodfacts.org/#{prefix}/#{self.code}"
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
114
176
|
end
|
115
177
|
end
|
@@ -1,27 +1,23 @@
|
|
1
1
|
require 'hashie'
|
2
|
-
require 'nokogiri'
|
3
|
-
require 'open-uri'
|
4
2
|
|
5
3
|
module Openfoodfacts
|
6
4
|
class ProductState < Hashie::Mash
|
7
5
|
|
6
|
+
# TODO: Add more locales
|
7
|
+
LOCALE_PATHS = {
|
8
|
+
'fr' => 'etats',
|
9
|
+
'uk' => 'states',
|
10
|
+
'us' => 'states',
|
11
|
+
'world' => 'states'
|
12
|
+
}
|
13
|
+
|
8
14
|
class << self
|
9
15
|
|
10
16
|
# Get product states
|
11
17
|
#
|
12
|
-
def all
|
13
|
-
|
14
|
-
|
15
|
-
dom = Nokogiri::HTML.fragment(html)
|
16
|
-
|
17
|
-
dom.css('table#tagstable tbody tr').map do |product_state|
|
18
|
-
link = product_state.css('a').first
|
19
|
-
|
20
|
-
new({
|
21
|
-
"name" => link.text.strip,
|
22
|
-
"url" => URI.join(url, link.attr('href')).to_s,
|
23
|
-
"products_count" => product_state.css('td').last.text.to_i
|
24
|
-
})
|
18
|
+
def all(locale: Openfoodfacts::DEFAULT_LOCALE)
|
19
|
+
if path = LOCALE_PATHS[locale]
|
20
|
+
Product.tags_from_page(self, "http://#{locale}.openfoodfacts.org/#{path}")
|
25
21
|
end
|
26
22
|
end
|
27
23
|
|
@@ -30,19 +26,7 @@ module Openfoodfacts
|
|
30
26
|
# Get products with state
|
31
27
|
#
|
32
28
|
def products(page: -1)
|
33
|
-
if url
|
34
|
-
if page == -1
|
35
|
-
if products_count
|
36
|
-
pages_count = (products_count.to_f / 20).ceil
|
37
|
-
(1..pages_count).map { |page| products(page: page) }.flatten
|
38
|
-
end
|
39
|
-
else
|
40
|
-
page_url = "#{url}/#{page}"
|
41
|
-
html = open(page_url).read
|
42
|
-
|
43
|
-
Product.from_website_list(html)
|
44
|
-
end
|
45
|
-
end
|
29
|
+
Product.from_website_page(url, page: page, products_count: products_count) if url
|
46
30
|
end
|
47
31
|
|
48
32
|
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'hashie'
|
2
|
+
|
3
|
+
module Openfoodfacts
|
4
|
+
class PurchasePlace < Hashie::Mash
|
5
|
+
|
6
|
+
# TODO: Add more locales
|
7
|
+
LOCALE_PATHS = {
|
8
|
+
'fr' => 'lieux-de-vente',
|
9
|
+
'uk' => 'purchase-places',
|
10
|
+
'us' => 'purchase-places',
|
11
|
+
'world' => 'purchase-places'
|
12
|
+
}
|
13
|
+
|
14
|
+
class << self
|
15
|
+
|
16
|
+
# Get purchase places
|
17
|
+
#
|
18
|
+
def all(locale: Openfoodfacts::DEFAULT_LOCALE)
|
19
|
+
if path = LOCALE_PATHS[locale]
|
20
|
+
Product.tags_from_page(self, "http://#{locale}.openfoodfacts.org/#{path}")
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
# Get products with purchase place
|
27
|
+
#
|
28
|
+
def products(page: -1)
|
29
|
+
Product.from_website_page(url, page: page, products_count: products_count) if url
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'hashie'
|
2
|
+
|
3
|
+
module Openfoodfacts
|
4
|
+
class Store < Hashie::Mash
|
5
|
+
|
6
|
+
# TODO: Add more locales
|
7
|
+
LOCALE_PATHS = {
|
8
|
+
'fr' => 'magasins',
|
9
|
+
'uk' => 'stores',
|
10
|
+
'us' => 'stores',
|
11
|
+
'world' => 'stores'
|
12
|
+
}
|
13
|
+
|
14
|
+
class << self
|
15
|
+
|
16
|
+
# Get stores
|
17
|
+
#
|
18
|
+
def all(locale: Openfoodfacts::DEFAULT_LOCALE)
|
19
|
+
if path = LOCALE_PATHS[locale]
|
20
|
+
Product.tags_from_page(self, "http://#{locale}.openfoodfacts.org/#{path}")
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
# Get products from store
|
27
|
+
#
|
28
|
+
def products(page: -1)
|
29
|
+
Product.from_website_page(url, page: page, products_count: products_count) if url
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'hashie'
|
2
|
+
|
3
|
+
module Openfoodfacts
|
4
|
+
class Trace < Hashie::Mash
|
5
|
+
|
6
|
+
# TODO: Add more locales
|
7
|
+
LOCALE_PATHS = {
|
8
|
+
'fr' => 'traces',
|
9
|
+
'uk' => 'traces',
|
10
|
+
'us' => 'traces',
|
11
|
+
'world' => 'traces'
|
12
|
+
}
|
13
|
+
|
14
|
+
class << self
|
15
|
+
|
16
|
+
# Get traces
|
17
|
+
#
|
18
|
+
def all(locale: Openfoodfacts::DEFAULT_LOCALE)
|
19
|
+
if path = LOCALE_PATHS[locale]
|
20
|
+
Product.tags_from_page(self, "http://#{locale}.openfoodfacts.org/#{path}")
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
# Get products with trace
|
27
|
+
#
|
28
|
+
def products(page: -1)
|
29
|
+
Product.from_website_page(url, page: page, products_count: products_count) if url
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
end
|