From 8a01b5c9a1a280c96cb936868ed991cd2764fdc7 Mon Sep 17 00:00:00 2001 From: monsieurtanuki Date: Mon, 25 Nov 2024 16:31:51 +0100 Subject: [PATCH] feat: 999 - added "bool? product.isImageLocked" method (#1000) * feat: 999 - added "bool? product.isImageLocked" method Impacted files: * `api_get_product_test.dart`: new test "check if images are locked" * `product.dart`: added field `owner` and methods around images - `isImageLocked`, `getLocalizedImage` and `getRawImage` * `product.g.dart`: generated * `product_fields.dart`: added field `OWNER` * Minor fix --- lib/src/model/product.dart | 58 +++++++++++++++++++++++++++++++ lib/src/model/product.g.dart | 2 ++ lib/src/utils/product_fields.dart | 1 + test/api_get_product_test.dart | 57 ++++++++++++++++++++++++++++++ 4 files changed, 118 insertions(+) diff --git a/lib/src/model/product.dart b/lib/src/model/product.dart index 8dd76818c..2b150fe92 100644 --- a/lib/src/model/product.dart +++ b/lib/src/model/product.dart @@ -222,6 +222,61 @@ class Product extends JsonObject { /// Images may be returned in multiple sizes List? getMainImages() => _getImageSubset(true); + bool? isImageLocked( + final ImageField imageField, + final OpenFoodFactsLanguage language, + ) { + if (owner == null) { + return null; + } + final ProductImage? localizedImage = getLocalizedImage( + imageField, + language, + ); + final String? imageId = localizedImage?.imgid; + if (imageId == null) { + return null; + } + final ProductImage? rawImage = getRawImage(imageId); + if (rawImage == null) { + return null; + } + return rawImage.contributor == owner; + } + + ProductImage? getLocalizedImage( + final ImageField imageField, + final OpenFoodFactsLanguage language, + ) { + if (images == null) { + return null; + } + for (final ProductImage productImage in images!) { + if (productImage.field == imageField && + productImage.language == language) { + if (productImage.rev == null) { + return null; + } + return productImage; + } + } + return null; + } + + ProductImage? getRawImage(final String imageId) { + if (images == null) { + return null; + } + for (final ProductImage productImage in images!) { + if (!productImage.isMain) { + if (productImage.imgid == imageId) { + return productImage; + } + } + } + return null; + } + List? _getImageSubset(final bool isMain) { if (images == null) { return null; @@ -537,6 +592,9 @@ class Product extends JsonObject { @JsonKey(name: 'owner_fields') Map? ownerFields; + @JsonKey() + String? owner; + /// Expiration date / best before. Just a string, no format control. @JsonKey(name: 'expiration_date') String? expirationDate; diff --git a/lib/src/model/product.g.dart b/lib/src/model/product.g.dart index eb132f3ef..1f3b60963 100644 --- a/lib/src/model/product.g.dart +++ b/lib/src/model/product.g.dart @@ -176,6 +176,7 @@ Product _$ProductFromJson(Map json) => Product( ..ownerFields = (json['owner_fields'] as Map?)?.map( (k, e) => MapEntry(k, (e as num).toInt()), ) + ..owner = json['owner'] as String? ..expirationDate = json['expiration_date'] as String?; Map _$ProductToJson(Product instance) { @@ -313,6 +314,7 @@ Map _$ProductToJson(Product instance) { writeNotNull('link', instance.website); val['obsolete'] = JsonHelper.checkboxToJSON(instance.obsolete); writeNotNull('owner_fields', instance.ownerFields); + writeNotNull('owner', instance.owner); writeNotNull('expiration_date', instance.expirationDate); val['no_nutrition_data'] = JsonHelper.checkboxToJSON(instance.noNutritionData); diff --git a/lib/src/utils/product_fields.dart b/lib/src/utils/product_fields.dart index a2e4b53aa..b17af30be 100644 --- a/lib/src/utils/product_fields.dart +++ b/lib/src/utils/product_fields.dart @@ -227,6 +227,7 @@ enum ProductField implements OffTagged { EXPIRATION_DATE(offTag: 'expiration_date'), OBSOLETE(offTag: 'obsolete'), OWNER_FIELDS(offTag: 'owner_fields'), + OWNER(offTag: 'owner'), /// All data as RAW from the server. E.g. packagings are only Strings there. RAW(offTag: 'raw'), diff --git a/test/api_get_product_test.dart b/test/api_get_product_test.dart index 341866508..12bb807f4 100644 --- a/test/api_get_product_test.dart +++ b/test/api_get_product_test.dart @@ -1259,6 +1259,63 @@ void main() { } }); + test('check if images are locked', () async { + Future checkLocked( + final String barcode, + final OpenFoodFactsLanguage language, + final Map areLocked, + ) async { + final ProductResultV3 result = await getProductV3InProd( + ProductQueryConfiguration( + barcode, + fields: [ + ProductField.OWNER, + ProductField.IMAGES, + ], + version: ProductQueryVersion.v3, + ), + ); + expect(result.status, ProductResultV3.statusSuccess); + expect(result.product, isNotNull); + for (final MapEntry entry in areLocked.entries) { + expect(result.product!.isImageLocked(entry.key, language), entry.value); + } + } + + await checkLocked( + '3560070800292', + OpenFoodFactsLanguage.FRENCH, + { + ImageField.FRONT: true, + ImageField.INGREDIENTS: false, + ImageField.NUTRITION: false, + ImageField.PACKAGING: false, + }, + ); + + await checkLocked( + '7300400481588', + OpenFoodFactsLanguage.FRENCH, + { + ImageField.FRONT: true, + ImageField.INGREDIENTS: false, + ImageField.NUTRITION: false, + ImageField.PACKAGING: false, + }, + ); + + await checkLocked( + '3240931543390', + OpenFoodFactsLanguage.FRENCH, + { + ImageField.FRONT: true, + ImageField.INGREDIENTS: true, + ImageField.NUTRITION: true, + ImageField.PACKAGING: true, + }, + ); + }); + group('$OpenFoodAPIClient get new packagings field', () { const String barcode = '3661344723290'; const OpenFoodFactsLanguage language = OpenFoodFactsLanguage.FRENCH;