Repository: openfoodfacts/openfoodfacts-python

Issue:
Travis build failing
@frankrousseau : Travis notifications are now available on Slack, shortly after you commit. The build is currently broken (looks like an encoding issue)

writing manifest file 'openfoodfacts.egg-info/SOURCES.txt'
running build_ext
Traceback (most recent call last):
  File "setup.py", line 63, in <module>
    extras_require={
  File "/opt/python/2.7.9/lib/python2.7/distutils/core.py", line 151, in setup
    dist.run_commands()
  File "/opt/python/2.7.9/lib/python2.7/distutils/dist.py", line 953, in run_commands
    self.run_command(cmd)
  File "/opt/python/2.7.9/lib/python2.7/distutils/dist.py", line 972, in run_command
    cmd_obj.run()
  File "/home/travis/virtualenv/python2.7.9/lib/python2.7/site-packages/setuptools/command/test.py", line 142, in run
    self.with_project_on_sys_path(self.run_tests)
  File "/home/travis/virtualenv/python2.7.9/lib/python2.7/site-packages/setuptools/command/test.py", line 122, in with_project_on_sys_path
    func()
  File "/home/travis/virtualenv/python2.7.9/lib/python2.7/site-packages/setuptools/command/test.py", line 163, in run_tests
    testRunner=self._resolve_as_ep(self.test_runner),
  File "/opt/python/2.7.9/lib/python2.7/unittest/main.py", line 94, in __init__
    self.parseArgs(argv)
  File "/opt/python/2.7.9/lib/python2.7/unittest/main.py", line 149, in parseArgs
    self.createTests()
  File "/opt/python/2.7.9/lib/python2.7/unittest/main.py", line 158, in createTests
    self.module)
  File "/opt/python/2.7.9/lib/python2.7/unittest/loader.py", line 130, in loadTestsFromNames
    suites = [self.loadTestsFromName(name, module) for name in names]
  File "/opt/python/2.7.9/lib/python2.7/unittest/loader.py", line 103, in loadTestsFromName
    return self.loadTestsFromModule(obj)
  File "/home/travis/virtualenv/python2.7.9/lib/python2.7/site-packages/setuptools/command/test.py", line 37, in loadTestsFromModule
    tests.append(self.loadTestsFromName(submodule))
  File "/opt/python/2.7.9/lib/python2.7/unittest/loader.py", line 91, in loadTestsFromName
    module = __import__('.'.join(parts_copy))
  File "/home/travis/build/openfoodfacts/openfoodfacts-python/tests/facets_test.py", line 3, in <module>
    import openfoodfacts
  File "/home/travis/build/openfoodfacts/openfoodfacts-python/openfoodfacts/__init__.py", line 4, in <module>
    from . import utils
  File "/home/travis/build/openfoodfacts/openfoodfacts-python/openfoodfacts/utils.py", line 21
SyntaxError: Non-ASCII character '\xe2' in file /home/travis/build/openfoodfacts/openfoodfacts-python/openfoodfacts/utils.py on line 22, but no encoding declared; see http://python.org/dev/peps/pep-0263/ for details
The command "python setup.py test" exited with 1.
0.11s$ pep8 openfoodfacts/*.py
openfoodfacts/utils.py:19:80: E501 line too long (127 characters)
openfoodfacts/utils.py:19:1: E302 expected 2 blank lines, found 1
openfoodfacts/utils.py:19:94: E231 missing whitespace after ','
openfoodfacts/utils.py:23:1: W191 indentation contains tabs
openfoodfacts/utils.py:23:1: E101 indentation contains mixed spaces and tabs
openfoodfacts/utils.py:23:2: E113 unexpected indentation
openfoodfacts/utils.py:24:95: E225 missing whitespace around operator
The command "pep8 openfoodfacts/*.py" exited with 1.
Done. Your build exited with 1.

Discussion:

Patch:
diff --git a/README.md b/README.md
index 20ee03d..218f642 100644
--- a/README.md
+++ b/README.md
@@ -137,6 +137,8 @@ states = openfoodfacts.facets.get_states()
 
 *Get all products for given facets.*
 
+Page access (pagination) is available through parameters.
+
 ```python
 products = openfoodfacts.products.get_by_facets({
   'trace': 'egg',
@@ -147,7 +149,7 @@ products = openfoodfacts.products.get_by_facets({
 *Get all products for given additive.*
 
 ```python
-products = openfoodfacts.products.get_by_additive(additive)
+products = openfoodfacts.products.get_by_additive(additive, page=1)
 ```
 
 *Get all products for given allergen.*
diff --git a/openfoodfacts/__init__.py b/openfoodfacts/__init__.py
index b9cef3b..6ad94e6 100644
--- a/openfoodfacts/__init__.py
+++ b/openfoodfacts/__init__.py
@@ -37,8 +37,8 @@ def add_by_facet_fetch_function(facet):
     else:
         facet = facet[:-1]
 
-    def func(facet_id):
-        return utils.fetch('%s/%s' % (facet, facet_id))['products']
+    def func(facet_id, page=1):
+        return utils.fetch('%s/%s/%s' % (facet, facet_id, page))['products']
 
     func.__name__ = "get_by_%s" % facet
     setattr(products, func.__name__, func)
diff --git a/openfoodfacts/products.py b/openfoodfacts/products.py
index 9ea0327..141d59e 100644
--- a/openfoodfacts/products.py
+++ b/openfoodfacts/products.py
@@ -12,7 +12,7 @@ def get_product(barcode):
     return utils.fetch('api/v0/product/%s' % barcode)
 
 
-def get_by_facets(query):
+def get_by_facets(query, page=1):
     """
     Return products for a set of facets.
     """
@@ -28,7 +28,7 @@ def get_by_facets(query):
             path.append(key)
             path.append(query[key])
 
-        return utils.fetch('/'.join(path))['products']
+        return utils.fetch('%s/%s' % ('/'.join(path), page))['products']
 
 
 def search(query, page=1, page_size=20, sort_by='unique_scans'):


Test patch:
diff --git a/tests/facets_test.py b/tests/facets_test.py
index 51f1fe9..b54228a 100644
--- a/tests/facets_test.py
+++ b/tests/facets_test.py
@@ -1,7 +1,5 @@
 import unittest
-import os
 import openfoodfacts
-import requests
 import requests_mock
 
 
@@ -9,14 +7,14 @@ class TestFacets(unittest.TestCase):
 
     def test_get_traces(self):
         with requests_mock.mock() as mock:
-            mock.get('http://world.openfoodfacts.org/traces.json',
+            mock.get('https://world.openfoodfacts.org/traces.json',
                      text='{"tags":["egg"]}')
             res = openfoodfacts.facets.get_traces()
             self.assertEquals(res, ["egg"])
 
     def test_get_additives(self):
         with requests_mock.mock() as mock:
-            mock.get('http://world.openfoodfacts.org/additives.json',
+            mock.get('https://world.openfoodfacts.org/additives.json',
                      text='{"tags":["additive"]}')
             res = openfoodfacts.facets.get_additives()
             self.assertEquals(res, ["additive"])
diff --git a/tests/products_test.py b/tests/products_test.py
index c58a6d2..a2c3ab9 100644
--- a/tests/products_test.py
+++ b/tests/products_test.py
@@ -10,21 +10,28 @@ class TestProducts(unittest.TestCase):
     def test_get_product(self):
         with requests_mock.mock() as mock:
             mock.get(
-                'http://world.openfoodfacts.org/api/v0/product/1223435.json',
+                'https://world.openfoodfacts.org/api/v0/product/1223435.json',
                 text='{"name":"product_test"}')
             res = openfoodfacts.get_product('1223435')
             self.assertEquals(res, {'name': 'product_test'})
 
     def test_get_by_trace(self):
         with requests_mock.mock() as mock:
-            mock.get('http://world.openfoodfacts.org/trace/egg.json',
+            mock.get('https://world.openfoodfacts.org/trace/egg/1.json',
                      text='{"products":["omelet"]}')
             res = openfoodfacts.products.get_by_trace('egg')
             self.assertEquals(res, ["omelet"])
 
+    def test_get_by_trace_pagination(self):
+        with requests_mock.mock() as mock:
+            mock.get('https://world.openfoodfacts.org/trace/egg/2.json',
+                     text='{"products":["omelet"]}')
+            res = openfoodfacts.products.get_by_trace('egg', 2)
+            self.assertEquals(res, ["omelet"])
+
     def test_get_by_country(self):
         with requests_mock.mock() as mock:
-            mock.get('http://world.openfoodfacts.org/country/france.json',
+            mock.get('https://world.openfoodfacts.org/country/france/1.json',
                      text='{"products":["omelet"]}')
             res = openfoodfacts.products.get_by_country('france')
             self.assertEquals(res, ["omelet"])
@@ -35,7 +42,8 @@ class TestProducts(unittest.TestCase):
 
         with requests_mock.mock() as mock:
             mock.get(
-                'http://world.openfoodfacts.org/country/france/trace/egg.json',
+                'https://world.openfoodfacts.org/country/'
+                'france/trace/egg/1.json',
                 text='{"products":["omelet"]}')
             res = openfoodfacts.products.get_by_facets(
                     {'trace': 'egg', 'country': 'france'})
@@ -44,14 +52,14 @@ class TestProducts(unittest.TestCase):
     def test_search(self):
         with requests_mock.mock() as mock:
             mock.get(
-                'http://world.openfoodfacts.org/cgi/search.pl?' +
+                'https://world.openfoodfacts.org/cgi/search.pl?' +
                 'search_terms=kinder bueno&json=1&page=' +
                 '1&page_size=20&sort_by=unique_scans',
                 text='{"products":["kinder bueno"], "count": 1}')
             res = openfoodfacts.products.search('kinder bueno')
             self.assertEquals(res["products"],  ["kinder bueno"])
             mock.get(
-                'http://world.openfoodfacts.org/cgi/search.pl?' +
+                'https://world.openfoodfacts.org/cgi/search.pl?' +
                 'search_terms=banania&json=1&page=' +
                 '2&page_size=10&sort_by=unique_scans',
                 text='{"products":["banania", "banania big"], "count": 2}')