Skip to content

Commit

Permalink
feat: 773 - new method "getProductNameBrandQuantity" (#844)
Browse files Browse the repository at this point in the history
* feat: 773 - new method "getProductNameBrandQuantity"

Impacted files:
* `api_get_product_test.dart`: tested "abbreviated product name" fields
* `product.dart`: added method `getProductNameBrandQuantity`; added "abbreviated product name" fields
* `product.g.dart`: generated
* `product_fields.dart`: added "abbreviated product name" fields

* typo fix
  • Loading branch information
monsieurtanuki authored Feb 4, 2024
1 parent 6bb8deb commit 2167bf9
Show file tree
Hide file tree
Showing 4 changed files with 118 additions and 2 deletions.
89 changes: 89 additions & 0 deletions lib/src/model/product.dart
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,18 @@ class Product extends JsonObject {
includeIfNull: false)
Map<OpenFoodFactsLanguage, String>? genericNameInLanguages;

/// Abbreviated product name.
@JsonKey(name: 'abbreviated_product_name', includeIfNull: false)
String? abbreviatedName;

/// Localized abbreviated product name.
@JsonKey(
name: 'abbreviated_product_name_in_languages',
fromJson: LanguageHelper.fromJsonStringMap,
toJson: LanguageHelper.toJsonStringMap,
includeIfNull: false)
Map<OpenFoodFactsLanguage, String>? abbreviatedNameInLanguages;

@JsonKey(name: 'brands', includeIfNull: false)
String? brands;
@JsonKey(name: 'brands_tags', includeIfNull: false)
Expand Down Expand Up @@ -610,6 +622,11 @@ class Product extends JsonObject {
result.genericNameInLanguages ??= {};
result.genericNameInLanguages![language] = label;
break;
case ProductField.ABBREVIATED_NAME_IN_LANGUAGES:
case ProductField.ABBREVIATED_NAME_ALL_LANGUAGES:
result.abbreviatedNameInLanguages ??= {};
result.abbreviatedNameInLanguages![language] = label;
break;
case ProductField.INGREDIENTS_TEXT_IN_LANGUAGES:
case ProductField.INGREDIENTS_TEXT_ALL_LANGUAGES:
result.ingredientsTextInLanguages ??= {};
Expand Down Expand Up @@ -692,6 +709,7 @@ class Product extends JsonObject {
switch (productField) {
case ProductField.NAME_IN_LANGUAGES:
case ProductField.GENERIC_NAME_IN_LANGUAGES:
case ProductField.ABBREVIATED_NAME_IN_LANGUAGES:
case ProductField.INGREDIENTS_TEXT_IN_LANGUAGES:
case ProductField.PACKAGING_TEXT_IN_LANGUAGES:
setLanguageString(productField, language, json[key]);
Expand Down Expand Up @@ -951,4 +969,75 @@ class Product extends JsonObject {
}
_nutriments = nutriments;
}

/// Returns the best version of a product name.
///
/// cf. openfoodfacts-server/lib/ProductOpener/Products.pm
String getBestProductName(final OpenFoodFactsLanguage language) {
String? tmp;
if ((tmp = productNameInLanguages?[language])?.isNotEmpty == true) {
return tmp!;
}
if ((tmp = productName)?.isNotEmpty == true) {
return tmp!;
}
if ((tmp = genericNameInLanguages?[language])?.isNotEmpty == true) {
return tmp!;
}
if ((tmp = genericName)?.isNotEmpty == true) {
return tmp!;
}
if ((tmp = abbreviatedNameInLanguages?[language])?.isNotEmpty == true) {
return tmp!;
}
if ((tmp = abbreviatedName)?.isNotEmpty == true) {
return tmp!;
}
return '';
}

/// Returns the first of all brands.
String? getFirstBrand() {
if (brands == null) {
return null;
}
final List<String> items = brands!.split(',');
if (items.isEmpty) {
return null;
}
return items.first;
}

/// Returns a combo of the best product name and the first brand.
///
/// cf. openfoodfacts-server/lib/ProductOpener/Products.pm
String getProductNameBrand(
final OpenFoodFactsLanguage language,
final String separator,
) {
final String bestProductName = getBestProductName(language);
final String? firstBrand = getFirstBrand();
if (firstBrand == null) {
return bestProductName;
}
return '$bestProductName$separator$firstBrand';
}

/// Returns a combo of best product name, first brand and quantity.
///
/// cf. openfoodfacts-server/lib/ProductOpener/Products.pm
String getProductNameBrandQuantity(
final OpenFoodFactsLanguage language,
final String separator,
) {
final String productNameBrand = getProductNameBrand(language, separator);
if (quantity?.isNotEmpty != true) {
return productNameBrand;
}
if (productNameBrand.contains(quantity!)) {
return productNameBrand;
}
// quantity: put non breaking spaces between numbers and units
return '$productNameBrand$separator${quantity!.replaceAll(' ', '\u{00A0}')}';
}
}
6 changes: 6 additions & 0 deletions lib/src/model/product.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions lib/src/utils/product_fields.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ enum ProductField implements OffTagged {
GENERIC_NAME(offTag: 'generic_name'),
GENERIC_NAME_IN_LANGUAGES(offTag: 'generic_name_'),
GENERIC_NAME_ALL_LANGUAGES(offTag: 'generic_name_languages'),
ABBREVIATED_NAME(offTag: 'abbreviated_product_name'),
ABBREVIATED_NAME_IN_LANGUAGES(offTag: 'abbreviated_product_name_'),
ABBREVIATED_NAME_ALL_LANGUAGES(offTag: 'abbreviated_product_name_languages'),
BRANDS(offTag: 'brands'),
BRANDS_TAGS(offTag: 'brands_tags'),
BRANDS_TAGS_IN_LANGUAGES(offTag: 'brands_tags_'),
Expand Down Expand Up @@ -111,6 +114,7 @@ enum ProductField implements OffTagged {
const Set<ProductField> fieldsInLanguages = {
ProductField.NAME_IN_LANGUAGES,
ProductField.GENERIC_NAME_IN_LANGUAGES,
ProductField.ABBREVIATED_NAME_IN_LANGUAGES,
ProductField.INGREDIENTS_TEXT_IN_LANGUAGES,
ProductField.PACKAGING_TEXT_IN_LANGUAGES,
ProductField.CATEGORIES_TAGS_IN_LANGUAGES,
Expand All @@ -129,6 +133,7 @@ const Set<ProductField> fieldsInLanguages = {
const Set<ProductField> fieldsAllLanguages = {
ProductField.NAME_ALL_LANGUAGES,
ProductField.GENERIC_NAME_ALL_LANGUAGES,
ProductField.ABBREVIATED_NAME_ALL_LANGUAGES,
ProductField.INGREDIENTS_TEXT_ALL_LANGUAGES,
ProductField.PACKAGING_TEXT_ALL_LANGUAGES,
};
Expand Down
20 changes: 18 additions & 2 deletions test/api_get_product_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -515,11 +515,18 @@ void main() {
});

test('product fields', () async {
String barcode = '20004361';
const String barcode = '7300400481588';
ProductQueryConfiguration configurations = ProductQueryConfiguration(
barcode,
language: OpenFoodFactsLanguage.GERMAN,
fields: [ProductField.NAME, ProductField.BRANDS_TAGS],
fields: [
ProductField.NAME,
ProductField.BRANDS_TAGS,
ProductField.ABBREVIATED_NAME,
ProductField.ABBREVIATED_NAME_ALL_LANGUAGES,
ProductField.BRANDS,
ProductField.QUANTITY,
],
version: ProductQueryVersion.v3,
);
ProductResultV3 result = await OpenFoodAPIClient.getProductV3(
Expand All @@ -536,6 +543,15 @@ void main() {
expect(result.product!.additives!.names, isEmpty);
expect(result.product!.nutrientLevels!.levels, isEmpty);
expect(result.product!.lang, OpenFoodFactsLanguage.UNDEFINED);
expect(result.product!.abbreviatedName, isNotNull);
expect(result.product!.abbreviatedNameInLanguages, isNotNull);
expect(
result
.product!.abbreviatedNameInLanguages![OpenFoodFactsLanguage.FRENCH],
isNotNull,
);
expect(result.product!.brands, isNotNull);
expect(result.product!.quantity, isNotNull);

configurations = ProductQueryConfiguration(
barcode,
Expand Down

0 comments on commit 2167bf9

Please sign in to comment.