Skip to content

Commit

Permalink
Enhancement 12 (#15)
Browse files Browse the repository at this point in the history
* Update 'strings2ReceiptProducts' (notice campaign rows)

* Add 'ansicolor' dependency & use it in 'eanHandler'

* Update 'eanHandler' (better logic for selecting from multiple products)

* Update 'eanHandler'

Temporarily comment problematic parts before a proper fix

* Initially add 'hive' dependency

A plan: save an asked product to a local Hive database

Continue later to implement this feature

* Add 2 generator dependencies

* Add 'HiveProduct' class & generated adapter

* Initially update 'eanHandler'

Starting point for using Hive

* Update readme

Add section for generating 'hive_product.g.dart' file

* Update 'eanHandler' & '_handleFoundCases'

Continue to implement hive_product handling (saving names to the local database)

* Add 'ansipen_helper.dart'

For refactoring usage

* Update 'ansipen_helper.dart'

Add 'peachPen' method

* Update 'ean_handler.dart'

- Refactor ansiPens
- Update '_filterEANProducts' method
- Add '_initializeHiveProducts' & '_filterHiveProducts' methods
- Add 'nonFoundReceiptProducts2' list helper

* Cleaning in 'ean_handler.dart'
  • Loading branch information
areee committed Apr 24, 2022
1 parent db073a1 commit b4811fa
Show file tree
Hide file tree
Showing 9 changed files with 584 additions and 47 deletions.
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,4 +92,12 @@ If you want to get an easier command, you can create an alias into Zsh or Bash p

```
alias kassakuitti='dart run $HOME/Documents/dart_kassakuitti_cli/bin/dart_kassakuitti_cli.dart'
```

## Generate `hive_product.g.dart` file

You can generate `hive_product.g.dart` file by running:

```
dart run build_runner build
```
2 changes: 1 addition & 1 deletion bin/dart_kassakuitti_cli.dart
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ void main(List<String> arguments) async {
var eanProducts = await readEANProducts(
selectedHtmlFile, ShopSelector.sKaupat, csvFilesPath);

eanHandler(receiptProducts, eanProducts.toList());
await eanHandler(receiptProducts, eanProducts.toList());

receiptProducts2CSV(receiptProducts, csvFilesPath);
eanProducts2CSV(eanProducts, csvFilesPath, ShopSelector.sKaupat.name);
Expand Down
19 changes: 19 additions & 0 deletions bin/models/hive_product.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import 'package:hive/hive.dart';

part 'hive_product.g.dart';

@HiveType(typeId: 0)
class HiveProduct extends HiveObject {
@HiveField(0)
String receiptName;

@HiveField(1)
String eanName;

HiveProduct({required this.receiptName, required this.eanName});

@override
String toString() {
return 'ReceiptName: $receiptName, eanName: $eanName';
}
}
44 changes: 44 additions & 0 deletions bin/models/hive_product.g.dart

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

151 changes: 114 additions & 37 deletions bin/specific/s_kaupat/ean_handler.dart
Original file line number Diff line number Diff line change
@@ -1,22 +1,42 @@
import 'dart:io';

import 'package:hive/hive.dart';

import '../../models/ean_product.dart';
import '../../models/hive_product.dart';
import '../../models/receipt_product.dart';
import '../../utils/ansipen_helper.dart';

const _kHiveBoxName = 'hiveProducts';

/// Handles the EAN products.
Future<void> eanHandler(
List<ReceiptProduct> receiptProducts, List<EANProduct> eanProducts) async {
List<ReceiptProduct> nonFoundReceiptProducts = [];
List<ReceiptProduct> nonFoundReceiptProducts2 = [];

Box<HiveProduct> hiveProducts = await _initializeHiveProducts();

void eanHandler(
List<ReceiptProduct> receiptProducts, List<EANProduct> eanProducts) {
print('\nThe first round begins!');
print('Statistics: ${receiptProducts.length} receiptProducts, '
print('Statistics:');
print(peachPen().write('Amount of hive products: ${hiveProducts.length}'));
print('${receiptProducts.length} receiptProducts, '
'${eanProducts.length} eanProducts\n');
List<ReceiptProduct> nonFoundReceiptProducts = [];

for (var receiptProduct in receiptProducts) {
print(receiptProduct);

String? eanProductName =
_filterHiveProducts(hiveProducts.values, receiptProduct.name);
print(peachPen().write('EAN product name: $eanProductName'));

var filteredEanProducts =
_filterEANProducts(receiptProduct.name, eanProducts);
_filterEANProducts(receiptProduct.name, eanProducts, eanProductName);

_handleFoundCases(
receiptProduct, filteredEanProducts, eanProducts, hiveProducts);

_handleFoundCases(receiptProduct, filteredEanProducts, eanProducts);
print(peachPen().write('Amount of hive products: ${hiveProducts.length}'));

if (filteredEanProducts.isEmpty) {
print('\tNo product found for the 1st round.');
Expand All @@ -39,55 +59,112 @@ void eanHandler(
continue;
}

var filteredEanProducts =
_filterEANProducts(splittedReceiptProcuctNames[0], eanProducts);
String? eanProductName =
_filterHiveProducts(hiveProducts.values, nonFoundReceiptProduct.name);
print(peachPen().write('EAN product name: $eanProductName'));

if (filteredEanProducts.length > 2 &&
splittedReceiptProcuctNames.length > 1) {
filteredEanProducts =
_filterEANProducts(splittedReceiptProcuctNames[1], eanProducts);
}
var filteredEanProducts = _filterEANProducts(
splittedReceiptProcuctNames[0], eanProducts, eanProductName);

_handleFoundCases(nonFoundReceiptProduct, filteredEanProducts, eanProducts);
_handleFoundCases(
nonFoundReceiptProduct, filteredEanProducts, eanProducts, hiveProducts);

if (filteredEanProducts.isEmpty) {
print('\tNo product found for the 2nd round.');
nonFoundReceiptProducts2.add(nonFoundReceiptProduct);
}
}

print('\nFinished!');
print('Only ${eanProducts.length} unknown eanProducts left.');
print('${nonFoundReceiptProducts2.length < 10 ? 'Only ' : ''}'
'${nonFoundReceiptProducts2.length} unknown receipt '
'${nonFoundReceiptProducts2.length == 1 ? 'product' : 'products'} '
'left.');
print(peachPen().write('Amount of hive products: ${hiveProducts.length}'));

hiveProducts.close();
}

List<EANProduct> _filterEANProducts(
String receiptProductName, List<EANProduct> eanProducts) {
String? _filterHiveProducts(
Iterable<HiveProduct> hiveProductsValues, String receiptProductName) {
String? eanProductName;

var filteredHiveProducts = hiveProductsValues
.where((hiveProduct) => hiveProduct.receiptName == receiptProductName);
/*
If the receipt product is already in the hive products,
get the ean product name from the hive product.
*/
if (filteredHiveProducts.isNotEmpty) {
print(greenPen().write(
'\tFound ${filteredHiveProducts.length} pcs in the hive products!'));
eanProductName = filteredHiveProducts.first.eanName;
print('\teanProductName in Hive: $eanProductName');
}
return eanProductName;
}

Future<Box<HiveProduct>> _initializeHiveProducts() async {
Hive.init(Directory.current.path);
Hive.registerAdapter(HiveProductAdapter());
return await Hive.openBox<HiveProduct>(_kHiveBoxName);
}

List<EANProduct> _filterEANProducts(String receiptProductName,
List<EANProduct> eanProducts, String? eanProductName) {
/*
If eanProductName is not null,
filter the ean products by the ean product name.
*/
if (eanProductName != null) {
return eanProducts
.where((eanProduct) =>
eanProduct.name.toLowerCase() == eanProductName.toLowerCase())
.toList();
}
return eanProducts
.where((eanProduct) =>
eanProduct.name.toLowerCase().contains(receiptProductName))
.where((eanProduct) => eanProduct.name
.toLowerCase()
.contains(receiptProductName.toLowerCase()))
.toList();
}

void _handleFoundCases(ReceiptProduct receiptProduct,
List<EANProduct> filteredEanProducts, List<EANProduct> origEanProducts) {
void _handleFoundCases(
ReceiptProduct receiptProduct,
List<EANProduct> filteredEanProducts,
List<EANProduct> origEanProducts,
Box<HiveProduct> hiveProducts) {
if (filteredEanProducts.length == 1) {
print('\tFound one product:');
print('\t\t${filteredEanProducts[0]}');
print(greenPen().write('\tFound one product:'));
print('\t\t${filteredEanProducts.first}');

receiptProduct.eanCode = filteredEanProducts[0].ean;
origEanProducts.remove(filteredEanProducts[0]);
receiptProduct.eanCode = filteredEanProducts.first.ean;
} else if (filteredEanProducts.length > 1) {
print(
'\tFound multiple products (${filteredEanProducts.length} possible choices):');

for (var filteredReceiptProduct in filteredEanProducts) {
print('\t\t$filteredReceiptProduct');
print('\t\tIs this the right product? (y/n)');
var answer = stdin.readLineSync();
if (answer?.toLowerCase() == 'y') {
receiptProduct.eanCode = filteredReceiptProduct.ean;
origEanProducts.remove(filteredReceiptProduct);
break;
}
print(redPen()
.write('\tFound multiple products (${filteredEanProducts.length}'
' possible choices):'));

for (var i = 0; i < filteredEanProducts.length; i++) {
print('\t\t${i + 1}. ${filteredEanProducts[i]}');
}
print('\nPlease enter the number of the product you want to select: ');
var answer = stdin.readLineSync();

if (answer!.isNotEmpty &&
int.parse(answer) <= filteredEanProducts.length &&
int.parse(answer) > 0) {
var selectedEanProduct = filteredEanProducts[int.parse(answer) - 1];

print(greenPen().write('\tYou selected: $selectedEanProduct'));

receiptProduct.eanCode = selectedEanProduct.ean;

hiveProducts.add(HiveProduct(
receiptName: receiptProduct.name, eanName: selectedEanProduct.name));
print(
peachPen().write('Amount of hive products: ${hiveProducts.length}'));
} else {
print('\tNo product selected.');
}
}
}
48 changes: 39 additions & 9 deletions bin/specific/s_kaupat/strings_to_receipt_products.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,24 @@ import '../../utils/extensions/double_extension.dart';
import '../../utils/line_helper.dart';
import '../../models/receipt_product.dart';

/// Goes through the list of lines and returns a list of products.
List<ReceiptProduct> strings2ReceiptProducts(List<String> lines) {
/// Goes through the list of rows and returns a list of products.
List<ReceiptProduct> strings2ReceiptProducts(List<String> rows) {
var helper = LineHelper();
List<ReceiptProduct> products = [];

for (var item in lines) {
for (var item in rows) {
item = item.trim();
item = item.toLowerCase();

// Do not handle sum lines (after a row of strokes):
if (item.contains('----------')) {
break;
}
// Refund line:
// Refund row:
else if (item.contains('palautus')) {
helper.previousLine = PreviousLine.refund;
}
// When previous line was a refund line, skip next two lines:
// When the previous row was a refund row, skip next two rows:
else if (helper.previousLine == PreviousLine.refund) {
if (helper.calcLines != 1) {
helper.calcLines++;
Expand All @@ -28,7 +28,7 @@ List<ReceiptProduct> strings2ReceiptProducts(List<String> lines) {
helper.previousLine = PreviousLine.notSet;
}
}
// A discount line:
// A discount row:
else if (item.contains('alennus')) {
var items = item.split(RegExp(r'\s{12,33}'));
var discountPrice =
Expand All @@ -42,7 +42,7 @@ List<ReceiptProduct> strings2ReceiptProducts(List<String> lines) {
var discountedPrice =
(origTotalPriceAsDouble - discountPriceAsDouble).toPrecision(2);
var discountedPriceAsString =
discountedPrice.toStringAsFixed(2).replaceAll(RegExp(r'\.'), ',');
discountedPrice.toString().replaceAll(RegExp(r'\.'), ',');

if (lastProduct.quantity > 1) {
var discountedPricePerUnit = (discountedPrice / lastProduct.quantity)
Expand All @@ -55,7 +55,37 @@ List<ReceiptProduct> strings2ReceiptProducts(List<String> lines) {
lastProduct.totalPrice = discountedPriceAsString;
lastProduct.discountCounted = 'yes';
}
// If a line starts with a digit, it is a quantity and price per unit row:
/*
A campaign row
(i.e. usually means that there's a mistake in the previous line):
*/
else if (item.contains('kampanja')) {
var items = item.split(RegExp(r'\s{12,33}'));
var campaignPrice =
items[1].replaceAll(RegExp(r'\-'), '').replaceAll(RegExp(r','), '.');
var campaignPriceAsDouble = double.parse(campaignPrice);

var lastProduct = products.last;
var origTotalPrice = lastProduct.totalPrice.replaceAll(RegExp(r','), '.');
var origTotalPriceAsDouble = double.parse(origTotalPrice);

var fixedPrice =
(origTotalPriceAsDouble - campaignPriceAsDouble).toPrecision(2);
var fixedPriceAsString =
fixedPrice.toString().replaceAll(RegExp(r'\.'), ',');

if (lastProduct.quantity > 1) {
var fixedPricePerUnit = (fixedPrice / lastProduct.quantity)
.toPrecision(2)
.toString()
.replaceAll(RegExp(r'\.'), ',');

lastProduct.pricePerUnit = fixedPricePerUnit;
}

lastProduct.totalPrice = fixedPriceAsString;
}
// If a row starts with a digit, it is a quantity and price per unit row:
else if (item.contains(RegExp(r'^\d'))) {
var items = item.split(RegExp(r'\s{6,7}'));
var quantity =
Expand All @@ -68,7 +98,7 @@ List<ReceiptProduct> strings2ReceiptProducts(List<String> lines) {
lastProduct.pricePerUnit = pricePerUnit;
}

// A "normal" line:
// A "normal" row:
else {
var items = item.split(RegExp(r'\s{11,33}'));

Expand Down
19 changes: 19 additions & 0 deletions bin/utils/ansipen_helper.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import 'package:ansicolor/ansicolor.dart';

AnsiPen greenPen() {
return AnsiPen()
..black(bold: true)
..green(bold: true);
}

AnsiPen redPen() {
return AnsiPen()
..black(bold: true)
..red(bold: true);
}

AnsiPen peachPen() {
return AnsiPen()
..black(bold: true)
..rgb(r: 1.0, g: 0.8, b: 0.2);
}
Loading

0 comments on commit b4811fa

Please sign in to comment.