Giter Club home page Giter Club logo

openfoodfacts-dart's Introduction

Open Food Facts - Dart

Pub Version Run sdk tests likes popularity pub points

Dart package for the Open Food Facts API. Free and Easy access to more than 2.9 million food products information from all around the world. Open Food Facts is powered by contributors from around the world and is constantly growing thanks to them.

General principles, how does it work ?

We use the ability of the Open Food Facts API to return products results in JSON, we then generate easily understandable objects structures to make it simple for you to use.

This plugin also allows you to edit a product or upload a new one to Open Food Facts. Using the same simple product structure you can create a product object or edit an existing one and send it to the API using a single function.

Migrating from 2.x.x to 3.x.x (breaking changes)

  • Starting with version 3.0.0, we now enforce all clients to provide a valid user agent. For this, please ensure to set the SDK before using any other functionality:
OpenFoodAPIConfiguration.userAgent = UserAgent(
  name: '<Name of your app>',
);
  • QueryType has been deleted. Now, for API calls you have to provide a UriProductHelper parameter. By default it will point you to openfoodfacts/prod.

  • For RobotoffAPIClient.getRandomInsights and RobotoffAPIClient.getQuestions, a list of countries instead of a single country as parameter.

  • Use OpenFoodFactsCountry.fromOffTag instead of CountryHelper.fromJson.

  • OpenFoodAPIClient.getOrderedNutrients now uses a OpenFoodFactsCountry parameter instead of a 2-letter country code.

  • Methods getProductImageRootUrl and getBarcodeSubPath are moved to UriProductHelper from ImageHelper

  • Method buildUrl renamed as getLocalizedProductImageUrl in ImageHelper

  • Removal of deprecated code.

Migrating from 1.x.x to 2.x.x (breaking changes)

  • Now the only entry point is import 'package:openfoodfacts/openfoodfacts.dart';
    • replace all your instances of import 'package:openfoodfacts/...'; with a single import 'package:openfoodfacts/openfoodfacts.dart';
  • If you used State from product_state.dart, you have to rename it to ProductState
  • If you used Level from nutrient_levels.dart, you have to rename it to NutrientLevel
  • Removed deprecated classes:
    • Page
    • ProductListQueryConfiguration
    • ToBeCompletedConfiguration
  • Removed deprecated fields and methods in Nutriments
    • all the single nutrient value fields were removed - use getValue and setValue instead
    • instead of getUnit use nutrient.typicalUnit

Usage

Installation

Follow the installing instructions on pub.dev.

How to authenticate

For most queries no authentication is required! :) Though we recommend to set a User-Agent to not to be blocked by mistake.

Setup (Optional)

At the beginning of the app you can define some global settings so that they don't need to be specified in each query. You can override these static values at any time in the app lifecycle.

import 'package:openfoodfacts/openfoodfacts.dart';

OpenFoodAPIConfiguration.userAgent = UserAgent(name: 'Your app name', url: 'Your url, if applicable');

OpenFoodAPIConfiguration.globalLanguages = <OpenFoodFactsLanguage>[
  OpenFoodFactsLanguage.ENGLISH
];

OpenFoodAPIConfiguration.globalCountry = OpenFoodFactsCountry.FRANCE;

All possible configurations can be found here.

Features

Code examples for the following tasks:

Reading data

Writing data

Robotoff support

Robotoff it the Open Food Facts AI which analyze every new pictures to extract new data.

Folksonomy Engine

Folksonomy is adding several kinds of new individual data properties to Open Food Facts or Open Products Facts.

Contributing data

Handle Open Food Facts accounts

Some queries which modify or enter data need a user account to validate this request. There are multiple ways to handle user accounts:

  1. Let your users login / create Open Food Facts user accounts (Recommended)
  2. Create a global user for your app through which all requests run

Currently there is no OAuth workflow, therefor a user is just a User object in this package. So you need to get the username as well as the password from the users and store it somewhere save. For Flutter apps we recommend using the flutter_secure_storage package

For the user to be taken into account you have mount a global user at one point in your app lifecycle:

  OpenFoodAPIConfiguration.globalUser = User(
    userId: 'myUsername',
    password: 'myPassword',
  );

Regardless if you are using a global app or accounts per user. After mounting this, the user will be added to queries where the user can be attributed.

Some methods in OpenFoodAPIClient require to pass a User, there you can access the before mounted user with:

 OpenFoodAPIClient.thisNeedsAnUser(
  user: OpenFoodAPIConfiguration.globalUser,
  ...
 );

Further examples:

If your users do not expect a specific result immediately (eg. Inventory apps)

  • Send photos (front/nutrition/ingredients/packaging): most painless thing for your users
  • The Open Food Facts AI Robotoff will generate some derived data from the photos
  • Overtime, other apps, and the Open Food Facts community will fill the data gaps

If your users do expect a result immediately (eg Nutrition apps, Scoring apps…)

  • Send nutrition facts + category > get the Nutri-Score
  • Send ingredients > get the NOVA group (about food ultra-processing), additives, allergens, normalized ingredients, vegan, vegetarian…
  • Send category (strict minimum) + labels + origins of ingredients + packaging (photo and text) > get the Eco-Score (about environmental impact)

Open Data License

The database in under the OdBL. This means attributing the source and also contributing back any additions (photos, data), which this package makes easy to do. You can check the terms of use here : Terms of use.

Useful recourses

Contribute to the package

If found a bug or missing features in this package, please open an issue for it.

  • Issue Tracker: github.com/openfoodfacts/openfoodfacts-dart/issues
  • Source Code: github.com/openfoodfacts/openfoodfacts-dart

Support

If you are having issues, that go beyond the scope of this package, please write to us on Slack or send us an email at [email protected]

Testing

Execute the following command from the root of the repository to run the tests:

dart test

Applications using this SDK

Official application

Open Food Facts (Codename Smoothie) is the official app developed by Open Food Facts, which is available on Android and iOS. The source code is also available on GitHub.

Third party applications

Feel free to open a PR to add your application in this list.

Authors

Thanks to Alexander Schacht and Primaël Quémerais for the initial creation of this package.

Contributors

Drag Racing

openfoodfacts-dart's People

Contributors

adfaure avatar akashsri3bi avatar alexgarel avatar ashaman999 avatar atharv028 avatar blazern avatar cantum2 avatar davidpryor avatar dependabot[bot] avatar fibbers avatar fredjul avatar g123k avatar github-actions[bot] avatar goerlitz avatar grumpf avatar grumpf86 avatar gspencergoog avatar indigothm avatar jasmeet0817 avatar kant avatar m123-dev avatar mohamedfboussaid avatar monsieurtanuki avatar openfoodfacts-bot avatar peterwvj avatar primaelquemerais avatar stephanegigandet avatar teolemon avatar vaiton avatar vik4114 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

openfoodfacts-dart's Issues

Fix pub.dev suggestions

Health suggestions

  • Fix lib/interface/JsonObject.dart. (-0.50 points)
    Analysis of lib/interface/JsonObject.dart reported 1 hint:
    line 1 col 8: Unused import: 'dart:ffi'.

  • Fix lib/openfoodfacts.dart. (-0.50 points)
    Analysis of lib/openfoodfacts.dart reported 1 hint:
    line 161 col 25: The declaration '_login' isn't referenced.

  • Fix lib/utils/HttpHelper.dart. (-0.50 points)
    Analysis of lib/utils/HttpHelper.dart reported 1 hint:
    line 70 col 47: This function has a return type of 'Future

  • Fix lib/utils/ImageHelper.dart. (-0.50 points)
    Analysis of lib/utils/ImageHelper.dart reported 1 hint:
    line 2 col 8: Unused import: '../model/ProductImage.dart'.

Constructing Product from JSON fails

I found an issue with construction of Product objects from JSON. The issue can be demonstrated using the following test:

import 'dart:convert';

import 'package:flutter_test/flutter_test.dart';
import 'package:openfoodfacts/model/Product.dart';
import 'package:openfoodfacts/model/ProductResult.dart';
import 'package:openfoodfacts/openfoodfacts.dart';
import 'package:openfoodfacts/utils/LanguageHelper.dart';
import 'package:openfoodfacts/utils/ProductFields.dart';
import 'package:openfoodfacts/utils/ProductQueryConfigurations.dart';

import 'test_constants.dart';

void main() {
  test('Construct Product from JSON - database example', () async {
    String barcode = '0030000010204';
    ProductQueryConfiguration configurations = ProductQueryConfiguration(
        barcode,
        language: OpenFoodFactsLanguage.ENGLISH,
        fields: [ProductField.ALL]);
    ProductResult result = await OpenFoodAPIClient.getProduct(configurations,
        user: TestConstants.TEST_USER);
    expect(result.status, 1);
    Product product = result.product;
    Map<String, dynamic> productMap = product.toJson();
    String json = jsonEncode(productMap);
    assert(json is String);
    Map<String, dynamic> productRestoredMap = jsonDecode(json);
    Product productRestored = Product.fromJson(productRestoredMap);
    expect(productRestored.productName, "Quaker Oats Old Fashioned Oatmeal 18 Ounce Paper Cannister");
  });
}

When you execute this test you get the following error:

Converting object to an encodable object failed: _LinkedHashMap len:5
dart:convert               jsonEncode
test/json_test.dart 25:19  main.<fn>

The problem is related to the selectedImages and additives fields in the Product class. If you assign these fields to null then the test passes:

...
Product product = result.product;
product.selectedImages = null;
product.additives = null;
...

kcal / kj

Hello,
is there a reason why the kcal value is not present in the nutriments class?

I saw that one can convert from KJ to kcal.
Is it a better option do to the conversion, or to add this value into the nutriment class to access the value returned by OFF API?

Thank you,
Adrien

Add support to retrieve product attributes, remove harcoded attribute groups names

See https://wiki.openfoodfacts.org/Product_Attributes for a description of product attributes.

It looks like @PrimaelQuemerais added some support for product attributes already:
https://github.com/openfoodfacts/openfoodfacts-dart/blob/master/lib/model/AttributeGroups.dart

But we should remove the harcoded names for attribute groups (nutritional_quality, processing, labels etc.): the server will add new attribute groups and attributes, and clients / libraries should not need to be updated.

Simplify the definition of search queries, remove some unneeded code

There are a few things that could be simplified / removed from the definition of search queries:

e.g.

  var parameters = <Parameter>[
    const OutputFormat(format: Format.JSON),
    const Page(page: 5),
    const PageSize(size: 10),
    const SearchSimple(active: true),
    const SortBy(option: SortOption.PRODUCT_NAME),
    const TagFilter(
        tagType: "categories",
        contains: true,
        tagName: "breakfast_cereals"),
    const TagFilter(
        tagType: "nutrition_grades", contains: true, tagName: "A")
  ];

const OutputFormat(format: Format.JSON) --> we just need JSON, the other formats like XML or JSONP are not needed, they are only available for clients that do not understand JSON

const SearchSimple(active: true), --> this has absolutely no effect, it can be removed

I think we should remove all associated code. That means it would be a breaking change, and clients would have to remove it.

10 out of 20 tests fail

After renaming the tests according according to the recommended conventions (see #31), I find that 10 of them fail.

The tests:

peter@peter-laptop:~/git/openfoodfacts-dart (master)
λ ls test/
api_addProductImage_test.dart
api_getProductRaw_test.dart
api_getProduct_test.dart
api_getRobotoff_test.dart
api_postRobotoff_test.dart
api_saveProduct_test.dart
api_searchProducts_test.dart
recommended_daily_intake_test.dart
test_constants.dart

Executing the tests:

flutter test
....
00:08 +20 -10: Some tests failed.   

The tests are noisy too, i.e. they generate a lot of console output, which is should be avoided.

The complete test log:

Test log

00:01 +0: /home/peter/git/openfoodfacts-dart/test/api_addProductImage_test.dart: OpenFoodAPIClient add product images add front image test                                     
TEST-MODE
00:01 +0 -1: /home/peter/git/openfoodfacts-dart/test/api_addProductImage_test.dart: OpenFoodAPIClient add product images add front image test [E]                              
  FileSystemException: Cannot open file, path = 'assets/front_de.jpg' (OS Error: No such file or directory, errno = 2)
  dart:io                                            _File.readAsBytes
  package:openfoodfacts/utils/HttpHelper.dart 60:61  HttpHelper.doMultipartRequest
  package:openfoodfacts/openfoodfacts.dart 96:10     OpenFoodAPIClient.addProductImage
  api_addProductImage_test.dart 25:47                main.<fn>.<fn>
  
00:01 +0 -1: /home/peter/git/openfoodfacts-dart/test/api_addProductImage_test.dart: OpenFoodAPIClient add product images add ingredients image test                            
TEST-MODE
00:01 +0 -2: /home/peter/git/openfoodfacts-dart/test/api_addProductImage_test.dart: OpenFoodAPIClient add product images add ingredients image test [E]                        
  FileSystemException: Cannot open file, path = 'assets/ingredients_en.jpg' (OS Error: No such file or directory, errno = 2)
  dart:io                                            _File.readAsBytes
  package:openfoodfacts/utils/HttpHelper.dart 60:61  HttpHelper.doMultipartRequest
  package:openfoodfacts/openfoodfacts.dart 96:10     OpenFoodAPIClient.addProductImage
  api_addProductImage_test.dart 40:47                main.<fn>.<fn>
  
00:02 +1 -2: /home/peter/git/openfoodfacts-dart/test/api_searchProducts_test.dart: OpenFoodAPIClient search products search favorite products                                  
URI: https://world.openfoodfacts.org/cgi/search.pl?json=1&page=1&page_size=10&search_type=1&sort_by=unique_scans_n&search_terms&lc=de
TEST-MODE
00:02 +1 -3: /home/peter/git/openfoodfacts-dart/test/api_saveProduct_test.dart: OpenFoodAPIClient add new products add new product test 1 [E]                                  
  NoSuchMethodError: The getter 'iterator' was called on null.
  Receiver: null
  Tried calling: iterator
  dart:core                                             Object.noSuchMethod
  package:openfoodfacts/utils/JsonHelper.dart 45:36     JsonHelper.selectedImagesToJson
  package:openfoodfacts/model/Product.g.dart 91:18      _$ProductToJson
  package:openfoodfacts/model/Product.dart 135:36       Product.toJson
  package:openfoodfacts/interface/JsonObject.dart 7:45  JsonObject.toData
  package:openfoodfacts/openfoodfacts.dart 67:33        OpenFoodAPIClient.saveProduct
  api_saveProduct_test.dart 30:35                       main.<fn>.<fn>
  
00:02 +1 -4: /home/peter/git/openfoodfacts-dart/test/api_saveProduct_test.dart: OpenFoodAPIClient add new products add new product test 2 [E]                                  
  NoSuchMethodError: The getter 'iterator' was called on null.
  Receiver: null
  Tried calling: iterator
  dart:core                                             Object.noSuchMethod
  package:openfoodfacts/utils/JsonHelper.dart 45:36     JsonHelper.selectedImagesToJson
  package:openfoodfacts/model/Product.g.dart 91:18      _$ProductToJson
  package:openfoodfacts/model/Product.dart 135:36       Product.toJson
  package:openfoodfacts/interface/JsonObject.dart 7:45  JsonObject.toData
  package:openfoodfacts/openfoodfacts.dart 67:33        OpenFoodAPIClient.saveProduct
  api_saveProduct_test.dart 46:35                       main.<fn>.<fn>
  
00:02 +1 -5: /home/peter/git/openfoodfacts-dart/test/api_saveProduct_test.dart: OpenFoodAPIClient add new products add new product test 3 [E]                                  
  NoSuchMethodError: The getter 'iterator' was called on null.
  Receiver: null
  Tried calling: iterator
  dart:core                                             Object.noSuchMethod
  package:openfoodfacts/utils/JsonHelper.dart 45:36     JsonHelper.selectedImagesToJson
  package:openfoodfacts/model/Product.g.dart 91:18      _$ProductToJson
  package:openfoodfacts/model/Product.dart 135:36       Product.toJson
  package:openfoodfacts/interface/JsonObject.dart 7:45  JsonObject.toData
  package:openfoodfacts/openfoodfacts.dart 67:33        OpenFoodAPIClient.saveProduct
  api_saveProduct_test.dart 64:35                       main.<fn>.<fn>
  
00:02 +1 -6: /home/peter/git/openfoodfacts-dart/test/api_postRobotoff_test.dart: OpenFoodAPIClient answer robotoff question get questions for Noix de Saint-Jacques EN and answer
TEST-MODE
00:02 +1 -6: /home/peter/git/openfoodfacts-dart/test/api_saveProduct_test.dart: OpenFoodAPIClient add new products add new product test 4 [E]                                  
  NoSuchMethodError: The getter 'iterator' was called on null.
  Receiver: null
  Tried calling: iterator
  dart:core                                             Object.noSuchMethod
  package:openfoodfacts/utils/JsonHelper.dart 45:36     JsonHelper.selectedImagesToJson
  package:openfoodfacts/model/Product.g.dart 91:18      _$ProductToJson
  package:openfoodfacts/model/Product.dart 135:36       Product.toJson
  package:openfoodfacts/interface/JsonObject.dart 7:45  JsonObject.toData
  package:openfoodfacts/openfoodfacts.dart 67:33        OpenFoodAPIClient.saveProduct
  api_saveProduct_test.dart 82:35                       main.<fn>.<fn>
  
00:02 +1 -6: /home/peter/git/openfoodfacts-dart/test/api_getProductRaw_test.dart: OpenFoodAPIClient get raw products get product test 1                                        
TEST-MODE
00:03 +1 -6: /home/peter/git/openfoodfacts-dart/test/api_postRobotoff_test.dart: OpenFoodAPIClient answer robotoff question get questions for Noix de Saint-Jacques EN and answer
No question found for this product
00:03 +2 -6: /home/peter/git/openfoodfacts-dart/test/api_searchProducts_test.dart: OpenFoodAPIClient search products search favorite products                                  
Unable to parse data to double : NoSuchMethodError: The getter 'length' was called on null.
Receiver: null
Tried calling: length
{code: 5449000000996, product_name: Coca Cola, product_name_de: Cola-Erfrischungsgetränk, product_name_en: Coca Cola, product_name_fr: Coca-Cola, brands: Coca-Cola, brands_tags: [coca-cola], lang: en, quantity: 330 ml, image_small_url: https://static.openfoodfacts.org/images/products/544/900/000/0996/front_de.169.200.jpg, serving_size: 330ml, serving_quantity: 330.0, product_quantity: 330, selected_images: {front: {thumb: {de: https://static.openfoodfacts.org/images/products/544/900/000/0996/front_de.169.100.jpg}, small: {de: https://static.openfoodfacts.org/images/products/544/900/000/0996/front_de.169.200.jpg}, display: {de: https://static.openfoodfacts.org/images/products/544/900/000/0996/front_de.169.400.jpg}, original: {}, null: {}}, ingredients: {thumb: {de: https://static.openfoodfacts.org/images/products/544/900/000/0996/ingredients_de.487.100.jpg}, small: {de: https://static.openfoodfacts.org/images/products/544/900/000/0996/ingredients_de.487.200.jpg}, display: {de: https://static.openfoodfacts.org/images/products/544/900/000/0996/ingredients_de.487.400.jpg}, original: {}, null: {}}, nutrition: {thumb: {en: https://static.openfoodfacts.org/images/products/544/900/000/0996/nutrition_en.402.100.jpg, es: https://static.openfoodfacts.org/images/products/544/900/000/0996/nutrition_es.218.100.jpg, fr: https://static.openfoodfacts.org/images/products/544/900/000/0996/nutrition_fr.346.100.jpg, pl: https://static.openfoodfacts.org/images/products/544/900/000/0996/nutrition_pl.369.100.jpg}, small: {en: https://static.openfoodfacts.org/images/products/544/900/000/0996/nutrition_en.402.200.jpg, es: https://static.openfoodfacts.org/images/products/544/900/000/0996/nutrition_es.218.200.jpg, fr: https://static.openfoodfacts.org/images/products/544/900/000/0996/nutrition_fr.346.200.jpg, pl: https://static.openfoodfacts.org/images/products/544/900/000/0996/nutrition_pl.369.200.jpg}, display: {en: https://static.openfoodfacts.org/images/products/544/900/000/0996/nutrition_en.402.400.jpg, es: https://static.openfoodfacts.org/images/products/544/900/000/0996/nutrition_es.218.400.jpg, fr: https://static.openfoodfacts.org/images/products/544/900/000/0996/nutrition_fr.346.400.jpg, pl: https://static.openfoodfacts.org/images/products/544/900/000/0996/nutrition_pl.369.400.jpg}, original: {}, null: {}}, other: {thumb: {}, small: {}, display: {}, original: {}, null: {}}}, images: {}, ingredients: [Instance of 'Ingredient', Instance of 'Ingredient', Instance of 'Ingredient', Instance of 'Ingredient', Instance of 'Ingredient', Instance of 'Ingredient', Instance of 'Ingredient'], nutriments: {salt_100g: 0.0, sugars_100g: 10.6, fat_100g: 0.0, saturated-fat_100g: 0.0, proteins_100g: 0.0, nova-group_100g: 4, energy_100g: 180.0, carbohydrates_100g: 10.6, salt_serving: 0.0, sugars_serving: 35.0, fat_serving: 0.0, saturated-fat_serving: 0.0, proteins_serving: 0.0, nova-group_serving: 4, energy_serving: 594.0, carbohydrates_serving: 35.0, energy_unit: KJ}, additives_tags: Instance of 'Additives', nutrient_levels: Instance of 'NutrientLevels', ingredients_text: water, sugar, carbon dioxide, dye e150d, acidifier e338, natural flavors including caffeine,, ingredients_text_de: Wasser, Zucker, Kohlensäure, Farbstoff E 150d, Säuerungsmittel: E 338, natürliche Aromen inklusive Koffein. Ingrédients: eau, sucre, acide carbonique, colorant E 150d, acidifiant E 338, arômes naturels incl. caféine., ingredients_text_en: water, sugar, carbon dioxide, dye e150d, acidifier e338, natural flavors including caffeine,, ingredients_text_fr: Eau gazéifiée, sucre, colorant : E150d, acidifiant : acide phosphorique, arômes naturels (extraits végétaux), dont caféine., ingredients_analysis_tags: [en:maybe-vegan, en:maybe-vegetarian, en:maybe-palm-oil-free], nutrition_data_per: 100g, nutrition_grade_fr: e, categories: Bevande,Bevande effervescente,Bevande zuccherate,en:boissons-boissons-gazeuses-sodas-boissons-sans-alcool-sodas-au-cola-boissons-avec-sucre-ajoute-pl-zawiera-kofeinę, categories_tags: [en:beverages, en:carbonated-drinks, en:sweetened-beverages, en:boissons-boissons-gazeuses-sodas-boissons-sans-alcool-sodas-au-cola-boissons-avec-sucre-ajoute-pl-zawiera-kofeinę], labels_tags: [en:verified], states_tags: [en:to-be-checked, en:complete, en:nutrition-facts-completed, en:ingredients-completed, en:expiration-date-to-be-completed, en:packaging-code-to-be-completed, en:characteristics-completed, en:categories-completed, en:brands-completed, en:packaging-completed, en:quantity-completed, en:product-name-completed, en:photos-validated, en:photos-uploaded], traces_tags: []}
Sucre
huile de palme
huile de noisettes
lait écrémé en poudre
cacao maigre
émulsifiants
vanilline
lécithines de soja
00:03 +3 -6: /home/peter/git/openfoodfacts-dart/test/api_searchProducts_test.dart: OpenFoodAPIClient search products search favorite products EN                               
URI: https://world.openfoodfacts.org/cgi/search.pl?json=1&page=14&page_size=3&search_type=1&sort_by=last_modified_t&search_terms&lc=en
TEST-MODE
00:03 +3 -6: /home/peter/git/openfoodfacts-dart/test/api_getRobotoff_test.dart: OpenFoodAPIClient get robotoff questions get questions for Noix de Saint-Jacques EN            
TEST-MODE
00:03 +4 -6: /home/peter/git/openfoodfacts-dart/test/api_getProductRaw_test.dart: OpenFoodAPIClient get raw products get product tiny twists - Rold Gold Pretzels - 16 OZ. (1 LB) 453.6g
https://world.openfoodfacts.org/api/v0/product/0028400047685.json?lc=en
TEST-MODE
00:03 +4 -6: /home/peter/git/openfoodfacts-dart/test/api_getRobotoff_test.dart: OpenFoodAPIClient get robotoff questions get questions for Noix de Saint-Jacques EN            
No question found for this product, please try with another barcode
00:03 +5 -6: /home/peter/git/openfoodfacts-dart/test/api_getProduct_test.dart: OpenFoodAPIClient get products get product Coca Cola Light                                      
https://world.openfoodfacts.org/api/v0/product/5000112548167.json?lc=de
00:03 +5 -6: /home/peter/git/openfoodfacts-dart/test/api_getRobotoff_test.dart: OpenFoodAPIClient get robotoff questions get questions for Noix de Saint-Jacques FR            
TEST-MODE
00:03 +5 -6: /home/peter/git/openfoodfacts-dart/test/api_getProduct_test.dart: OpenFoodAPIClient get products get product Coca Cola Light                                      
TEST-MODE
00:03 +6 -6: /home/peter/git/openfoodfacts-dart/test/api_getProductRaw_test.dart: OpenFoodAPIClient get raw products get product test 2                                        
TEST-MODE
00:03 +6 -6: /home/peter/git/openfoodfacts-dart/test/api_searchProducts_test.dart: OpenFoodAPIClient search products search favorite products EN                               
Unable to parse data to double : NoSuchMethodError: The getter 'length' was called on null.
Receiver: null
Tried calling: length
Unable to parse data to double : NoSuchMethodError: The getter 'length' was called on null.
Receiver: null
Tried calling: length
{code: 3250390796633, product_name: P'tites Ailes rôties, product_name_fr: P'tites Ailes rôties, brands: Netto, brands_tags: [netto], lang: fr, quantity: 250 g, image_small_url: https://static.openfoodfacts.org/images/products/325/039/079/6633/front_fr.8.200.jpg, serving_size: 90 g, serving_quantity: 90.0, product_quantity: 250, selected_images: {front: {thumb: {fr: https://static.openfoodfacts.org/images/products/325/039/079/6633/front_fr.8.100.jpg}, small: {fr: https://static.openfoodfacts.org/images/products/325/039/079/6633/front_fr.8.200.jpg}, display: {fr: https://static.openfoodfacts.org/images/products/325/039/079/6633/front_fr.8.400.jpg}, original: {}, null: {}}, ingredients: {thumb: {fr: https://static.openfoodfacts.org/images/products/325/039/079/6633/ingredients_fr.14.100.jpg}, small: {fr: https://static.openfoodfacts.org/images/products/325/039/079/6633/ingredients_fr.14.200.jpg}, display: {fr: https://static.openfoodfacts.org/images/products/325/039/079/6633/ingredients_fr.14.400.jpg}, original: {}, null: {}}, nutrition: {thumb: {fr: https://static.openfoodfacts.org/images/products/325/039/079/6633/nutrition_fr.11.100.jpg}, small: {fr: https://static.openfoodfacts.org/images/products/325/039/079/6633/nutrition_fr.11.200.jpg}, display: {fr: https://static.openfoodfacts.org/images/products/325/039/079/6633/nutrition_fr.11.400.jpg}, original: {}, null: {}}, other: {thumb: {}, small: {}, display: {}, original: {}, null: {}}}, images: {}, ingredients: [Instance of 'Ingredient', Instance of 'Ingredient', Instance of 'Ingredient', Instance of 'Ingredient', Instance of 'Ingredient', Instance of 'Ingredient', Instance of 'Ingredient', Instance of 'Ingredient', Instance of 'Ingredient', Instance of 'Ingredient', Instance of 'Ingredient', Instance of 'Ingredient'], nutriments: {salt_100g: 1.44, sugars_100g: 0.6, fat_100g: 10.7, saturated-fat_100g: 3.5, proteins_100g: 20.1, nova-group_100g: 4, energy_100g: 792.0, carbohydrates_100g: 3.1, salt_serving: 1.3, sugars_serving: 0.54, fat_serving: 9.63, saturated-fat_serving: 3.15, proteins_serving: 18.1, nova-group_serving: 4, energy_serving: 713.0, carbohydrates_serving: 2.79, energy_unit: KJ}, additives_tags: Instance of 'Additives', nutrient_levels: Instance of 'NutrientLevels', ingredients_text: Manchons de poulet (UE) (85%), sirop de glucose, amidon transformé de tapioca, sel, arôme (contient _gluten_ ), épaississant: carraghénanes. Peut contenir des traces de céréales, soja, oeuf, lait, céleri et moutarde., ingredients_text_fr: Manchons de poulet (UE) (85%), sirop de glucose, amidon transformé de tapioca, sel, arôme (contient _gluten_ ), épaississant: carraghénanes. Peut contenir des traces de céréales, soja, oeuf, lait, céleri et moutarde., ingredients_analysis_tags: [en:non-vegan, en:non-vegetarian, en:palm-oil-free], nutrition_data_per: 100g, nutrition_grade_fr: d, categories: Viandes, Volailles, Poulets, Poulets cuisinés, Manchons de poulet, categories_tags: [en:meats, en:poultries, en:chickens, en:cooked-chicken, en:chicken-drumsticks], labels_tags: [en:no-flavour-enhancer, en:green-dot, en:no-preservatives], states_tags: [en:to-be-checked, en:complete, en:nutrition-facts-completed, en:ingredients-completed, en:expiration-date-completed, en:packaging-code-to-be-completed, en:characteristics-completed, en:categories-completed, en:brands-completed, en:packaging-completed, en:quantity-completed, en:product-name-completed, en:photos-validated, en:photos-uploaded], traces_tags: [en:celery, en:eggs, en:fish, en:gluten, en:milk, en:molluscs, en:mustard, en:soybeans]}
00:03 +7 -6: /home/peter/git/openfoodfacts-dart/test/api_searchProducts_test.dart: OpenFoodAPIClient search products type bug : ingredient percent int vs String               
URI: https://world.openfoodfacts.org/cgi/search.pl?json=1&page=16&page_size=5&search_type=1&sort_by=unique_scans_n&search_terms&lc=de
TEST-MODE
00:03 +7 -6: /home/peter/git/openfoodfacts-dart/test/api_getRobotoff_test.dart: OpenFoodAPIClient get robotoff questions get questions for Noix de Saint-Jacques FR            
No question found for this product, please try with another barcode
00:03 +8 -6: /home/peter/git/openfoodfacts-dart/test/api_getRobotoff_test.dart: OpenFoodAPIClient get robotoff questions get 2 random questions                                
https://robotoff.openfoodfacts.org/api/v1/questions/random?lang=fr&count=2&insight_types=category
TEST-MODE
00:03 +8 -6: /home/peter/git/openfoodfacts-dart/test/api_getProductRaw_test.dart: OpenFoodAPIClient get raw products get product test 2                                        
Unable to parse data to double : NoSuchMethodError: The getter 'length' was called on null.
Receiver: null
Tried calling: length
00:03 +9 -6: /home/peter/git/openfoodfacts-dart/test/api_getProduct_test.dart: OpenFoodAPIClient get products get product Coca Cola Light                                      
Unable to parse data to double : NoSuchMethodError: The getter 'length' was called on null.
Receiver: null
Tried calling: length
Wasser, Kohlensäure, Farbstoff E 150d, Säuerungsmittel Phosphorsäure und Citronensäure, Süßungsmittel (Natriumcyclamat, Acesulfam K, Aspartam), Aroma, Aroma Koffein.
{rank: 1, id: en:water, text: Wasser, bold: null}
{rank: 2, id: en:e290, text: Kohlensäure, bold: null}
{rank: 3, id: en:colour, text: Farbstoff, bold: null}
{rank: 4, id: en:acid, text: Säuerungsmittel, bold: null}
{rank: 5, id: en:sweetener, text: Süßungsmittel, bold: null}
{rank: 6, id: en:flavouring, text: Aroma, bold: null}
{rank: 7, id: de:aroma-koffein, text: Aroma Koffein, bold: null}
{id: en:e150d, text: e150d, bold: null}
{id: en:e338, text: Phosphorsäure, bold: null}
{id: en:e330, text: Citronensäure, bold: null}
{id: en:e952, text: Natriumcyclamat, bold: null}
{id: en:e950, text: Acesulfam K, bold: null}
{id: en:e951, text: Aspartam, bold: null}
00:03 +10 -6: /home/peter/git/openfoodfacts-dart/test/api_getProduct_test.dart: OpenFoodAPIClient get products get product tiny twists - Rold Gold Pretzels - 16 OZ. (1 LB) 453.6g
https://world.openfoodfacts.org/api/v0/product/0028400047685.json?lc=en
TEST-MODE
23.0
00:03 +11 -6: /home/peter/git/openfoodfacts-dart/test/api_getProduct_test.dart: OpenFoodAPIClient get products get product Danish Butter Cookies & Chocolate Chip Cookies      
https://world.openfoodfacts.org/api/v0/product/5701184005007.json?lc=de
TEST-MODE
00:03 +11 -6: /home/peter/git/openfoodfacts-dart/test/api_searchProducts_test.dart: OpenFoodAPIClient search products type bug : ingredient percent int vs String              
Instance of 'SearchResult'
{code: 5449000232250, product_name: Thé glacé pêche intense, product_name_en: , product_name_fr: Thé glacé pêche intense, brands: Fuze Tea, brands_tags: [fuze-tea], lang: fr, quantity: 1.25L, image_small_url: https://static.openfoodfacts.org/images/products/544/900/023/2250/front_fr.184.200.jpg, serving_size: 250ml, serving_quantity: 250.0, product_quantity: 1250, selected_images: {front: {thumb: {fr: https://static.openfoodfacts.org/images/products/544/900/023/2250/front_fr.184.100.jpg}, small: {fr: https://static.openfoodfacts.org/images/products/544/900/023/2250/front_fr.184.200.jpg}, display: {fr: https://static.openfoodfacts.org/images/products/544/900/023/2250/front_fr.184.400.jpg}, original: {}, null: {}}, ingredients: {thumb: {fr: https://static.openfoodfacts.org/images/products/544/900/023/2250/ingredients_fr.174.100.jpg}, small: {fr: https://static.openfoodfacts.org/images/products/544/900/023/2250/ingredients_fr.174.200.jpg}, display: {fr: https://static.openfoodfacts.org/images/products/544/900/023/2250/ingredients_fr.174.400.jpg}, original: {}, null: {}}, nutrition: {thumb: {fr: https://static.openfoodfacts.org/images/products/544/900/023/2250/nutrition_fr.185.100.jpg}, small: {fr: https://static.openfoodfacts.org/images/products/544/900/023/2250/nutrition_fr.185.200.jpg}, display: {fr: https://static.openfoodfacts.org/images/products/544/900/023/2250/nutrition_fr.185.400.jpg}, original: {}, null: {}}, other: {thumb: {}, small: {}, display: {}, original: {}, null: {}}}, images: {}, ingredients: [Instance of 'Ingredient', Instance of 'Ingredient', Instance of 'Ingredient', Instance of 'Ingredient', Instance of 'Ingredient', Instance of 'Ingredient', Instance of 'Ingredient', Instance of 'Ingredient', Instance of 'Ingredient', Instance of 'Ingredient', Instance of 'Ingredient', Instance of 'Ingredient', Instance of 'Ingredient', Instance of 'Ingredient', Instance of 'Ingredient'], nutriments: {salt_100g: 0.03, sugars_100g: 4.3, fat_100g: 0.0, saturated-fat_100g: 0.0, proteins_100g: 0.0, nova-group_100g: 4, energy_100g: 79.0, carbohydrates_100g: 4.4, salt_serving: 0.075, sugars_serving: 10.8, fat_serving: 0.0, saturated-fat_serving: 0.0, proteins_serving: 0.0, nova-group_serving: 4, energy_serving: 198.0, carbohydrates_serving: 11.0, energy_unit: KJ}, additives_tags: Instance of 'Additives', nutrient_levels: Instance of 'NutrientLevels', ingredients_text: Eau, sucre, jus de pêche à base de concentré (1%), acidifiants : acide citrique, citrate de sodium, acide malique, extraits de thé (0,12 %), arômes naturels de pêche et thé avec autres arômes naturels, antioxydant : acide ascorbique, édulcorant : glycosides de stéviol., ingredients_text_en: , ingredients_text_fr: Eau, sucre, jus de pêche à base de concentré (1%), acidifiants : acide citrique, citrate de sodium, acide malique, extraits de thé (0,12 %), arômes naturels de pêche et thé avec autres arômes naturels, antioxydant : acide ascorbique, édulcorant : glycosides de stéviol., ingredients_analysis_tags: [en:maybe-vegan, en:maybe-vegetarian, en:palm-oil-free], nutrition_data_per: 100g, nutrition_grade_fr: d, categories: Beverages,Artificially sweetened beverages,Non-Alcoholic beverages,Tea-based beverages,Iced teas,Peach flavored iced teas,Sweetened beverages, categories_tags: [en:beverages, en:artificially-sweetened-beverages, en:non-alcoholic-beverages, en:tea-based-beverages, en:iced-teas, en:peach-flavored-iced-teas, en:sweetened-beverages], labels_tags: [en:with-sweeteners], states_tags: [en:to-be-checked, en:complete, en:nutrition-facts-completed, en:ingredients-completed, en:expiration-date-completed, en:packaging-code-completed, en:characteristics-completed, en:categories-completed, en:brands-completed, en:packaging-completed, en:quantity-completed, en:product-name-completed, en:photos-validated, en:photos-uploaded], traces_tags: []}
null
null
1.0
null
null
null
0.12
null
null
null
null
null
null
null
null
{code: 7613035866386, product_name: Eau, product_name_fr: Eau, brands: Contrex, brands_tags: [contrex], lang: fr, quantity: 1,5 l, image_small_url: https://static.openfoodfacts.org/images/products/761/303/586/6386/front_fr.101.200.jpg, serving_size: 1 l, serving_quantity: 1000.0, product_quantity: 1500, selected_images: {front: {thumb: {fr: https://static.openfoodfacts.org/images/products/761/303/586/6386/front_fr.101.100.jpg}, small: {fr: https://static.openfoodfacts.org/images/products/761/303/586/6386/front_fr.101.200.jpg}, display: {fr: https://static.openfoodfacts.org/images/products/761/303/586/6386/front_fr.101.400.jpg}, original: {}, null: {}}, ingredients: {thumb: {fr: https://static.openfoodfacts.org/images/products/761/303/586/6386/ingredients_fr.42.100.jpg}, small: {fr: https://static.openfoodfacts.org/images/products/761/303/586/6386/ingredients_fr.42.200.jpg}, display: {fr: https://static.openfoodfacts.org/images/products/761/303/586/6386/ingredients_fr.42.400.jpg}, original: {}, null: {}}, nutrition: {thumb: {fr: https://static.openfoodfacts.org/images/products/761/303/586/6386/nutrition_fr.54.100.jpg}, small: {fr: https://static.openfoodfacts.org/images/products/761/303/586/6386/nutrition_fr.54.200.jpg}, display: {fr: https://static.openfoodfacts.org/images/products/761/303/586/6386/nutrition_fr.54.400.jpg}, original: {}, null: {}}, other: {thumb: {}, small: {}, display: {}, original: {}, null: {}}}, images: {}, ingredients: [Instance of 'Ingredient', Instance of 'Ingredient'], nutriments: {salt_100g: 0.002, sugars_100g: 0.0, fat_100g: 0.0, saturated-fat_100g: 0.0, proteins_100g: 0.0, nova-group_100g: 1, energy_100g: 0.0, carbohydrates_100g: 0.0, salt_serving: 0.02, sugars_serving: 0.0, fat_serving: 0.0, saturated-fat_serving: 0.0, proteins_serving: 0.0, nova-group_serving: 1, energy_serving: 0.0, carbohydrates_serving: 0.0, energy_unit: KCAL}, additives_tags: Instance of 'Additives', nutrient_levels: Instance of 'NutrientLevels', ingredients_text: Eau minérale naturelle (source Contrex), ingredients_text_fr: Eau minérale naturelle (source Contrex), ingredients_analysis_tags: [en:maybe-vegan, en:maybe-vegetarian, en:maybe-palm-oil-free], nutrition_data_per: serving, nutrition_grade_fr: a, categories: Beverages, Dairies, Waters, Spring waters, Mineral waters, Natural mineral waters, en:unsweetened-beverages, categories_tags: [en:beverages, en:dairies, en:waters, en:spring-waters, en:mineral-waters, en:natural-mineral-waters, en:unsweetened-beverages], labels_tags: [en:green-dot, en:low-sodium], states_tags: [en:to-be-checked, en:complete, en:nutrition-facts-completed, en:ingredients-completed, en:expiration-date-completed, en:packaging-code-to-be-completed, en:characteristics-completed, en:categories-completed, en:brands-completed, en:packaging-completed, en:quantity-completed, en:product-name-completed, en:photos-validated, en:photos-uploaded], traces_tags: []}
null
null
{code: 5010477348678, product_name: Muesli 30% fruits & noix céréales complètes, product_name_en: Mueslis, product_name_fr: Muesli 30% fruits & noix céréales complètes, brands: Jordans, brands_tags: [jordans], lang: fr, quantity: 750 g, image_small_url: https://static.openfoodfacts.org/images/products/501/047/734/8678/front_fr.148.200.jpg, serving_size: 45g, serving_quantity: 45.0, product_quantity: 750, selected_images: {front: {thumb: {fr: https://static.openfoodfacts.org/images/products/501/047/734/8678/front_fr.148.100.jpg}, small: {fr: https://static.openfoodfacts.org/images/products/501/047/734/8678/front_fr.148.200.jpg}, display: {fr: https://static.openfoodfacts.org/images/products/501/047/734/8678/front_fr.148.400.jpg}, original: {}, null: {}}, ingredients: {thumb: {fr: https://static.openfoodfacts.org/images/products/501/047/734/8678/ingredients_fr.149.100.jpg}, small: {fr: https://static.openfoodfacts.org/images/products/501/047/734/8678/ingredients_fr.149.200.jpg}, display: {fr: https://static.openfoodfacts.org/images/products/501/047/734/8678/ingredients_fr.149.400.jpg}, original: {}, null: {}}, nutrition: {thumb: {fr: https://static.openfoodfacts.org/images/products/501/047/734/8678/nutrition_fr.153.100.jpg}, small: {fr: https://static.openfoodfacts.org/images/products/501/047/734/8678/nutrition_fr.153.200.jpg}, display: {fr: https://static.openfoodfacts.org/images/products/501/047/734/8678/nutrition_fr.153.400.jpg}, original: {}, null: {}}, other: {thumb: {}, small: {}, display: {}, original: {}, null: {}}}, images: {}, ingredients: [Instance of 'Ingredient', Instance of 'Ingredient', Instance of 'Ingredient', Instance of 'Ingredient', Instance of 'Ingredient', Instance of 'Ingredient', Instance of 'Ingredient', Instance of 'Ingredient', Instance of 'Ingredient', Instance of 'Ingredient', Instance of 'Ingredient', Instance of 'Ingredient', Instance of 'Ingredient', Instance of 'Ingredient'], nutriments: {salt_100g: 0.03, fiber_100g: 8.8, sugars_100g: 17.2, fat_100g: 7.2, saturated-fat_100g: 2.0, proteins_100g: 10.2, nova-group_100g: 1, energy_100g: 1523.0, carbohydrates_100g: 59.6, salt_serving: 0.0135, fiber_serving: 3.96, sugars_serving: 7.74, fat_serving: 3.24, saturated-fat_serving: 0.9, proteins_serving: 4.59, nova-group_serving: 1, energy_serving: 685.0, carbohydrates_serving: 26.8, energy_unit: KJ}, additives_tags: Instance of 'Additives', nutrient_levels: Instance of 'NutrientLevels', ingredients_text: Céréales Complètes (59%) (Flocons d'Avoine, Flocons de Blé, Flocons de Blé Grillés), Fruits Secs & Noix (30%) (Raisins Secs, Abricots secs hachés, Tranches de Pommes Séchées, Éclats de Noix de Coco Grillés, Noisettes Hachées Grillées, Amandes Effilées), Flocons d'Orge, Graines de Tournesol., ingredients_text_en: , ingredients_text_fr: Céréales Complètes (59%) (Flocons d'Avoine, Flocons de Blé, Flocons de Blé Grillés), Fruits Secs & Noix (30%) (Raisins Secs, Abricots secs hachés, Tranches de Pommes Séchées, Éclats de Noix de Coco Grillés, Noisettes Hachées Grillées, Amandes Effilées), Flocons d'Orge, Graines de Tournesol., ingredients_analysis_tags: [en:maybe-vegan, en:maybe-vegetarian, en:maybe-palm-oil-free], nutrition_data_per: 100g, nutrition_grade_fr: a, categories: Aliments et boissons à base de végétaux,Aliments d'origine végétale,Petit-déjeuners,Céréales et pommes de terre,Céréales et dérivés,Céréales pour petit-déjeuner,Mueslis,Mueslis aux fruits,Mueslis aux noix, categories_tags: [en:plant-based-foods-and-beverages, en:plant-based-foods, en:cereals-and-potatoes, en:breakfasts, en:cereals-and-their-products, en:breakfast-cereals, en:mueslis, en:mueslis-with-fruits, fr:mueslis-aux-noix], labels_tags: [en:new-recipe, en:no-added-sugar, en:no-colorings, en:no-preservatives], states_tags: [en:to-be-checked, en:complete, en:nutrition-facts-completed, en:ingredients-completed, en:expiration-date-completed, en:packaging-code-to-be-completed, en:characteristics-completed, en:categories-completed, en:brands-completed, en:packaging-completed, en:quantity-completed, en:product-name-completed, en:photos-validated, en:photos-uploaded], traces_tags: [en:gluten, en:nuts]}
59.0
30.0
30.0
null
null
null
null
null
null
null
null
null
null
null
{code: 3664346305990, product_name: Grand Arôme 32% de Cacao, product_name_fr: Grand Arôme 32% de Cacao, brands: Poulain, brands_tags: [poulain], lang: fr, quantity: 450 g, image_small_url: https://static.openfoodfacts.org/images/products/366/434/630/5990/front_fr.56.200.jpg, serving_size: 20 g + 200 ml, serving_quantity: 20.0, product_quantity: 450, selected_images: {front: {thumb: {fr: https://static.openfoodfacts.org/images/products/366/434/630/5990/front_fr.56.100.jpg}, small: {fr: https://static.openfoodfacts.org/images/products/366/434/630/5990/front_fr.56.200.jpg}, display: {fr: https://static.openfoodfacts.org/images/products/366/434/630/5990/front_fr.56.400.jpg}, original: {}, null: {}}, ingredients: {thumb: {fr: https://static.openfoodfacts.org/images/products/366/434/630/5990/ingredients_fr.51.100.jpg}, small: {fr: https://static.openfoodfacts.org/images/products/366/434/630/5990/ingredients_fr.51.200.jpg}, display: {fr: https://static.openfoodfacts.org/images/products/366/434/630/5990/ingredients_fr.51.400.jpg}, original: {}, null: {}}, nutrition: {thumb: {fr: https://static.openfoodfacts.org/images/products/366/434/630/5990/nutrition_fr.52.100.jpg}, small: {fr: https://static.openfoodfacts.org/images/products/366/434/630/5990/nutrition_fr.52.200.jpg}, display: {fr: https://static.openfoodfacts.org/images/products/366/434/630/5990/nutrition_fr.52.400.jpg}, original: {}, null: {}}, other: {thumb: {}, small: {}, display: {}, original: {}, null: {}}}, images: {}, ingredients: [Instance of 'Ingredient', Instance of 'Ingredient', Instance of 'Ingredient', Instance of 'Ingredient', Instance of 'Ingredient'], nutriments: {salt_100g: 0.03, fiber_100g: 10.5, sugars_100g: 67.0, fat_100g: 3.9, saturated-fat_100g: 2.4, proteins_100g: 7.5, nova-group_100g: 4, energy_100g: 1575.0, carbohydrates_100g: 71.0, salt_serving: 0.006, fiber_serving: 2.1, sugars_serving: 13.4, fat_serving: 0.78, saturated-fat_serving: 0.48, proteins_serving: 1.5, nova-group_serving: 4, energy_serving: 315.0, carbohydrates_serving: 14.2, energy_unit: KJ}, additives_tags: Instance of 'Additives', nutrient_levels: Instance of 'NutrientLevels', ingredients_text: Sucre, cacao maigre (11 % de beurre de cacao) en poudre, arôme., ingredients_text_fr: Sucre, cacao maigre (11 % de beurre de cacao) en poudre, arôme., ingredients_analysis_tags: [en:maybe-vegan, en:maybe-vegetarian, en:maybe-palm-oil-free], nutrition_data_per: 100g, nutrition_grade_fr: e, categories: Boissons,Petit-déjeuners,Produits déshydratés,Produits lyophilisés à reconstituer,Boissons instantanées,Cacaos et chocolats en poudre,Chocolats en poudre, categories_tags: [en:beverages, en:breakfasts, en:dried-products, en:dried-products-to-be-rehydrated, en:instant-beverages, en:cocoa-and-chocolate-powders, en:chocolate-powders], labels_tags: [en:green-dot, en:made-in-france], states_tags: [en:checked, en:complete, en:nutrition-facts-completed, en:ingredients-completed, en:expiration-date-to-be-completed, en:packaging-code-to-be-completed, en:characteristics-completed, en:categories-completed, en:brands-completed, en:packaging-completed, en:quantity-completed, en:product-name-completed, en:photos-validated, en:photos-uploaded], traces_tags: [en:milk, en:soybeans]}
null
null
null
null
11.0
{code: 5410041001204, product_name: TUC Original, product_name_de: TUC Original, product_name_en: TUC Original, product_name_fr: TUC original, brands: LU, brands_tags: [lu], lang: en, quantity: 100 g, image_small_url: https://static.openfoodfacts.org/images/products/541/004/100/1204/front_de.167.200.jpg, serving_size: 3 g, serving_quantity: 3.0, product_quantity: 100, selected_images: {front: {thumb: {de: https://static.openfoodfacts.org/images/products/541/004/100/1204/front_de.167.100.jpg}, small: {de: https://static.openfoodfacts.org/images/products/541/004/100/1204/front_de.167.200.jpg}, display: {de: https://static.openfoodfacts.org/images/products/541/004/100/1204/front_de.167.400.jpg}, original: {}, null: {}}, ingredients: {thumb: {de: https://static.openfoodfacts.org/images/products/541/004/100/1204/ingredients_de.168.100.jpg}, small: {de: https://static.openfoodfacts.org/images/products/541/004/100/1204/ingredients_de.168.200.jpg}, display: {de: https://static.openfoodfacts.org/images/products/541/004/100/1204/ingredients_de.168.400.jpg}, original: {}, null: {}}, nutrition: {thumb: {de: https://static.openfoodfacts.org/images/products/541/004/100/1204/nutrition_de.169.100.jpg}, small: {de: https://static.openfoodfacts.org/images/products/541/004/100/1204/nutrition_de.169.200.jpg}, display: {de: https://static.openfoodfacts.org/images/products/541/004/100/1204/nutrition_de.169.400.jpg}, original: {}, null: {}}, other: {thumb: {}, small: {}, display: {}, original: {}, null: {}}}, images: {}, ingredients: [Instance of 'Ingredient', Instance of 'Ingredient', Instance of 'Ingredient', Instance of 'Ingredient', Instance of 'Ingredient', Instance of 'Ingredient', Instance of 'Ingredient', Instance of 'Ingredient', Instance of 'Ingredient', Instance of 'Ingredient', Instance of 'Ingredient', Instance of 'Ingredient', Instance of 'Ingredient', Instance of 'Ingredient'], nutriments: {salt_100g: 1.75, fiber_100g: 2.4, sugars_100g: 7.1, fat_100g: 19.0, saturated-fat_100g: 1.9, proteins_100g: 8.3, nova-group_100g: 4, energy_100g: 2006.0, carbohydrates_100g: 67.0, salt_serving: 0.0525, fiber_serving: 0.072, sugars_serving: 0.213, fat_serving: 0.57, saturated-fat_serving: 0.057, proteins_serving: 0.249, nova-group_serving: 4, energy_serving: 60.2, carbohydrates_serving: 2.01, energy_unit: KJ}, additives_tags: Instance of 'Additives', nutrient_levels: Instance of 'NutrientLevels', ingredients_text: wheat  flour, palm fat,  barley  malt extract, glucose syrup, raising agents (ammonium hydrogen carbonate, sodium hydrogen carbonate),  eggs , salt, flavouring, flour treatment agent (sodium  sulphite ), emulsifier (sunflower lecithin), ingredients_text_de: _Weizenmehl_, Sonnenblumenöl (17%), _Gerstenmalzextrakt_, Glukosesirup, Backtriebmittel (Ammoniumcarbonate, Natriumcarbonate), Speisesalz, _Ei_, Aroma, Mehlbehandlungsmittel (_Natriumsulfit_), Emulgator (Sonnenblumenlecithin)., ingredients_text_en: wheat  flour, palm fat,  barley  malt extract, glucose syrup, raising agents (ammonium hydrogen carbonate, sodium hydrogen carbonate),  eggs , salt, flavouring, flour treatment agent (sodium  sulphite ), emulsifier (sunflower lecithin), ingredients_text_fr: Farine de blé, huile de tournesol 17%, extrait de malt d'orge, sirop de glucose, poudre à lever (carbonates d’ammonium, carbonate de sodium), sel, œufs, arôme, agent de traitement de la farine (sulfite de sodium), émulsifiant (lécithine de tournesol). Peut contenir lait, soja, sésame., ingredients_analysis_tags: [en:non-vegan, en:maybe-vegetarian, en:maybe-palm-oil-free], nutrition_data_per: 100g, nutrition_grade_fr: d, categories: Imbiss,Salzige Snacks,Süßwaren,Vorspeisen,Fertiggerichte,Kekse und Kuchen,Kekse,Crackers, categories_tags: [en:snacks, en:salty-snacks, en:sweet-snacks, en:appetizers, en:meals, en:biscuits-and-cakes, en:biscuits, en:crackers, en:starters], labels_tags: [], states_tags: [en:checked, en:complete, en:nutrition-facts-completed, en:ingredients-completed, en:expiration-date-completed, en:packaging-code-to-be-completed, en:characteristics-completed, en:categories-completed, en:brands-completed, en:packaging-completed, en:quantity-completed, en:product-name-completed, en:photos-validated, en:photos-uploaded], traces_tags: [en:milk, en:sesame-seeds, en:soybeans]}
null
null
null
null
null
null
null
null
null
null
null
null
null
null
00:03 +12 -6: /home/peter/git/openfoodfacts-dart/test/api_searchProducts_test.dart: OpenFoodAPIClient search products search products by keywords                              
URI: https://world.openfoodfacts.org/cgi/search.pl?json=1&page=5&page_size=10&search_type=1&sort_by=product_name&search_terms=Fruit&lc=fr
TEST-MODE
00:03 +12 -6: /home/peter/git/openfoodfacts-dart/test/api_getProduct_test.dart: OpenFoodAPIClient get products get product Danish Butter Cookies & Chocolate Chip Cookies      
Unable to parse data to double : NoSuchMethodError: The getter 'length' was called on null.
Receiver: null
Tried calling: length
Buttergebäck (_Weizenmehl_, Zucker, _Butter_ 26%, Speisesalz, Backtriebmittel (Ammouniumhydrogencarbonat), Invertzuckersirup, natürliches Aroma),
Schokolade Mürbegebäck (_Weizenmehl_, Pflanzenfett (Palm), Zucker, Schokoladenstückchen 10% (Zucker, Kakaomasse, Kakaobutter, Emulgator (Lecithin)), Backtriebmittel (Ammouniumhydrogencarbonat), fettarmes Kakaopulver, Speisesalz)
00:03 +12 -7: /home/peter/git/openfoodfacts-dart/test/api_getProduct_test.dart: OpenFoodAPIClient get products get product Danish Butter Cookies & Chocolate Chip Cookies [E]  
  Expected: <18>
    Actual: <24>
  
  package:test_api                                   expect
  package:flutter_test/src/widget_tester.dart 348:3  expect
  api_getProduct_test.dart 152:7                     main.<fn>.<fn>
  
00:03 +12 -7: /home/peter/git/openfoodfacts-dart/test/api_getProduct_test.dart: OpenFoodAPIClient get products get product Pâte brisée                                         
https://world.openfoodfacts.org/api/v0/product/20004361.json?lc=fr
TEST-MODE
00:04 +12 -7: /home/peter/git/openfoodfacts-dart/test/api_searchProducts_test.dart: OpenFoodAPIClient search products search products by keywords                              
Unable to parse data to double : NoSuchMethodError: The getter 'length' was called on null.
Receiver: null
Tried calling: length
Unable to parse data to double : NoSuchMethodError: The getter 'length' was called on null.
Receiver: null
Tried calling: length
Unable to parse data to double : NoSuchMethodError: The getter 'length' was called on null.
Receiver: null
Tried calling: length
Unable to parse data to double : NoSuchMethodError: The getter 'length' was called on null.
Receiver: null
Tried calling: length
Unable to parse data to double : NoSuchMethodError: The getter 'length' was called on null.
Receiver: null
Tried calling: length
Unable to parse data to double : NoSuchMethodError: The getter 'length' was called on null.
Receiver: null
Tried calling: length
Unable to parse data to double : NoSuchMethodError: The getter 'length' was called on null.
Receiver: null
Tried calling: length
00:04 +13 -7: /home/peter/git/openfoodfacts-dart/test/api_searchProducts_test.dart: OpenFoodAPIClient search products search products filter additives                         
URI: https://world.openfoodfacts.org/cgi/search.pl?json=1&page=5&page_size=10&search_type=1&sort_by=product_name&search_terms=Fruit+%C3%A0+coques&additives&lc=fr
TEST-MODE
00:04 +13 -8: /home/peter/git/openfoodfacts-dart/test/api_getProduct_test.dart: OpenFoodAPIClient get products get product Pâte brisée [E]                                     
  Expected: 'Pâte brisée'
    Actual: 'Ice Cream, Dulce De Leche'
     Which: is different.
            Expected: Pâte brisé ...
              Actual: Ice Cream, ...
                      ^
             Differ at offset 0
  
  package:test_api                                   expect
  package:flutter_test/src/widget_tester.dart 348:3  expect
  api_getProduct_test.dart 230:7                     main.<fn>.<fn>
  
00:04 +13 -8: /home/peter/git/openfoodfacts-dart/test/api_getProduct_test.dart: OpenFoodAPIClient get products product not available                                           
https://world.openfoodfacts.org/api/v0/product/11111111111111111111111111.json?lc=de
TEST-MODE
00:04 +14 -8: /home/peter/git/openfoodfacts-dart/test/api_getProduct_test.dart: OpenFoodAPIClient get products product ingredients                                             
https://world.openfoodfacts.org/api/v0/product/4316268596299.json?lc=de
TEST-MODE
00:04 +14 -8: /home/peter/git/openfoodfacts-dart/test/api_searchProducts_test.dart: OpenFoodAPIClient search products search products filter additives                         
Unable to parse data to double : NoSuchMethodError: The getter 'length' was called on null.
Receiver: null
Tried calling: length
Unable to parse data to double : NoSuchMethodError: The getter 'length' was called on null.
Receiver: null
Tried calling: length
Unable to parse data to double : NoSuchMethodError: The getter 'length' was called on null.
Receiver: null
Tried calling: length
Unable to parse data to double : NoSuchMethodError: The getter 'length' was called on null.
Receiver: null
Tried calling: length
Unable to parse data to double : NoSuchMethodError: The getter 'length' was called on null.
Receiver: null
Tried calling: length
Unable to parse data to double : NoSuchMethodError: The getter 'length' was called on null.
Receiver: null
Tried calling: length
Unable to parse data to double : NoSuchMethodError: The getter 'length' was called on null.
Receiver: null
Tried calling: length
Unable to parse data to double : NoSuchMethodError: The getter 'length' was called on null.
Receiver: null
Tried calling: length
URI: https://world.openfoodfacts.org/cgi/search.pl?json=1&page=5&page_size=10&search_type=1&sort_by=product_name&search_terms=Fruit+%C3%A0+coques&additives=without&lc=fr
TEST-MODE
00:04 +14 -8: /home/peter/git/openfoodfacts-dart/test/api_getProduct_test.dart: OpenFoodAPIClient get products product ingredients                                             
number of ingredients: 27
00:04 +16 -8: /home/peter/git/openfoodfacts-dart/test/api_getProduct_test.dart: OpenFoodAPIClient get products product fields                                                  
https://world.openfoodfacts.org/api/v0/product/20004361.json?lc=de&fields=product_name%2Cbrands_tags
TEST-MODE
00:04 +16 -8: /home/peter/git/openfoodfacts-dart/test/api_getRobotoff_test.dart: OpenFoodAPIClient get robotoff insights get random insight                                    
https://robotoff.openfoodfacts.org/api/v1/insights/random/?type=category
TEST-MODE
00:04 +16 -8: /home/peter/git/openfoodfacts-dart/test/api_searchProducts_test.dart: OpenFoodAPIClient search products search products filter additives                         
Unable to parse data to double : NoSuchMethodError: The getter 'length' was called on null.
Receiver: null
Tried calling: length
Unable to parse data to double : NoSuchMethodError: The getter 'length' was called on null.
Receiver: null
Tried calling: length
Unable to parse data to double : NoSuchMethodError: The getter 'length' was called on null.
Receiver: null
Tried calling: length
Unable to parse data to double : NoSuchMethodError: The getter 'length' was called on null.
Receiver: null
Tried calling: length
Unable to parse data to double : NoSuchMethodError: The getter 'length' was called on null.
Receiver: null
Tried calling: length
Unable to parse data to double : NoSuchMethodError: The getter 'length' was called on null.
Receiver: null
Tried calling: length
Unable to parse data to double : NoSuchMethodError: The getter 'length' was called on null.
Receiver: null
Tried calling: length
Unable to parse data to double : NoSuchMethodError: The getter 'length' was called on null.
Receiver: null
Tried calling: length
Unable to parse data to double : NoSuchMethodError: The getter 'length' was called on null.
Receiver: null
Tried calling: length
Total product count : 2912; Filtered count : 1494
00:04 +17 -8: /home/peter/git/openfoodfacts-dart/test/api_searchProducts_test.dart: OpenFoodAPIClient search products search products with filter on tags                      
URI: https://world.openfoodfacts.org/cgi/search.pl?json=1&page=5&page_size=10&search_type=1&sort_by=product_name&tagtype_0=categories&tag_contains_0=contains&tag_0=breakfast_cereals&tagtype_1=nutrition_grades&tag_contains_1=contains&tag_1=A&search_terms&lc=fr
TEST-MODE
00:04 +17 -8: /home/peter/git/openfoodfacts-dart/test/api_getProduct_test.dart: OpenFoodAPIClient get products product fields                                                  
Unable to parse data to double : NoSuchMethodError: The getter 'length' was called on null.
Receiver: null
Tried calling: length
00:04 +17 -9: /home/peter/git/openfoodfacts-dart/test/api_getProduct_test.dart: OpenFoodAPIClient get products product fields [E]                                              
  NoSuchMethodError: The getter 'length' was called on null.
  Receiver: null
  Tried calling: length
  dart:core                        Object.noSuchMethod
  api_getProduct_test.dart 361:41  main.<fn>.<fn>
  
00:04 +17 -9: /home/peter/git/openfoodfacts-dart/test/api_searchProducts_test.dart: OpenFoodAPIClient search products search products with filter on tags                      
Unable to parse data to double : NoSuchMethodError: The getter 'length' was called on null.
Receiver: null
Tried calling: length
Unable to parse data to double : NoSuchMethodError: The getter 'length' was called on null.
Receiver: null
Tried calling: length
Unable to parse data to double : NoSuchMethodError: The getter 'length' was called on null.
Receiver: null
Tried calling: length
Unable to parse data to double : NoSuchMethodError: The getter 'length' was called on null.
Receiver: null
Tried calling: length
Unable to parse data to double : NoSuchMethodError: The getter 'length' was called on null.
Receiver: null
Tried calling: length
{code: 50167862010002, product_name: Alara Gluten Free Fruity Oats Muesli, product_name_en: Alara Gluten Free Fruity Oats Muesli, brands: Alara, brands_tags: [alara], lang: en, quantity: 500 g, image_small_url: https://static.openfoodfacts.org/images/products/501/678/620/10002/front_en.3.200.jpg, serving_size: null, serving_quantity: null, product_quantity: 500, selected_images: {front: {thumb: {en: https://static.openfoodfacts.org/images/products/501/678/620/10002/front_en.3.100.jpg}, small: {en: https://static.openfoodfacts.org/images/products/501/678/620/10002/front_en.3.200.jpg}, display: {en: https://static.openfoodfacts.org/images/products/501/678/620/10002/front_en.3.400.jpg}, original: {}, null: {}}, ingredients: {thumb: {}, small: {}, display: {}, original: {}, null: {}}, nutrition: {thumb: {}, small: {}, display: {}, original: {}, null: {}}, other: {thumb: {}, small: {}, display: {}, original: {}, null: {}}}, images: {}, ingredients: [Instance of 'Ingredient', Instance of 'Ingredient', Instance of 'Ingredient', Instance of 'Ingredient', Instance of 'Ingredient', Instance of 'Ingredient', Instance of 'Ingredient'], nutriments: {salt_100g: 0.0, fiber_100g: 8.9, sugars_100g: 20.3, fat_100g: 8.4, saturated-fat_100g: 1.1, proteins_100g: 10.1, nova-group_100g: 1, energy_100g: 1549.0, carbohydrates_100g: 65.6, nova-group_serving: 1, energy_unit: KJ}, additives_tags: Instance of 'Additives', nutrient_levels: Instance of 'NutrientLevels', ingredients_text: _Oat_ Flakes 57%, Sultanas 18%, Sunflower Seeds 7%, Dates Chopped 5.7%, Raisins 5.7%, Linseeds 3.5%, Apricots Chopped 3%, ingredients_text_en: _Oat_ Flakes 57%, Sultanas 18%, Sunflower Seeds 7%, Dates Chopped 5.7%, Raisins 5.7%, Linseeds 3.5%, Apricots Chopped 3%, ingredients_analysis_tags: [en:vegan, en:vegetarian, en:palm-oil-free], nutrition_data_per: 100g, nutrition_grade_fr: a, categories: Plant-based foods and beverages, Plant-based foods, Breakfasts, Cereals and potatoes, Cereals and their products, Breakfast cereals, Flakes, Cereal flakes, Mixed cereal flakes, Mueslis, Wholegrains, categories_tags: [en:plant-based-foods-and-beverages, en:plant-based-foods, en:breakfasts, en:cereals-and-potatoes, en:cereals-and-their-products, en:breakfast-cereals, en:flakes, en:cereal-flakes, en:mixed-cereal-flakes, en:mueslis, en:wholegrains], labels_tags: [en:low-or-no-salt, en:low-or-no-sugar, en:vegetarian, en:gluten-free, en:vegan, en:high-fibres, en:high-proteins, en:no-milk, en:no-salt, en:no-sugar, en:nut-free, en:wheat-free, en:wholegrains], states_tags: [en:to-be-completed, en:nutrition-facts-completed, en:ingredients-completed, en:expiration-date-completed, en:packaging-code-to-be-completed, en:characteristics-completed, en:categories-completed, en:brands-completed, en:packaging-completed, en:quantity-completed, en:product-name-completed, en:photos-to-be-validated, en:photos-uploaded], traces_tags: [en:sodium]}
00:05 +19 -9: /home/peter/git/openfoodfacts-dart/test/api_getRobotoff_test.dart: OpenFoodAPIClient get robotoff insights get product insights                                  
https://robotoff.openfoodfacts.org/api/v1/insights/8025386005564
TEST-MODE
00:05 +20 -9: /home/peter/git/openfoodfacts-dart/test/api_getRobotoff_test.dart: OpenFoodAPIClient get robotoff ingredient spelling corrections get farine de blé spelling corrections
TEST-MODE
00:08 +20 -10: /home/peter/git/openfoodfacts-dart/test/api_getRobotoff_test.dart: OpenFoodAPIClient get robotoff ingredient spelling corrections get farine de blé spelling corrections [E]
  NoSuchMethodError: The getter 'length' was called on null.
  Receiver: null
  Tried calling: length
  dart:core                         Object.noSuchMethod
  api_getRobotoff_test.dart 129:52  main.<fn>.<fn>
  
00:08 +20 -10: Some tests failed
\```

</p>
</details>  

The test files are not named according to the recommended convention

The test files are not named according to the recommended convention. In consequence the tests do not run when you execute flutter test. Example:

$ flutter test
....
Test directory "test" does not appear to contain any test files.
Test files must be in that directory and end with the pattern "_test.dart".

Solution/suggestion: rename the test as follows:

api_addProductImage_test.dart
api_getProductRaw_test.dart
api_getProduct_test.dart
api_getRobotoff_test.dart
api_postRobotoff_test.dart
api_saveProduct_test.dart
api_searchProducts_test.dart
recommended_daily_intake_test.dart
test_constants.dart

I'm at this commit: 21df57e

The tests break in master

Three tests are currently failing in the master branch. I'm running the test from this commit fa0950b.

To produce the test errors run

flutter test test/

Relevant output:

TEST-MODE
00:03 +5 -1: /home/peter/git/openfoodfacts-dart/test/api_getProductRaw_test.dart: OpenFoodAPIClient get raw products get product test 1 [E]                                    
  Expected: 'Maisstärke'
    Actual: 'corn starch'
     Which: is different.
            Expected: Maisstärke ...
              Actual: corn starc ...
                      ^
             Differ at offset 0
  
  package:test_api                                   expect
  package:flutter_test/src/widget_tester.dart 348:3  expect
  test/api_getProductRaw_test.dart 35:7              main.<fn>.<fn>
...
0:04 +12 -2: /home/peter/git/openfoodfacts-dart/test/api_getProduct_test.dart: OpenFoodAPIClient get products get product Coca Cola Light [E]                                 
  Expected: <0.02>
    Actual: <0.01>
  
  package:test_api                                   expect
  package:flutter_test/src/widget_tester.dart 348:3  expect
  test/api_getProduct_test.dart 74:7                 main.<fn>.<fn>
  
00:04 +12 -2: /home/peter/git/openfoodfacts-dart/test/api_getProduct_test.dart: OpenFoodAPIClient get products get product tiny twists - Rold Gold Pretzels - 16 OZ. (1 LB) 453.6g

....
00:31 +29 -3: /home/peter/git/openfoodfacts-dart/test/api_getRobotoff_test.dart: OpenFoodAPIClient get robotoff ingredient spelling corrections get farine de blé spelling corrections [E]
  FormatException: Unexpected character (at character 1)
  <html>
  ^
  
  dart:convert                                     JsonCodec.decode
  package:openfoodfacts/openfoodfacts.dart 369:14  OpenFoodAPIClient.getIngredientSpellingCorrection
  
00:31 +29 -3: Some tests failed. 

The first two errors are probably just a consequence of changes in the database. So probably the tests should be rewritten in a way to avoid this issue. The last error ("get robotoff ingredient..") is different because the error only happens occasionally (it does not produce every time).

So in short the problem might not be the SDK itself but rather the tests. Nevertheless, this definitely complicates enabling CI (#3).

JSON generation broken

There is an issue with generation of JSON from Product objects. The issue seems to related to product images.

The issue can be demonstrated using the following test:

import 'dart:convert';

import 'package:flutter_test/flutter_test.dart';
import 'package:openfoodfacts/model/Product.dart';
import 'package:openfoodfacts/model/ProductResult.dart';
import 'package:openfoodfacts/openfoodfacts.dart';
import 'package:openfoodfacts/utils/LanguageHelper.dart';
import 'package:openfoodfacts/utils/ProductFields.dart';
import 'package:openfoodfacts/utils/ProductQueryConfigurations.dart';

import 'test_constants.dart';

void main() {
  test('Generate JSON - database example', () async {
    String barcode = '0030000010204';
    ProductQueryConfiguration configurations = ProductQueryConfiguration(
        barcode,
        language: OpenFoodFactsLanguage.ENGLISH,
        fields: [ProductField.ALL]);
    ProductResult result = await OpenFoodAPIClient.getProduct(configurations,
        user: TestConstants.TEST_USER);
    expect(result.status, 1);
    Product product = result.product;
    Map<String, dynamic> productMap = product.toJson();
    String json = jsonEncode(productMap);
    assert(json is String);
  });
}

This test prduces the following error:

Converting object to an encodable object failed: _LinkedHashMap len:5
dart:convert               jsonEncode
test/json_test.dart 25:19  main.<fn>

Hint: the problem seems to be the selectedImages defined in the Product class. In particular, if you remove the content of this field by setting it to null then the JSON is generated without errors:

....
Product product = result.product;
product.selectedImages = null;
....

Support new "packaging" image type

We added a new image type for the packaging information and/or recycling instructions. It works in the same way as the ingredients and nutrition images.

Allow to get ingredient OCR output

Open Food Facts uses optical character recognition (OCR) to retrieve nutritional data and other information from the product labels.

Process

  • Capture the barcode of the product where you want to perform the OCR.
  • The Product Opener server software opens the image (process_image=1)
  • Product Opener returns a JSON response. Processing is done using Tesseract or Google Cloud Vision (recommended). The result is often cripped with errors with Tesseract, less with Google Cloud Vision.

Notes: * The OCR may contain errors. Encourage your users to correct the output using the ingredients WRITE API. * You can also use your own OCR, especially if to plan to send a high number of queries.

Parameters
Test server: https://world.openfoodfacts.org/cgi/ingredients.pl
code=code
id=imagefield
process_image=1

How to get the serving size?

I pass in the barcode and am of course returned a ProductResult which I can drill through to find the nutriments. However the nutriments are per 100g but I need per serving. How do I obtain the serving size of the product using getProductRaw so I can do the math to get the nutriments per serving as opposed to per 100g?

Passing a ProductConfiguration without the optional fields is throwing an error

ProductQueryConfiguration configuration = ProductQueryConfiguration(barcode);

Tested with openfoodfacts: ^0.3.14+1 dart package,
As I want to get all field from all languages I was expecting that doing this is enough:
ProductQueryConfiguration configuration = ProductQueryConfiguration(barcode);

But this throw an exception:
NoSuchMethodError: The method 'contains' was called on null. Receiver: null Tried calling: contains(Instance of 'ProductField')

Maybe adding a default values can help?
ProductQueryConfiguration(this.barcode, {this.language = OpenFoodFactsLanguage.WORLD, this.fields=[ProductField.ALL]});

Problem constructing products from json

I'm experiencing an issue with the way products are constructed from JSON. The issue can be demonstrated using the following test, which currently fails, but which I'd expect to pass. I'm using openfoodfacts: 0.3.5.

import 'dart:convert';

import 'package:flutter_test/flutter_test.dart';
import 'package:openfoodfacts/model/Product.dart';

void main() {
  test('Deserialize', () {
    var product = Product();
    product.productName = 'Oatmeal';
    String jsonString = jsonEncode(product.toJson());
    Map<String, dynamic> productMap = json.decode(jsonString);
    expect(Product.fromJson(productMap).productName, 'Oatmeal');
  });
}

Test error:

NoSuchMethodError: The getter 'iterator' was called on null.
Receiver: null
Tried calling: iterator
dart:core                                          Object.noSuchMethod
package:openfoodfacts/utils/JsonHelper.dart 45:36  JsonHelper.selectedImagesToJson
package:openfoodfacts/model/Product.g.dart 91:18   _$ProductToJson
package:openfoodfacts/model/Product.dart 135:36    Product.toJson
test/serialize-json_test.dart 12:44                main.<fn>```

Return images object containing image instead of list of image objects.

The title is a little confusing but what I mean. When I built my app with off integrated. I was very confused because I got the image url, but this photo was very small / pixelated. After a while I found out that a list of images with different sizes was returned.
At the time it's:

result.product.images[2].url

It would be much easier to understand to access the image by calling

result.product.images.small.url

Product JSON generation broken due to problem with EcoscoreData

The Product JSON generation is broken. In particular, the ecoscore_data field is not converted into JSON when the Product.toJson method is called.

The problem can be demonstrated using the following test, which is expected to pass but currently doesn't with version 0.3.14+1 of the openfoodfacts-dart SDK.

import 'package:flutter_test/flutter_test.dart';
import 'package:openfoodfacts/model/EcoscoreData.dart';
import 'package:openfoodfacts/model/Product.dart';

void main() {
  test('Product/EcoscoreData Json bug', () {
    final product = Product();
    product.productName = 'TestProduct';
    final ecoscoreData = EcoscoreData();
    ecoscoreData.grade = 'x';
    product.ecoscoreData = ecoscoreData;
    final productJson = product.toJson();
    assert(!(productJson['ecoscore_data'] is EcoscoreData));
  });
}

I expect this to be related to #63. @FredJul do you know what is up?

Find a better way to handle language specific fields (e.g. product_name, ingredients_text)

The Product class has fields like:

  this.productName,
  this.productNameDE,
  this.productNameEN,
  this.productNameFR,
  this.brands,
  this.lang,
  this.quantity,
  this.imgSmallUrl,
  this.ingredientsText,
  this.ingredientsTextDE,
  this.ingredientsTextEN,

productNameDE is mapped to the product_name_de field we retrieve from the API.

But in practice, OFF contains product data in 200 languages, it doesn't make sense to have 200 fields for product name, for ingredients, for packagings, for generic name etc.

We could change the Product class so that all language specific fields are a map, with language code as keys. But that would mean we would need to check if "product_name_[language code]" exists in the JSON to populate the map, and most of them won't exist.

Instead, I think we could just remove the productNameDE etc. fields and just provide getter functions that will check in the json object whenever they get called. Something like getFieldInLanguage(PRODUCT_NAME, "de") that we could call on all language specific fields, (with PRODUCT_NAME a constant for "product_name", that we can concatenate with the language to access the right field in the JSON)

There is a similar issue for images.

Use fixed data for the tests

Today the tests are using products from the OFF DB. And with time those products are getting updated by the community, which lead to instable tests.
Maybe using mock data, a test DB if it existe or a Read-Only products to make the tests stable.

Enable CI

We have CI for Open Food Facts to run tests on all repos. It could be nice to enable in the future.

Add support for Autocompleting values

Add support for the new "Image Refresh" API

Add support for the new "Image Refresh" API

Ensure we can request photo updates for select products to users for images which should be taken or re-taken (because they are too old, or possibly too small / blurry).

https://fr.openfoodfacts.org/api/v0/produit/3483130043180/beurre-cru-a-la-baratte-les-petites-laiteries?fields=images_to_update_fr

It returns a hash of image types + language code (only for the requested language code which should be the language of the app). The value is 0 for images we don't have, or the age of the image (in seconds) for apps that want to add some context like "Our photo of the ingredients is 14 months old, could you take a new one?".

Sample API response

product: {
images_to_update_fr: {
packaging_fr: 0,
front_fr: 83734290,
ingredients_fr: 83734290
}
},

Pseudo code to use it in an app

for field in images_to_update_fr:
if field.value=0
verb = "take"
else:
verb = "refresh"
field_name = field.split.before("")
field_language = field.split.after("
")
button_text = fetch_button_text(field_name, field_language, verb)

Strings to combine (suffix the language at the end, like: "Take a nutrition picture (fr)")

verb (pick the right one based on the verb)

"Take %s picture"
"Refresh %s picture"

field_name

"ingredients"
"front"
"nutrition"
"packaging"

Optional - mention how old the image is

How to convert seconds in human readable format: 83734290 = 2 years and 7 months (example routine to convert) - https://stackoverflow.com/questions/29681328/convert-seconds-into-years-months-weeks-hours-minutes-and-seconds

Send the right query based on the initial field in images_to_update_fr

  • Do not use the computed values in the pseudo code
  • Probably refactor the methods we currently have to pass the field name, and make it future proof if we want to add new image fields.

(adapted from the original issue @ openfoodfacts/api-documentation#15 )

Null safety

Big news. Flutter 2.0.0 has just entered the stable channel.
There are a lot of big changes. 💯
Here all the release notes and the annauncment.

The most important thing for the package, null safety, is now also stable. As I already noted in #90 , all packages we use are ready for null safety. That means we are now ready to upgrade.

Remove PnnsGroupQueryConfiguration, use ProductSearchQueryConfiguration instead

There shouldn't be a need for special classes and methods to make search queries for a specific PNNS group. Those queries can be supported by the normal search queries, by specifying a tag filter on pnns_groups_1 or pnns_groups_2

e.g. https://world.openfoodfacts.org/pnns-group-2/biscuits-and-cakes is the same as https://world.openfoodfacts.org/search?pnns_groups_2_tags=biscuits-and-cakes

I doubt that any app is using those PNNS queries (except for Smoothie), but it will be a breaking change, so we should update the version to from 0.3.something to 0.4.0 when we do that.

In the mean time, maybe we could display some kind of warning to indicate that those functions are deprecated.

something like this should work:

      var parameters = <Parameter>[
        const OutputFormat(format: Format.JSON),
        const Page(page: 5),
        const PageSize(size: 10),
        const SearchSimple(active: true),
        const SortBy(option: SortOption.PRODUCT_NAME),
        const TagFilter(
            tagType: "pnns_groups_2",
            contains: true,
            tagName: "biscuits-and-cakes"),
      ];

      ProductSearchQueryConfiguration configuration =
          ProductSearchQueryConfiguration(
              parametersList: parameters,
              fields: [ProductField.ALL],
              language: OpenFoodFactsLanguage.FRENCH);

      SearchResult result = await OpenFoodAPIClient.searchProducts(
          TestConstants.TEST_USER, configuration,
          queryType: QueryType.TEST);

Add urls for selected ingredients, nutrition, and packaging images (small + medium size)

We currently have this in Product.json

@jsonkey(name: 'image_small_url', includeIfNull: false)
String imgSmallUrl;

Which corresponds to the image_small_url field. But the API can return also images for ingredients, nutrition and packaging, in small and normal size (e.g. image_ingredients_small_url / image_ingredients_url).

I'd like to add corresponding fields, and also to rename imgSmallUrl to imageSmallUrl so that the class names match the API names. (we can keep imgSmallUrl for a while and mark it as deprecated).

Clean up test output

Generally chatty tests/code should be avoided. This issue suggests getting rid of all the print statements in order to improve the test output.

Enums in IngredientsAnalysisTags.dart don't have the 'unknown' element

IngredientsAnalysisTags.dart currently have next enums:

enum VeganStatus { IS_VEGAN, IS_NOT_VEGAN, MAYBE }
enum VegetarianStatus { IS_VEGETARIAN, IS_NOT_VEGETARIAN, MAYBE }
enum PalmOilFreeStatus { IS_PALM_OIL_FREE, IS_NOT_PALM_OIL_FREE, MAYBE }

Neither of them have an unknown element, but the statuses can have such a value:

image

Looking at the code, seems like currently the IngredientsAnalysisTags structure assigns the maybe value to the statuses when an unknown status is received from server.

Since unknown and maybe actually mean different things, an unknown element should be added.

EcoScoreData JSON generation is broken

There's a problem with converting EcoScoreData objects to JSON, which can be demonstrated using the following test, which is expected to pass but currently does not:

  test('EcoscoreData JSON generation', () {
    final product = _createProductWithEcoscoreData();
    var productJson = product.toJson();
    final ecoscoreDataJson = productJson['ecoscore_data'];
    assert(ecoscoreDataJson is Map);
    final adjustmentsJson = ecoscoreDataJson['adjustments'];
    assert(adjustmentsJson is Map);
    final packagingJson = adjustmentsJson['packaging'];
    assert(packagingJson is Map);
    final originJson = adjustmentsJson['origins_of_ingredients'];
    assert(originJson is Map);
    product.ecoscoreData = null;
    productJson = product.toJson();
    assert(productJson['ecoscore_data'] == null);
  });

Product _createProductWithEcoscoreData() {
  final packaging = Packaging(score: 1.2, value: 4.1);
  final originOfIngredients = OriginsOfIngredients(
      epiScore: 1.2,
      epiValue: 3.1,
      transportationScore: 1.1,
      transportationValue: 6.7);
  final adjustments = EcoscoreAdjustments(
      packaging: packaging, originsOfIngredients: originOfIngredients);
  final ecoscoreData = EcoscoreData(
      grade: 'x',
      score: 1.2,
      status: EcoscoreStatus.KNOWN,
      adjustments: adjustments);
  final product = Product(productName: 'TestProduct');
  product.ecoscoreData = ecoscoreData;
  return product;
}

novascore is missing from Product

Tested with openfoodfacts: ^0.3.14+1 dart package, and I was looking for the novascore of the scanned product, but this result is not handled by product.dart.

Maybe adding this info could be helpful.

search functionality broken (Json mapping)

all the search tests are failing with an error message like this:

_CastError (type 'int' is not a subtype of type 'String' in type cast) #0 _$ProductFromJson (package:openfoodfacts/model/Product.g.dart:39:47) #1 new Product.fromJson (package:openfoodfacts/model/Product.dart:132:7) #2 _$SearchResultFromJson.<anonymous closure> (package:openfoodfacts/model/SearchResult.g.dart:17:40) #3 MappedListIterable.elementAt (dart:_internal/iterable.dart:417:31) #4 ListIterable.toList (dart:_internal/iterable.dart:221:19) #5 _$SearchResultFromJson (package:openfoodfacts/model/SearchResult.g.dart:18:11) #6 new SearchResult.fromJson (package:openfoodfacts/model/SearchResult.dart:28:7) #7 OpenFoodAPIClient.searchProducts (package:openfoodfacts/openfoodfacts.dart:167:31) <asynchronous suspension> #8 _SearchRouteState._search (package:fit_app/search.dart:43:35) #9 _SearchRouteState.build.<anonymous closure> (package:fit_app/search.dart:77:29) #10 EditableTextState._finalizeEditing (package:flutter/src/widgets/editable_text.dart:1380:25) #11 EditableTextState.performAction (package:flutter/src/widgets/editable_text.dart:1283:9) #12 TextInput._handleTextInputInvocation (package:flutter/src/services/text_input.dart:1070:36) #13 MethodChannel._handleAsMethodCall (package:flutter/src/services/platform_channel.dart:409:55) #14 MethodChannel.setMethodCallHandler.<anonymous closure> (package:flutter/src/services/platform_channel.dart:377:54) #15 _DefaultBinaryMessenger.handlePlatformMessage (package:flutter/src/services/binding.dart:199:33) #16 _invoke3.<anonymous closure> (dart:ui/hooks.dart:290:15) #17 _rootRun (dart:async/zone.dart:1184:13) #18 _CustomZone.run (dart:async/zone.dart:1077:19) #19 _CustomZone.runGuarded (dart:async/zone.dart:979:7) #20 _invoke3 (dart:ui/hooks.dart:289:10) #21 _dispatchPlatformMessage (dart:ui/hooks.dart:164:5)

the problem may relate to version 0.3.4

image of the product or nutrient levels is always null or empty list

Hi everyone! I’m using open food facts api in my flutter app, it works fine and return the product name, but the image of the product or nutrient levels is always null or empty list. e.g: Shiitake Pastete (Barcode: 4104420173194), I can get all the info about this product by the website or open food facts application , but I get this when I use the api :

(keys are :[code, product_name, brands, lang, selected_images, images, nutriments, additives_tags, allergens_tags, nutrient_levels, ingredients_analysis_tags]....... values are :[null, Shiitake Pastete, null, -, {}, {}, {}, [], [], {}, [en:maybe-vegan, en:maybe-vegetarian, en:maybe-palm-oil-free]])

OpenFoodAPIClient.addProductImage does not work

I'm trying to use OpenFoodAPIClient.addProductImage to upload an image but I keep receiving errors like "field imgupload_front_xx not set" despite following the examples in the documentation. I've tried to change the image fields but without luck so I suspect this to be a problem with the SDK. The problem can be demonstrated using the following test, which fails:

    test('add ingredients image test', () async {
      SendImage image = new SendImage(
        lang: OpenFoodFactsLanguage.DANISH,
        barcode: "5722970900207",
        imageField: ImageField.FRONT,
        imageUrl: Uri.parse("assets/corn_da.jpg"), // Use attached image
      );
      Status status = await OpenFoodAPIClient.addProductImage(
          TestConstants.TEST_USER, image);

      expect(status != null, true);
      assert(status.error != "field imgupload_front_xx not set");
    });

For testing purposes, here is the image I'm trying to upload
corn_da

Add vegan and vegetarian status to the Ingredient class

Hi! :)

When a product is requested from Rest API of OFF, the response contains vegan/vegetarian status for each ingredient of the product.
But the Ingredient class from Dart SDK doesn't include the veg-status for some reason.

It seems that adding the fields wouldn't be too hard as Ingredient extends JsonObject and is parsed automatically.

Image upload is broken - Unhandled Exception: Null check operator used on a null value

I'm getting this error when I call addProductmage:

2021-03-14 21:58:10.234 3827-4274/org.openfoodfacts.app E/flutter: [ERROR:flutter/lib/ui/ui_dart_state.cc(186)] Unhandled Exception: Null check operator used on a null value
#0 HttpHelper.doMultipartRequest (package:openfoodfacts/utils/HttpHelper.dart:66:65)

#1 OpenFoodAPIClient.addProductImage (package:openfoodfacts/openfoodfacts.dart:108:12)

#2 _ImageUploadCardState._getImage (package:smooth_app/cards/data_cards/image_upload_card.dart:77:13)

OpenFoodAPIClient.saveProduct fails to transfer nutriments

I found what seems to be a critical bug in OpenFoodAPIClient.saveProduct that prevents nutriments from being saved.

How to produce

Specify working user credentials and run the following code to add/edit a (real) product to the database:

import 'package:openfoodfacts/model/Nutriments.dart';
import 'package:openfoodfacts/model/Product.dart';
import 'package:openfoodfacts/model/Status.dart';
import 'package:openfoodfacts/model/User.dart';
import 'package:openfoodfacts/openfoodfacts.dart';

void main() async {
  // TODO: specify user credentials
  User myUser = User(userId: 'user', password: 'secret-password');

  var nutriments = Nutriments();
  nutriments.energy = 365;
  nutriments.carbohydrates = 12;
  nutriments.proteins = 6;
  nutriments.fat = 0.1;

  var newProduct = Product(
      barcode: "7340011364184",
      productName: "Chili beans",
      nutrimentDataPer: "100g",
      nutriments: nutriments);

  Status result = await OpenFoodAPIClient.saveProduct(myUser, newProduct);
  print(result.statusVerbose);
}

Next, look up the product by the barcode by entering https://world.openfoodfacts.org/api/v0/product/7340011364184.json into your browser.

Expected result

I'd expect the values of the code, product_name, nutrition_data_per and nutriments fields in 7340011364184.json to match those in the code snippet above.

Actual result

You'll see that the code, product_name and nutrition_data_per fields have the correct values but nutriments is empty, i.e. "nutriments":{}. So for some reason nutriments are not saved.

Other information

Produced using version 0.3.10 of the API.

error while parsing ingredient percentage

the ingredient percentage attribute could not be mappend, cause double could not be parsed as int.

Steps to reproduce: start api_test_searchProducts.dart - search by keyword

`package:openfoodfacts/interface/JsonObject.dart 19:7 JsonObject.parseInt
package:openfoodfacts/model/Ingredient.g.dart 14:25 _$IngredientFromJson
package:openfoodfacts/model/Ingredient.dart 22:7 new Ingredient.fromJson
package:openfoodfacts/model/Product.g.dart 46:43 _$ProductFromJson.
dart:_internal ListIterable.toList
package:openfoodfacts/model/Product.g.dart 47:11 _$ProductFromJson
package:openfoodfacts/model/Product.dart 99:7 new Product.fromJson
package:openfoodfacts/model/SearchResult.g.dart 17:40 _$SearchResultFromJson.
dart:_internal ListIterable.toList
package:openfoodfacts/model/SearchResult.g.dart 18:11 _$SearchResultFromJson
package:openfoodfacts/model/SearchResult.dart 28:7 new SearchResult.fromJson
package:openfoodfacts/openfoodfacts.dart 149:31 OpenFoodAPIClient.searchProducts

type 'double' is not a subtype of type 'int'`

Add support for non-destructive value addition

...
add_categories
add_labels
add_brands
...

Using these fields the requests adds a new value but doesn't replace the previous ones.

Examples:

  • https://world.openfoodfacts.net/cgi/product_jqm2.pl?code=0048151621226&user_id=username&password=*****&add_brands=Brand%203

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.