diff --git a/example/lib/main.dart b/example/lib/main.dart index fcf429fdc8..6a1c151a4b 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -2,6 +2,30 @@ import 'dart:async'; import 'package:openfoodfacts/openfoodfacts.dart'; +void main() { + OpenFoodAPIConfiguration.userAgent = UserAgent( + name: 'openfoodfacts-dart', + version: '1.0.0', + url: '', + ); + getTickets(); +} + +/// Get the ticket by its ID +/// The result will be a MaybeError that can be parsed +void getTicket() async { + await NutripatrolApiClient.getTicket(ticketId: 2); +} + +/// Get all tickets +/// The result will be a MaybeError that can be parsed +void getTickets() async { + await NutripatrolApiClient.getTickets( + status: NutripatrolTicketStatus.open, + type: NutripatrolType.image, + page: 1); +} + /// request a product from the OpenFoodFacts database Future getProduct() async { var barcode = '0048151623426'; diff --git a/lib/openfoodfacts.dart b/lib/openfoodfacts.dart index 187b2ad9c8..369c7e192b 100644 --- a/lib/openfoodfacts.dart +++ b/lib/openfoodfacts.dart @@ -81,6 +81,12 @@ export 'src/model/taxonomy_packaging_recycling.dart'; export 'src/model/taxonomy_packaging_shape.dart'; export 'src/model/user.dart'; export 'src/model/user_agent.dart'; +export 'src/nutripatrol/create_flag.dart'; +export 'src/nutripatrol/get_flag.dart'; +export 'src/nutripatrol/get_ticket.dart'; +export 'src/nutripatrol/get_tickets.dart'; +export 'src/nutripatrol/update_ticket_status.dart'; +export 'src/nutripatrol_api_client.dart'; export 'src/open_food_api_client.dart'; export 'src/open_food_search_api_client.dart'; export 'src/open_prices_api_client.dart'; @@ -97,6 +103,7 @@ export 'src/prices/flavor.dart'; export 'src/prices/get_locations_order.dart'; export 'src/prices/get_locations_parameters.dart'; export 'src/prices/get_locations_result.dart'; +export 'src/prices/get_price_count_parameters_helper.dart'; // export 'src/prices/get_parameters_helper.dart'; // uncomment if really needed export 'src/prices/get_prices_order.dart'; export 'src/prices/get_prices_parameters.dart'; @@ -104,7 +111,6 @@ export 'src/prices/get_price_products_order.dart'; export 'src/prices/get_price_products_parameters.dart'; export 'src/prices/get_price_products_result.dart'; export 'src/prices/get_prices_result.dart'; -export 'src/prices/get_price_count_parameters_helper.dart'; export 'src/prices/get_proofs_order.dart'; export 'src/prices/get_proofs_parameters.dart'; export 'src/prices/get_proofs_result.dart'; @@ -127,6 +133,7 @@ export 'src/prices/proof_type.dart'; export 'src/prices/session.dart'; export 'src/prices/update_price_parameters.dart'; export 'src/prices/update_proof_parameters.dart'; +export 'src/robot_off_api_client.dart'; export 'src/search/autocomplete_search_result.dart'; export 'src/search/autocomplete_single_result.dart'; export 'src/search/fuzziness.dart'; @@ -158,4 +165,3 @@ export 'src/utils/too_many_requests_manager.dart'; export 'src/utils/unit_helper.dart'; export 'src/utils/uri_helper.dart'; export 'src/utils/uri_reader.dart'; -export 'src/robot_off_api_client.dart'; diff --git a/lib/src/model/old_product_result.g.dart b/lib/src/model/old_product_result.g.dart index 71fa41dca0..049d197480 100644 --- a/lib/src/model/old_product_result.g.dart +++ b/lib/src/model/old_product_result.g.dart @@ -1,7 +1,5 @@ // GENERATED CODE - DO NOT MODIFY BY HAND -// ignore_for_file: deprecated_member_use_from_same_package - part of 'old_product_result.dart'; // ************************************************************************** diff --git a/lib/src/nutripatrol/create_flag.dart b/lib/src/nutripatrol/create_flag.dart new file mode 100644 index 0000000000..f969bdc3c8 --- /dev/null +++ b/lib/src/nutripatrol/create_flag.dart @@ -0,0 +1,69 @@ +import 'package:json_annotation/json_annotation.dart'; +import 'package:openfoodfacts/src/nutripatrol/get_ticket.dart'; + +import '../interface/json_object.dart'; + +part 'create_flag.g.dart'; + +@JsonSerializable() +class CreateNutripatrolFlag extends JsonObject { + /// Ticket ID associated with the flag + @JsonKey() + late NutripatrolTicket ticket; + + /// Barcode of the product. + @JsonKey() + String? barcode; + + /// Type of the flag + @JsonKey() + late String type; + + /// Url of the product + @JsonKey() + late String url; + + /// User ID + @JsonKey(name: 'user_id') + late String userId; + + /// Device ID + @JsonKey(name: 'device_id') + late String deviceId; + + /// Source of the flag + @JsonKey() + late String source; + + /// Confidence of the flag + @JsonKey() + double? confidence; + + /// Image ID of the flag + @JsonKey(name: 'image_id') + String? imageId; + + /// Flavor of the flag + @JsonKey() + late String flavor; + + /// Reason for the flag + @JsonKey() + String? reason; + + /// Comment associated with the flag + @JsonKey() + String? comment; + + /// Created date of the flag + @JsonKey(name: 'created_at') + late DateTime createdAt; + + CreateNutripatrolFlag(); + + factory CreateNutripatrolFlag.fromJson(Map json) => + _$CreateNutripatrolFlagFromJson(json); + + @override + Map toJson() => _$CreateNutripatrolFlagToJson(this); +} diff --git a/lib/src/nutripatrol/create_flag.g.dart b/lib/src/nutripatrol/create_flag.g.dart new file mode 100644 index 0000000000..aaa2127930 --- /dev/null +++ b/lib/src/nutripatrol/create_flag.g.dart @@ -0,0 +1,43 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'create_flag.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +CreateNutripatrolFlag _$CreateNutripatrolFlagFromJson( + Map json) => + CreateNutripatrolFlag() + ..ticket = + NutripatrolTicket.fromJson(json['ticket'] as Map) + ..barcode = json['barcode'] as String? + ..type = json['type'] as String + ..url = json['url'] as String + ..userId = json['user_id'] as String + ..deviceId = json['device_id'] as String + ..source = json['source'] as String + ..confidence = (json['confidence'] as num?)?.toDouble() + ..imageId = json['image_id'] as String? + ..flavor = json['flavor'] as String + ..reason = json['reason'] as String? + ..comment = json['comment'] as String? + ..createdAt = DateTime.parse(json['created_at'] as String); + +Map _$CreateNutripatrolFlagToJson( + CreateNutripatrolFlag instance) => + { + 'ticket': instance.ticket, + 'barcode': instance.barcode, + 'type': instance.type, + 'url': instance.url, + 'user_id': instance.userId, + 'device_id': instance.deviceId, + 'source': instance.source, + 'confidence': instance.confidence, + 'image_id': instance.imageId, + 'flavor': instance.flavor, + 'reason': instance.reason, + 'comment': instance.comment, + 'created_at': instance.createdAt.toIso8601String(), + }; diff --git a/lib/src/nutripatrol/get_flag.dart b/lib/src/nutripatrol/get_flag.dart new file mode 100644 index 0000000000..2612065020 --- /dev/null +++ b/lib/src/nutripatrol/get_flag.dart @@ -0,0 +1,75 @@ +import 'package:json_annotation/json_annotation.dart'; +import 'get_ticket.dart'; +import '../prices/flavor.dart'; + +import '../interface/json_object.dart'; +import '../utils/nutripatrol_types.dart'; + +part 'get_flag.g.dart'; + +@JsonSerializable() +class NutripatrolFlag extends JsonObject { + /// Flag ID. Read-only. + @JsonKey() + late String id; + + /// Ticket ID. Read-only. + @JsonKey() + late NutripatrolTicket ticket; + + /// Barcode of the product. Read-only. + @JsonKey() + String? barcode; + + /// Type of the flag + @JsonKey() + late NutripatrolType type; + + /// Url of the product. Read-only. + @JsonKey() + late String url; + + /// User ID. Read-only. + @JsonKey(name: 'user_id') + late String userId; + + /// Device ID. Read-only. + @JsonKey(name: 'device_id') + late String deviceId; + + /// Source of the flag + @JsonKey() + late NutripatrolSource source; + + /// Confidence of the flag + @JsonKey() + double? confidence; + + /// Image ID of the flag. Read-only. + @JsonKey(name: 'image_id') + String? imageId; + + /// Flavor of the flag + @JsonKey() + late Flavor flavor; + + /// Reason of the flag + @JsonKey() + String? reason; + + /// Comment of the flag + @JsonKey() + String? comment; + + /// created date of the ticket. Read-only. + @JsonKey(name: 'created_at') + late DateTime createdAt; + + NutripatrolFlag(); + + factory NutripatrolFlag.fromJson(Map json) => + _$NutripatrolFlagFromJson(json); + + @override + Map toJson() => _$NutripatrolFlagToJson(this); +} diff --git a/lib/src/nutripatrol/get_flag.g.dart b/lib/src/nutripatrol/get_flag.g.dart new file mode 100644 index 0000000000..9899a6904c --- /dev/null +++ b/lib/src/nutripatrol/get_flag.g.dart @@ -0,0 +1,63 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'get_flag.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +NutripatrolFlag _$NutripatrolFlagFromJson(Map json) => + NutripatrolFlag() + ..id = json['id'] as String + ..ticket = + NutripatrolTicket.fromJson(json['ticket'] as Map) + ..barcode = json['barcode'] as String? + ..type = $enumDecode(_$NutripatrolTypeEnumMap, json['type']) + ..url = json['url'] as String + ..userId = json['user_id'] as String + ..deviceId = json['device_id'] as String + ..source = $enumDecode(_$NutripatrolSourceEnumMap, json['source']) + ..confidence = (json['confidence'] as num?)?.toDouble() + ..imageId = json['image_id'] as String? + ..flavor = $enumDecode(_$FlavorEnumMap, json['flavor']) + ..reason = json['reason'] as String? + ..comment = json['comment'] as String? + ..createdAt = DateTime.parse(json['created_at'] as String); + +Map _$NutripatrolFlagToJson(NutripatrolFlag instance) => + { + 'id': instance.id, + 'ticket': instance.ticket, + 'barcode': instance.barcode, + 'type': _$NutripatrolTypeEnumMap[instance.type]!, + 'url': instance.url, + 'user_id': instance.userId, + 'device_id': instance.deviceId, + 'source': _$NutripatrolSourceEnumMap[instance.source]!, + 'confidence': instance.confidence, + 'image_id': instance.imageId, + 'flavor': _$FlavorEnumMap[instance.flavor]!, + 'reason': instance.reason, + 'comment': instance.comment, + 'created_at': instance.createdAt.toIso8601String(), + }; + +const _$NutripatrolTypeEnumMap = { + NutripatrolType.image: 'image', + NutripatrolType.product: 'product', + NutripatrolType.search: 'search', +}; + +const _$NutripatrolSourceEnumMap = { + NutripatrolSource.mobile: 'mobile', + NutripatrolSource.web: 'web', + NutripatrolSource.robotoff: 'robotoff', +}; + +const _$FlavorEnumMap = { + Flavor.openFoodFacts: 'off', + Flavor.openBeautyFacts: 'obf', + Flavor.openPetFoodFacts: 'opff', + Flavor.openProductFacts: 'opf', + Flavor.openFoodProductFactsPro: 'off-pro', +}; diff --git a/lib/src/nutripatrol/get_ticket.dart b/lib/src/nutripatrol/get_ticket.dart new file mode 100644 index 0000000000..1183a713ba --- /dev/null +++ b/lib/src/nutripatrol/get_ticket.dart @@ -0,0 +1,50 @@ +import 'package:json_annotation/json_annotation.dart'; +import 'package:openfoodfacts/openfoodfacts.dart'; +import 'package:openfoodfacts/src/utils/nutripatrol_types.dart'; + +import '../interface/json_object.dart'; + +part 'get_ticket.g.dart'; + +@JsonSerializable() +class NutripatrolTicket extends JsonObject { + /// Flag ID. Read-only. + @JsonKey() + late int id; + + /// Barcode of the product. Read-only. + @JsonKey() + String? barcode; + + /// Type of the ticket. + @JsonKey() + late NutripatrolType type; + + /// Url of the ticket. Read-only. + @JsonKey() + late String url; + + /// Status of the ticket. + @JsonKey() + late NutripatrolTicketStatus status; + + /// Image id of the ticket. Read-only. + @JsonKey(name: 'image_id') + String? imageId; + + /// Flavor of the ticket. + @JsonKey() + late Flavor flavor; + + /// created date of the ticket. Read-only. + @JsonKey(name: 'created_at') + late DateTime createdAt; + + NutripatrolTicket(); + + factory NutripatrolTicket.fromJson(Map json) => + _$NutripatrolTicketFromJson(json); + + @override + Map toJson() => _$NutripatrolTicketToJson(this); +} diff --git a/lib/src/nutripatrol/get_ticket.g.dart b/lib/src/nutripatrol/get_ticket.g.dart new file mode 100644 index 0000000000..dfd21e09b0 --- /dev/null +++ b/lib/src/nutripatrol/get_ticket.g.dart @@ -0,0 +1,49 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'get_ticket.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +NutripatrolTicket _$NutripatrolTicketFromJson(Map json) => + NutripatrolTicket() + ..id = (json['id'] as num).toInt() + ..barcode = json['barcode'] as String? + ..type = $enumDecode(_$NutripatrolTypeEnumMap, json['type']) + ..url = json['url'] as String + ..status = $enumDecode(_$NutripatrolTicketStatusEnumMap, json['status']) + ..imageId = json['image_id'] as String? + ..flavor = $enumDecode(_$FlavorEnumMap, json['flavor']) + ..createdAt = DateTime.parse(json['created_at'] as String); + +Map _$NutripatrolTicketToJson(NutripatrolTicket instance) => + { + 'id': instance.id, + 'barcode': instance.barcode, + 'type': _$NutripatrolTypeEnumMap[instance.type]!, + 'url': instance.url, + 'status': _$NutripatrolTicketStatusEnumMap[instance.status]!, + 'image_id': instance.imageId, + 'flavor': _$FlavorEnumMap[instance.flavor]!, + 'created_at': instance.createdAt.toIso8601String(), + }; + +const _$NutripatrolTypeEnumMap = { + NutripatrolType.image: 'image', + NutripatrolType.product: 'product', + NutripatrolType.search: 'search', +}; + +const _$NutripatrolTicketStatusEnumMap = { + NutripatrolTicketStatus.open: 'open', + NutripatrolTicketStatus.closed: 'closed', +}; + +const _$FlavorEnumMap = { + Flavor.openFoodFacts: 'off', + Flavor.openBeautyFacts: 'obf', + Flavor.openPetFoodFacts: 'opff', + Flavor.openProductFacts: 'opf', + Flavor.openFoodProductFactsPro: 'off-pro', +}; diff --git a/lib/src/nutripatrol/get_tickets.dart b/lib/src/nutripatrol/get_tickets.dart new file mode 100644 index 0000000000..0b0e52b9f9 --- /dev/null +++ b/lib/src/nutripatrol/get_tickets.dart @@ -0,0 +1,25 @@ +import 'package:json_annotation/json_annotation.dart'; +import 'get_ticket.dart'; + +import '../interface/json_object.dart'; + +part 'get_tickets.g.dart'; + +@JsonSerializable() +class NutripatrolTickets extends JsonObject { + /// List of Tickets + @JsonKey() + late List tickets; + + /// Max Page + @JsonKey(name: 'max_page') + late int maxPage; + + NutripatrolTickets(); + + factory NutripatrolTickets.fromJson(Map json) => + _$NutripatrolTicketsFromJson(json); + + @override + Map toJson() => _$NutripatrolTicketsToJson(this); +} diff --git a/lib/src/nutripatrol/get_tickets.g.dart b/lib/src/nutripatrol/get_tickets.g.dart new file mode 100644 index 0000000000..505197e294 --- /dev/null +++ b/lib/src/nutripatrol/get_tickets.g.dart @@ -0,0 +1,20 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'get_tickets.dart'; + +// ************************************************************************** +// JsonSerializableGenerator +// ************************************************************************** + +NutripatrolTickets _$NutripatrolTicketsFromJson(Map json) => + NutripatrolTickets() + ..tickets = (json['tickets'] as List) + .map((e) => NutripatrolTicket.fromJson(e as Map)) + .toList() + ..maxPage = (json['max_page'] as num).toInt(); + +Map _$NutripatrolTicketsToJson(NutripatrolTickets instance) => + { + 'tickets': instance.tickets, + 'max_page': instance.maxPage, + }; diff --git a/lib/src/nutripatrol/update_ticket_status.dart b/lib/src/nutripatrol/update_ticket_status.dart new file mode 100644 index 0000000000..0062eebc71 --- /dev/null +++ b/lib/src/nutripatrol/update_ticket_status.dart @@ -0,0 +1,42 @@ +// import 'package:json_annotation/json_annotation.dart'; + +// part 'update_ticket_status.g.dart'; + + +// @JsonSerializable() +// class UpdateTicketStatus { +// /// Barcode of the product, if any +// @JsonKey() +// String? barcode; + +// /// Type of the ticket +// @JsonKey() +// late String type; + +// /// URL associated with the ticket +// @JsonKey() +// late String url; + +// /// Status of the ticket +// @JsonKey() +// late String status; + +// /// Image ID associated with the ticket +// @JsonKey(name: 'image_id') +// String? imageId; + +// /// Flavor of the ticket +// @JsonKey() +// late String flavor; + +// /// Created date of the ticket +// @JsonKey(name: 'created_at') +// late DateTime createdAt; + +// UpdateTicketStatus(); + +// factory UpdateTicketStatus.fromJson(Map json) => +// _$UpdateTicketStatusFromJson(json); + +// Map toJson() => _$UpdateTicketStatusToJson(this); +// } diff --git a/lib/src/nutripatrol_api_client.dart b/lib/src/nutripatrol_api_client.dart new file mode 100644 index 0000000000..c671108987 --- /dev/null +++ b/lib/src/nutripatrol_api_client.dart @@ -0,0 +1,191 @@ +import 'dart:convert'; + +import 'package:http/http.dart'; +import 'package:openfoodfacts/openfoodfacts.dart'; +import 'nutripatrol/create_flag.dart'; +import 'nutripatrol/get_tickets.dart'; +import 'prices/maybe_error.dart'; +import 'utils/nutripatrol_types.dart'; +import 'utils/http_helper.dart'; +import 'utils/open_food_api_configuration.dart'; +import 'utils/uri_helper.dart'; + +import 'nutripatrol/get_ticket.dart'; + +/// Client calls of the Nutripatrol API. +/// +/// cf. [Nutripatrol](https://nutripatrol.openfoodfacts.org/api/docs) +class NutripatrolApiClient { + NutripatrolApiClient._(); + + /// Subdomain of the Nutripatrol API. + static const String _subdomain = 'nutripatrol'; + + static String _getHost(final UriProductHelper uriHelper) => + uriHelper.getHost(_subdomain); + + static Uri getUri({ + required final String path, + final Map? queryParameters, + final UriProductHelper uriHelper = uriHelperFoodProd, + final bool? addUserAgentParameters, + }) => + uriHelper.getUri( + path: path, + queryParameters: queryParameters, + forcedHost: _getHost(uriHelper), + addUserAgentParameters: addUserAgentParameters, + ); + + /// Get a ticket by its ID. + /// + /// [ticketId] is the ID of the ticket. + static Future> getTicket({ + required final int ticketId, + final UriProductHelper uriHelper = uriHelperFoodProd, + }) async { + assert(ticketId >= 0, "The id must be >= 0"); + final Uri uri = getUri( + path: '/api/v1/tickets/$ticketId', + uriHelper: uriHelper, + ); + final Response response = await HttpHelper().doGetRequest( + uri, + uriHelper: uriHelper, + ); + if (response.statusCode == 200) { + try { + final dynamic decodedResponse = HttpHelper().jsonDecodeUtf8(response); + return MaybeError.value( + NutripatrolTicket.fromJson(decodedResponse)); + } catch (_) { + // + } + } + return MaybeError.responseError(response); + } + + /// Get all tickets. + static Future> getTickets({ + final NutripatrolTicketStatus status = NutripatrolTicketStatus.open, + final NutripatrolType type = NutripatrolType.image, + final int? page, + final int? pageSize, + final UriProductHelper uriHelper = uriHelperFoodProd, + }) async { + final Map queryParameters = {}; + queryParameters['status'] = status.toString().split('.').last; + queryParameters['type_'] = type.toString().split('.').last; + if (page != null) queryParameters['page'] = page.toString(); + if (pageSize != null) queryParameters['page_size'] = pageSize.toString(); + + final Uri uri = getUri( + path: '/api/v1/tickets', + queryParameters: queryParameters, + uriHelper: uriHelper, + ); + final Response response = await HttpHelper().doGetRequest( + uri, + uriHelper: uriHelper, + ); + if (response.statusCode == 200) { + try { + final dynamic decodedResponse = HttpHelper().jsonDecodeUtf8(response); + final List tickets = decodedResponse['tickets']; + return MaybeError.value( + NutripatrolTickets.fromJson({ + 'tickets': tickets, + 'max_page': decodedResponse['max_page'], + }), + ); + } catch (_) { + // + } + } + return MaybeError.responseError(response); + } + + /// Create a Flag for a product. + /// + /// [flag] is the flag to create. + static Future> createFlag({ + required final String barcode, + required final String comment, + required final double confidence, + required final String imageId, + required final NutripatrolFlagReason reason, + required final NutripatrolType type, + required final String url, + required final String userId, + required final String deviceId, + required final NutripatrolSource source, + required final Flavor flavor, + final UriProductHelper uriHelper = uriHelperFoodProd, + }) async { + final Uri uri = getUri( + path: '/api/v1/flags', + uriHelper: uriHelper, + ); + final Response response = await HttpHelper().doPostJsonRequest( + uri, + jsonEncode({ + 'barcode': barcode, + 'comment': comment, + 'confidence': confidence, + 'image_id': imageId, + 'reason': reason.toString().split('.').last, + 'type': type.toString().split('.').last, + 'url': url, + 'user_id': userId, + 'device_id': deviceId, + 'source': source.toString().split('.').last, + 'flavor': flavor.offTag, + }), + uriHelper: uriHelper, + bearerToken: ""); + if (response.statusCode == 201) { + try { + final dynamic decodedResponse = HttpHelper().jsonDecodeUtf8(response); + return MaybeError.value( + CreateNutripatrolFlag.fromJson(decodedResponse)); + } catch (_) { + // Handle parsing errors if necessary + } + } + return MaybeError.responseError(response); + } + + /// Update a Ticket Status by its ID. + /// + /// [ticketId] is the ID of the ticket. + static Future> updateTicketStatus({ + required final int ticketId, + required final NutripatrolTicketStatus status, + final UriProductHelper uriHelper = uriHelperFoodProd, + }) async { + assert(ticketId >= 0, "The id must be >= 0"); + final Uri uri = getUri( + path: '/api/v1/tickets/$ticketId/status', + uriHelper: uriHelper, + ); + final Map body = { + 'ticket_id': ticketId, + 'status': status.toString().split('.').last, + }; + final Response response = await HttpHelper().doPutRequest( + uri, + jsonEncode(body), + uriHelper: uriHelper, + ); + if (response.statusCode == 200) { + try { + final dynamic decodedResponse = HttpHelper().jsonDecodeUtf8(response); + return MaybeError.value( + NutripatrolTicket.fromJson(decodedResponse)); + } catch (_) { + // + } + } + return MaybeError.responseError(response); + } +} diff --git a/lib/src/utils/nutripatrol_types.dart b/lib/src/utils/nutripatrol_types.dart new file mode 100644 index 0000000000..6e2e470407 --- /dev/null +++ b/lib/src/utils/nutripatrol_types.dart @@ -0,0 +1,49 @@ +import 'package:json_annotation/json_annotation.dart'; + + +enum NutripatrolSource { + @JsonValue('mobile') + mobile, + + @JsonValue('web') + web, + + @JsonValue('robotoff') + robotoff, +} + +/// Enum for ticket type +enum NutripatrolType { + @JsonValue('image') + image, + + @JsonValue('product') + product, + + @JsonValue('search') + search +} + +/// Enum for ticket status +enum NutripatrolTicketStatus { + @JsonValue('open') + open, + + @JsonValue('closed') + closed +} + +/// Enum for flag reason +enum NutripatrolFlagReason { + @JsonValue('inappropriate') + inappropriate, + + @JsonValue('human') + human, + + @JsonValue('beauty') + beauty, + + @JsonValue('other') + other +}