87 lines
2.1 KiB
Dart
87 lines
2.1 KiB
Dart
/* SPDX-License-Identifier: GPL-3.0-or-later */
|
|
/* Copyright (C) 2024 Marco Groß <mgross@sw-gross.de> */
|
|
import 'dart:convert';
|
|
import 'dart:developer';
|
|
import 'dart:io';
|
|
|
|
class FoodFactLookupClient {
|
|
FoodFactLookupClient();
|
|
|
|
static const String url = "https://world.openfoodfacts.org/api/v3/product/";
|
|
|
|
String getProductUrl(String ean) {
|
|
return "$url$ean.json";
|
|
}
|
|
|
|
Future<FoodFactResponse> retrieveFoodInfo(String ean) async {
|
|
HttpClient client = HttpClient();
|
|
String asString = "";
|
|
|
|
try {
|
|
var request = await client.getUrl(Uri.parse(getProductUrl(ean)));
|
|
|
|
var response = await request.close();
|
|
|
|
if (response.statusCode != HttpStatus.ok) {
|
|
return FoodFactResponse(
|
|
food: null, status: FoodFactResponseStatus.barcodeNotFound);
|
|
}
|
|
|
|
asString = await response.transform(utf8.decoder).join();
|
|
} on SocketException {
|
|
return FoodFactResponse(
|
|
food: null,
|
|
status: FoodFactResponseStatus.foodFactServerNotReachable);
|
|
} catch (e) {
|
|
log(e.toString());
|
|
} finally {
|
|
client.close();
|
|
}
|
|
|
|
return FoodFactResponse(
|
|
food: FoodFactModel.fromJson(jsonDecode(asString)),
|
|
status: FoodFactResponseStatus.ok);
|
|
}
|
|
}
|
|
|
|
class FoodFactModel {
|
|
final String name;
|
|
final int kcalPer100g;
|
|
final int mass;
|
|
|
|
FoodFactModel({
|
|
required this.name,
|
|
required this.mass,
|
|
required this.kcalPer100g,
|
|
});
|
|
|
|
factory FoodFactModel.fromJson(Map<String, dynamic> json) {
|
|
String quantityString = json['product']['product_quantity'] ?? "0";
|
|
int quantity;
|
|
|
|
try {
|
|
quantity = int.parse(quantityString);
|
|
} catch (e) {
|
|
quantity = 0;
|
|
}
|
|
|
|
return FoodFactModel(
|
|
name: json['product']['product_name'],
|
|
kcalPer100g: json['product']['nutriments']['energy-kcal_100g'],
|
|
mass: quantity);
|
|
}
|
|
}
|
|
|
|
enum FoodFactResponseStatus {
|
|
foodFactServerNotReachable,
|
|
barcodeNotFound,
|
|
ok,
|
|
}
|
|
|
|
class FoodFactResponse {
|
|
final FoodFactModel? food;
|
|
final FoodFactResponseStatus status;
|
|
|
|
FoodFactResponse({required this.food, required this.status});
|
|
}
|