Skip to content

Commit

Permalink
Copy an EAN code from an EAN product to a receipt product (#14)
Browse files Browse the repository at this point in the history
* Refactoring (add 'printBasicInfo') & add Future + await for 'readHtmlAndSaveAsCSV'

* Set 'printBasicInfo' & 'printSelectedValues' as private methods

* Refactor (move 'receiptProducts2CSV' to the main level)

* Refactor (move 'eanProductListToCSV' to the main level & some renaming)

* Rename a file ('save_rps_as_csv' -> 'receipt_products_2_csv')

* Update the main method (give shop selector name to eanProducts2CSV)

* Refactor (move 'printSelectedValues' & 'printBasicInfo' to an own file)

* Update dart_kassakuitti_cli (the starting point for EAN handling)

* Update pubspec.yaml (update the version number)

* Update toStrings of models (better printing)

* Update the main method (initial product name comparison)

* Update 'printSelectedValues' method (add new line to the end of the print)

* Update the main method (initial EAN code handling)

* Update the main method (continue initial EAN handling)

* Add 'eanHandler' & move code from the method to it

* Update receipt csv EAN title

* Update eanHandler (EAN product name to lower case)

* Update eanHandler (clean a bit)

* Update eanHandler (refactor & enhance a bit)

* Update the main method (clean & give a copy of eanProducts to the EAN handler)

* Update eanHandler (better handling for non-found RPs)

* Update eanHandler (check if splittedReceiptProcuctNames is empty)

* Update eanHandler (a bit fine-tuning)

* Update eanHandler (handle cases with >2 filtered results)

* Update eanHandler (make 2 methods private)
  • Loading branch information
areee committed Apr 16, 2022
1 parent 4861ee3 commit db073a1
Show file tree
Hide file tree
Showing 10 changed files with 162 additions and 64 deletions.
57 changes: 20 additions & 37 deletions bin/dart_kassakuitti_cli.dart
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import 'dart:io';

import 'package:path/path.dart';
import 'package:yaml/yaml.dart';

import 'read_html_and_save_as_csv.dart';
import 'specific/s_kaupat/read_receipt_and_save_as_csv.dart';
import 'read_ean_products.dart';
import 'ean_products_2_csv.dart';
import 'specific/s_kaupat/ean_handler.dart';
import 'specific/s_kaupat/read_receipt_products.dart';
import 'specific/s_kaupat/receipt_products_2_csv.dart';
import 'utils/arg_selector_helper.dart';
import 'utils/parse_kassakuitti_arguments.dart';
import 'utils/printing_helper.dart';
import 'utils/shop_selector_helper.dart';

void main(List<String> arguments) async {
Expand All @@ -19,7 +20,7 @@ void main(List<String> arguments) async {
print('Help:\n${parser.usage}');
return;
} else if (argResults.command?.name == ArgSelector.run.value!) {
print('Running...');
print('\nRunning...\n');

var selectedTextFile = argResults[ArgSelector.textFile.value!] as String;
var selectedHtmlFile = argResults[ArgSelector.htmlFile.value!] as String;
Expand All @@ -32,12 +33,20 @@ void main(List<String> arguments) async {

try {
if (ShopSelector.sKaupat.isEqual(selectedStore)) {
readReceiptAndSaveAsCSV(selectedTextFile, csvFilesPath);
readHtmlAndSaveAsCSV(
var receiptProducts =
await readReceiptProducts(selectedTextFile, csvFilesPath);
var eanProducts = await readEANProducts(
selectedHtmlFile, ShopSelector.sKaupat, csvFilesPath);

eanHandler(receiptProducts, eanProducts.toList());

receiptProducts2CSV(receiptProducts, csvFilesPath);
eanProducts2CSV(eanProducts, csvFilesPath, ShopSelector.sKaupat.name);
} else if (ShopSelector.kRuoka.isEqual(selectedStore)) {
readHtmlAndSaveAsCSV(
var eanProducts = await readEANProducts(
selectedHtmlFile, ShopSelector.kRuoka, csvFilesPath);

eanProducts2CSV(eanProducts, csvFilesPath, ShopSelector.kRuoka.name);
} else {
print('Unknown store: $selectedStore');
exitCode = 1;
Expand All @@ -47,34 +56,8 @@ void main(List<String> arguments) async {
exitCode = 1;
}

print('Done!');
print('\nDone!');
} else {
String pathToYaml =
join(dirname(Platform.script.toFilePath()), '../pubspec.yaml');
var file = File(pathToYaml);
var fileAsString = await file.readAsString();

Map yaml = loadYaml(fileAsString);
print(yaml['name']);
print(yaml['description']);
print('Version: ${yaml['version']}');
print('Homepage: ${yaml['homepage']}');

print('''\nTo get help, run:
dart run bin/dart_kassakuitti_cli.dart help
or when using alias:
kassakuitti help\n''');
await printBasicInfo();
}
}

void printSelectedValues(String selectedTextFile, String selectedHtmlFile,
String selectedStore, String csvFilesPath) {
print('''Selected values:
- Path to the cash receipt:\t\t$selectedTextFile
- Path to the EAN products file:\t$selectedHtmlFile
- Selected store:\t\t\t$selectedStore
- Path where to save CSV files:\t$csvFilesPath''');
}
7 changes: 3 additions & 4 deletions bin/save_eps_as_csv.dart → bin/ean_products_2_csv.dart
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import 'dart:io';
import 'models/ean_product.dart';
import 'utils/date_helper.dart';
import 'utils/shop_selector_helper.dart';

void eanProductListToCSV(List<EANProduct> eanProductList, String csvFilePath,
ShopSelector shopSelector) {
void eanProducts2CSV(
List<EANProduct> eanProductList, String csvFilePath, String shopSelector) {
var csv = StringBuffer();

csv.write('EAN code;Name;Quantity;Total price;Price per unit;More details\n');
Expand All @@ -15,6 +14,6 @@ void eanProductListToCSV(List<EANProduct> eanProductList, String csvFilePath,
}

var file = File(
'$csvFilePath/${shopSelector.name}_ean_products_${formattedDateTime()}.csv');
'$csvFilePath/${shopSelector}_ean_products_${formattedDateTime()}.csv');
file.writeAsString(csv.toString());
}
4 changes: 1 addition & 3 deletions bin/models/ean_product.dart
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,6 @@ class EANProduct {

@override
String toString() {
return 'EANProduct{ean: $ean, name: $name, quantity: $quantity,'
' price: $totalPrice, pricePerUnit: $pricePerUnit,'
' moreDetails: $moreDetails}';
return '$quantity x $name${quantity > 1 ? ' ($pricePerUnit e / pcs)' : ''} = $totalPrice e';
}
}
3 changes: 1 addition & 2 deletions bin/models/receipt_product.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ class ReceiptProduct {
String totalPrice;
int quantity;
String pricePerUnit;
// TODO: In the future, this should include an EAN code for a receipt product.
String eanCode;
String discountCounted;

Expand All @@ -18,6 +17,6 @@ class ReceiptProduct {

@override
String toString() {
return 'Product{name: $name, totalPrice: $totalPrice, quantity: $quantity, pricePerUnit: $pricePerUnit, eanCode: $eanCode, discountCounted: $discountCounted}';
return '$quantity x $name${quantity > 1 ? ' ($pricePerUnit e / pcs)' : ''} = $totalPrice e';
}
}
13 changes: 3 additions & 10 deletions bin/read_html_and_save_as_csv.dart → bin/read_ean_products.dart
Original file line number Diff line number Diff line change
@@ -1,21 +1,14 @@
import 'models/ean_product.dart';
import 'specific/s_kaupat/load_html_s_kaupat.dart' as s_kaupat;
import 'specific/k_ruoka/load_html_k_ruoka.dart' as k_ruoka;
import 'save_eps_as_csv.dart';
import 'utils/shop_selector_helper.dart';

void readHtmlAndSaveAsCSV(
Future<List<EANProduct>> readEANProducts(
String filePath, ShopSelector shopSelector, String csvFilePath) async {
List<EANProduct> awaitedEANProductList = [];

switch (shopSelector) {
case ShopSelector.sKaupat:
awaitedEANProductList = await s_kaupat.loadHtmlFromAssets(filePath);
break;
return await s_kaupat.loadHtmlFromAssets(filePath);
case ShopSelector.kRuoka:
awaitedEANProductList = await k_ruoka.loadHtmlFromAssets(filePath);
break;
return await k_ruoka.loadHtmlFromAssets(filePath);
}

eanProductListToCSV(awaitedEANProductList, csvFilePath, shopSelector);
}
93 changes: 93 additions & 0 deletions bin/specific/s_kaupat/ean_handler.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import 'dart:io';

import '../../models/ean_product.dart';
import '../../models/receipt_product.dart';

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

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

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

_handleFoundCases(receiptProduct, filteredEanProducts, eanProducts);

if (filteredEanProducts.isEmpty) {
print('\tNo product found for the 1st round.');
nonFoundReceiptProducts.add(receiptProduct);
}
}

print('\nThe second round begins!');
print(
'Statistics: ${nonFoundReceiptProducts.length} nonFoundReceiptProducts, '
'${eanProducts.length} eanProducts\n');

for (var nonFoundReceiptProduct in nonFoundReceiptProducts) {
print(nonFoundReceiptProduct);

var splittedReceiptProcuctNames = nonFoundReceiptProduct.name.split(' ');

if (splittedReceiptProcuctNames.isEmpty) {
print('\tNo product found for the 2nd round.');
continue;
}

var filteredEanProducts =
_filterEANProducts(splittedReceiptProcuctNames[0], eanProducts);

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

_handleFoundCases(nonFoundReceiptProduct, filteredEanProducts, eanProducts);

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

print('\nFinished!');
print('Only ${eanProducts.length} unknown eanProducts left.');
}

List<EANProduct> _filterEANProducts(
String receiptProductName, List<EANProduct> eanProducts) {
return eanProducts
.where((eanProduct) =>
eanProduct.name.toLowerCase().contains(receiptProductName))
.toList();
}

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

receiptProduct.eanCode = filteredEanProducts[0].ean;
origEanProducts.remove(filteredEanProducts[0]);
} 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;
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import 'dart:io';

import 'save_rps_as_csv.dart';
import '../../models/receipt_product.dart';
import 'strings_to_receipt_products.dart';

void readReceiptAndSaveAsCSV(String filePath, String csvFilePath) async {
Future<List<ReceiptProduct>> readReceiptProducts(
String filePath, String csvFilePath) async {
var lines = await readReceiptFile(filePath);
var products = strings2ReceiptProducts(lines ?? []);
receiptProducts2CSV(products, csvFilePath);
return strings2ReceiptProducts(lines ?? []);
}

/// Read a text file and return as a list of lines.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,11 @@ void receiptProducts2CSV(List<ReceiptProduct> products, String csvFilePath) {

// If there aren't any discountCounted products, don't add it to the CSV file.
if (products.every((product) => product.discountCounted.isEmpty)) {
header =
'Name;Quantity;Price per unit;Total price;EAN code (add manually)\n';
header = 'Name;Quantity;Price per unit;Total price;EAN code\n';
} else {
discountCounted = true;
header =
'Name;Quantity;Price per unit;Total price;Discount counted;EAN code (add manually)\n';
'Name;Quantity;Price per unit;Total price;Discount counted;EAN code\n';
}

// Write the header
Expand Down
34 changes: 34 additions & 0 deletions bin/utils/printing_helper.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import 'dart:io';

import 'package:path/path.dart';
import 'package:yaml/yaml.dart';

void printSelectedValues(String selectedTextFile, String selectedHtmlFile,
String selectedStore, String csvFilesPath) {
print('''Selected values:
- Path to the cash receipt:\t\t$selectedTextFile
- Path to the EAN products file:\t$selectedHtmlFile
- Selected store:\t\t\t$selectedStore
- Path where to save CSV files:\t$csvFilesPath\n''');
}

Future<void> printBasicInfo() async {
String pathToYaml =
join(dirname(Platform.script.toFilePath()), '../pubspec.yaml');
var file = File(pathToYaml);
var fileAsString = await file.readAsString();

Map yaml = loadYaml(fileAsString);
print(yaml['name']);
print(yaml['description']);
print('Version: ${yaml['version']}');
print('Homepage: ${yaml['homepage']}');

print('''\nTo get help, run:
dart run bin/dart_kassakuitti_cli.dart help
or when using alias:
kassakuitti help\n''');
}
2 changes: 1 addition & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: dart_kassakuitti_cli
description: A simple Dart CLI app to handle a cash receipt coming from S-kaupat or K-ruoka (two Finnish food online stores).
version: 0.10.0
version: 0.11.0
homepage: https://github.com/areee/dart_kassakuitti_cli

environment:
Expand Down

0 comments on commit db073a1

Please sign in to comment.