forked from openfoodfacts/smooth-app
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feature/#21 - automatically storing keywords and group results into t…
…he database ... and automagically asking the end-user if they want to see the cache New files: * `dao_product.dart` * `dao_product_list.dart` * `database_product_list_supplier.dart` * `product_list.dart` * `product_list_supplier.dart` * `product_query_page_helper.dart` * `query_product_list_supplier.dart` Impacted files: * `choose_page.dart`: now uses `ProductQueryPageHelper` in order to get database or http results; refactored * `continuous_scan_model.dart`: minor refactoring due to DAO * `group_product_query.dart`: now implements new method `getProductList` * `keywords_product_query.dart`: now implements new method `getProductList` * `local_database.dart`: now it's version 2; refactored using the new DAO classes * `product_query.dart`: new method `getProductList` * `product_query_model.dart`: now we use a `ProductListSupplier` instead of a `ProductQuery` * `product_query_page.dart`: now we use a `ProductListSupplier` instead of a `ProductQuery`
- Loading branch information
1 parent
5faab97
commit 43fda6e
Showing
15 changed files
with
688 additions
and
166 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
34 changes: 34 additions & 0 deletions
34
packages/smooth_app/lib/data_models/database_product_list_supplier.dart
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
import 'package:smooth_app/data_models/product_list_supplier.dart'; | ||
import 'package:smooth_app/database/dao_product_list.dart'; | ||
import 'package:smooth_app/database/local_database.dart'; | ||
import 'package:smooth_app/data_models/product_list.dart'; | ||
import 'package:smooth_app/database/product_query.dart'; | ||
|
||
class DatabaseProductListSupplier implements ProductListSupplier { | ||
DatabaseProductListSupplier(this.productQuery, this.localDatabase); | ||
|
||
final ProductQuery productQuery; | ||
final LocalDatabase localDatabase; | ||
ProductList _productList; | ||
|
||
@override | ||
ProductList getProductList() => _productList; | ||
|
||
@override | ||
Future<String> asyncLoad() async { | ||
try { | ||
final ProductList productList = productQuery.getProductList(); | ||
final bool result = await DaoProductList(localDatabase).get(productList); | ||
if (!result) { | ||
return 'unexpected empty record'; | ||
} | ||
_productList = productList; | ||
return null; | ||
} catch (e) { | ||
return e.toString(); | ||
} | ||
} | ||
|
||
@override | ||
bool needsToBeSavedIntoDb() => false; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
import 'package:flutter/material.dart'; | ||
import 'package:openfoodfacts/model/Product.dart'; | ||
|
||
class ProductList { | ||
ProductList({ | ||
@required this.listType, | ||
@required this.parameters, | ||
}); | ||
|
||
final String listType; | ||
final String parameters; | ||
|
||
final List<String> _barcodes = <String>[]; | ||
final Map<String, Product> _products = <String, Product>{}; | ||
|
||
static const String LIST_TYPE_HTTP_SEARCH_GROUP = 'http/search/group'; | ||
static const String LIST_TYPE_HTTP_SEARCH_KEYWORDS = 'http/search/keywords'; | ||
|
||
List<String> get barcodes => _barcodes; | ||
|
||
bool isEmpty() => _barcodes.isEmpty; | ||
|
||
void clear() { | ||
_barcodes.clear(); | ||
_products.clear(); | ||
} | ||
|
||
void add(final Product product) { | ||
if (product == null) { | ||
throw Exception('null product'); | ||
} | ||
final String barcode = product.barcode; | ||
if (barcode == null) { | ||
throw Exception('null barcode'); | ||
} | ||
_barcodes.add(barcode); | ||
_products[barcode] = product; | ||
} | ||
|
||
void addAll(final List<Product> products) => products.forEach(add); | ||
|
||
void set( | ||
final List<String> barcodes, | ||
final Map<String, Product> products, | ||
) { | ||
clear(); | ||
_barcodes.addAll(barcodes); | ||
_products.addAll(products); | ||
} | ||
|
||
List<Product> getList() { | ||
final List<Product> result = <Product>[]; | ||
for (final String barcode in _barcodes) { | ||
final Product product = _products[barcode]; | ||
if (product == null) { | ||
throw Exception('no product for barcode $barcode'); | ||
} | ||
result.add(product); | ||
} | ||
return result; | ||
} | ||
} |
10 changes: 10 additions & 0 deletions
10
packages/smooth_app/lib/data_models/product_list_supplier.dart
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
import 'package:smooth_app/data_models/product_list.dart'; | ||
|
||
abstract class ProductListSupplier { | ||
/// returns null if OK, or the message error | ||
Future<String> asyncLoad(); | ||
|
||
ProductList getProductList(); | ||
|
||
bool needsToBeSavedIntoDb(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
29 changes: 29 additions & 0 deletions
29
packages/smooth_app/lib/data_models/query_product_list_supplier.dart
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
import 'package:smooth_app/data_models/product_list_supplier.dart'; | ||
import 'package:smooth_app/data_models/product_list.dart'; | ||
import 'package:smooth_app/database/product_query.dart'; | ||
import 'package:openfoodfacts/model/SearchResult.dart'; | ||
|
||
class QueryProductListSupplier implements ProductListSupplier { | ||
QueryProductListSupplier(this.productQuery); | ||
|
||
final ProductQuery productQuery; | ||
ProductList _productList; | ||
|
||
@override | ||
ProductList getProductList() => _productList; | ||
|
||
@override | ||
Future<String> asyncLoad() async { | ||
try { | ||
final SearchResult searchResult = await productQuery.getSearchResult(); | ||
_productList = productQuery.getProductList(); | ||
_productList.addAll(searchResult.products); | ||
return null; | ||
} catch (e) { | ||
return e.toString(); | ||
} | ||
} | ||
|
||
@override | ||
bool needsToBeSavedIntoDb() => true; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
import 'dart:async'; | ||
import 'dart:convert'; | ||
import 'package:smooth_app/database/local_database.dart'; | ||
import 'package:sqflite/sqflite.dart'; | ||
import 'package:openfoodfacts/model/Product.dart'; | ||
|
||
class DaoProduct { | ||
DaoProduct(this.localDatabase); | ||
|
||
final LocalDatabase localDatabase; | ||
|
||
static const String TABLE_PRODUCT = 'product'; | ||
static const String TABLE_PRODUCT_COLUMN_BARCODE = 'barcode'; | ||
static const String _TABLE_PRODUCT_COLUMN_JSON = 'encoded_json'; | ||
|
||
static FutureOr<void> onUpgrade( | ||
final Database db, | ||
final int oldVersion, | ||
final int newVersion, | ||
) async { | ||
if (oldVersion < 1) { | ||
await db.execute('create table $TABLE_PRODUCT(' | ||
'$TABLE_PRODUCT_COLUMN_BARCODE TEXT PRIMARY KEY,' | ||
'$_TABLE_PRODUCT_COLUMN_JSON TEXT NOT NULL,' | ||
'${LocalDatabase.COLUMN_TIMESTAMP} INT NOT NULL' | ||
')'); | ||
} | ||
} | ||
|
||
Future<Product> get(final String barcode) async { | ||
final List<Map<String, dynamic>> queryResult = | ||
await localDatabase.database.query( | ||
TABLE_PRODUCT, | ||
columns: <String>[_TABLE_PRODUCT_COLUMN_JSON], | ||
where: '$TABLE_PRODUCT_COLUMN_BARCODE = ?', | ||
whereArgs: <String>[barcode], | ||
); | ||
if (queryResult.isEmpty) { | ||
// not found | ||
return null; | ||
} | ||
if (queryResult.length > 1) { | ||
// very very unlikely to happen | ||
throw Exception('Several products with the same barcode $barcode'); | ||
} | ||
return _getProductFromQueryResult(queryResult[0]); | ||
} | ||
|
||
Future<Map<String, Product>> getAll(final List<String> barcodes) async { | ||
final Map<String, Product> result = <String, Product>{}; | ||
if (barcodes == null || barcodes.isEmpty) { | ||
return result; | ||
} | ||
final List<Map<String, dynamic>> queryResults = | ||
await localDatabase.database.query( | ||
TABLE_PRODUCT, | ||
columns: <String>[ | ||
TABLE_PRODUCT_COLUMN_BARCODE, | ||
_TABLE_PRODUCT_COLUMN_JSON, | ||
], | ||
where: | ||
'$TABLE_PRODUCT_COLUMN_BARCODE in(? ${',?' * (barcodes.length - 1)})', | ||
whereArgs: barcodes, | ||
); | ||
if (queryResults.isEmpty) { | ||
return result; | ||
} | ||
for (final Map<String, dynamic> row in queryResults) { | ||
result[row[TABLE_PRODUCT_COLUMN_BARCODE] as String] = | ||
_getProductFromQueryResult(row); | ||
} | ||
return result; | ||
} | ||
|
||
Future<void> put(final Product product) async => | ||
await _put(product, localDatabase.database); | ||
|
||
Future<void> putProducts(final List<Product> products) async => | ||
await localDatabase.database | ||
.transaction((final Transaction transaction) async { | ||
for (final Product product in products) { | ||
await _put(product, transaction); | ||
} | ||
}); | ||
|
||
static Future<void> _put( | ||
final Product product, | ||
final DatabaseExecutor databaseExecutor, | ||
) async { | ||
await databaseExecutor.execute( | ||
'insert into $TABLE_PRODUCT(' | ||
' $TABLE_PRODUCT_COLUMN_BARCODE,' | ||
' $_TABLE_PRODUCT_COLUMN_JSON,' | ||
' ${LocalDatabase.COLUMN_TIMESTAMP}' | ||
')values(?, ?, ?)' | ||
' on conflict($TABLE_PRODUCT_COLUMN_BARCODE) DO UPDATE SET ' | ||
' $_TABLE_PRODUCT_COLUMN_JSON=excluded.$_TABLE_PRODUCT_COLUMN_JSON,' | ||
' ${LocalDatabase.COLUMN_TIMESTAMP}=excluded.${LocalDatabase.COLUMN_TIMESTAMP}', | ||
<dynamic>[ | ||
product.barcode, | ||
json.encode(product.toJson()), | ||
LocalDatabase.nowInMillis(), | ||
]); // TODO(monsieurtanuki): check if this upsert does not cause delete+insert, but just update | ||
} | ||
|
||
Product _getProductFromQueryResult(final Map<String, dynamic> row) { | ||
final String encodedJson = row[_TABLE_PRODUCT_COLUMN_JSON] as String; | ||
final Map<String, dynamic> decodedJson = | ||
json.decode(encodedJson) as Map<String, dynamic>; | ||
return Product.fromJson(decodedJson); | ||
} | ||
} |
Oops, something went wrong.