Giter Club home page Giter Club logo

openfoodfacts-dart's Issues

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.

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 )

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)

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]});

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?

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.

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

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.

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

Add support for Autocompleting values

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.

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

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?

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>```

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.

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

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'.

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;
....

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

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.

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);

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

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.

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.

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>  

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).

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.

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).

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;
...

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

Enable CI

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

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;
}

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'`

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.