From 4e1b9fd605ce0fe14e1b102736e055c3a7e490b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Arttu=20Ylh=C3=A4vuori?= <10089872+areee@users.noreply.github.com> Date: Mon, 12 Sep 2022 21:59:37 +0300 Subject: [PATCH] Add Excel support & make it version 1.0.0 (#20) * Add excel dependency * Upgrade some transitive dependencies * Start implementing file Excel format selection * Add a default choice for export path & document it * Use table in readme file * Fix empty cells bug in readme * Update Dart SDK version number to the latest stable * Prepare for exporting receiptProducts as xlsx * Add home_directory_helper & use it. Use path package (cross-platform) * Fine-tune printSelectedValues (fix indentations) * Add Excel exports for eanProducts & receiptProducts * Clean pubspec.yaml * Add cellStyle for receiptProducts xlsx * Add a minimum example & change version number (use 1.0.0) * Bold some texts in readme * Small update into replaceTildeWithHomeDirectory method * Some small K-ruoka specific fixes into runMainProgram * Add a todo message for K-ruoka EAN products * Add Excel stylizing for EAN products (& rename ean) * Refactoring (add SheetExtension for updating the row style) * Add green background color for some extra products * Update Dart SDK version & some transitive dependencies * Remove assets section from pubspec * A small update into the installation file * Update readme --- INSTALLATION.md | 2 +- README.md | 28 +++++--- bin/ean_products_2_csv.dart | 14 ++-- bin/ean_products_2_xlsx.dart | 68 ++++++++++++++++++ bin/models/ean_product.dart | 4 +- bin/read_ean_products.dart | 2 +- bin/run_main_program.dart | 62 +++++++++++++---- bin/specific/k_ruoka/load_html_k_ruoka.dart | 10 +-- bin/specific/s_kaupat/ean_handler.dart | 4 +- bin/specific/s_kaupat/load_html_s_kaupat.dart | 2 +- .../s_kaupat/read_receipt_products.dart | 2 +- .../s_kaupat/receipt_products_2_csv.dart | 9 ++- .../s_kaupat/receipt_products_2_xlsx.dart | 69 +++++++++++++++++++ bin/utils/arg_selector_helper.dart | 6 +- bin/utils/export_format_helper.dart | 5 ++ bin/utils/extensions/sheet_extension.dart | 13 ++++ bin/utils/home_directory_helper.dart | 32 +++++++++ bin/utils/parse_kassakuitti_arguments.dart | 15 +++- bin/utils/printing_helper.dart | 11 +-- pubspec.lock | 47 +++++++++++-- pubspec.yaml | 14 ++-- 21 files changed, 351 insertions(+), 68 deletions(-) create mode 100644 bin/ean_products_2_xlsx.dart create mode 100644 bin/specific/s_kaupat/receipt_products_2_xlsx.dart create mode 100644 bin/utils/export_format_helper.dart create mode 100644 bin/utils/extensions/sheet_extension.dart create mode 100644 bin/utils/home_directory_helper.dart diff --git a/INSTALLATION.md b/INSTALLATION.md index 7f7c114..749e1a3 100644 --- a/INSTALLATION.md +++ b/INSTALLATION.md @@ -26,4 +26,4 @@ alias kassakuitti='dart run $HOME/Documents/dart_kassakuitti_cli/bin/dart_kassak If you're unsure where to save the alias, this might help you: - If you're using _Zsh_ as your shell, use `~/.zshrc` profile file. -- If you're using _Bash_ as your shell, use `~/.bash_profile` profile file. \ No newline at end of file +- If you're using _Bash_ as your shell, you could use `~/.bashrc` or `~/.bash_profile` profile file (or e.g. `~/.bash_aliases` file). \ No newline at end of file diff --git a/README.md b/README.md index 6ddfdd8..d7d1720 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ # dart_kassakuitti_cli -> A simple Dart CLI app to handle a cash receipt coming from S-kaupat or K-ruoka (two Finnish food online stores). +> A simple Dart CLI app to handle a cash receipt coming from S-kaupat or K-ruoka (two Finnish food online stores). Fully compatible with macOS, Linux & Windows. ## Installation @@ -30,21 +30,29 @@ See the installation in [its own page](https://github.com/areee/dart_kassakuitti The basic usage looks like this: ``` -kassakuitti run -t [a path to the cash receipt file] -h [a path to the EAN file] -s [S-kaupat or K-ruoka] -c [a path to generated CSV files] +kassakuitti run -t [a path to the cash receipt file] -h [a path to the EAN file] -s [S-kaupat or K-ruoka] -p [a path to generated files] -f [csv or Excel = xlsx] ``` -You can define -- a cash receipt (`-t` = text file), -- an EAN products file (`-h` = html file), -- optionally: which food online store to use (`-s` = store) and -- where to save the output files (`-c` = CSV file). +#### Choices (flags) for `kassakuitti run` command -S-kaupat is a default choice for the food online store selection (`-s`). +Mandatory | Abbreviation | Meaning | Default choice +:---: | :---: | :---: | :---: +✅ | `-t` | **Text** file (a cash receipt) | - +✅ | `-h` | **Html** file (an EAN products file) | - +  | `-s` | Which food online **store** to use | S-kaupat +  | `-p` | **Path** where to save the output files | User's Downloads folder (`~/Downloads`) +  | `-f` | In which file **format** the output files will be saved | csv -#### An example +#### An example #1 (minimum choices) ``` -kassakuitti run -t /Users/username/Downloads/cash_receipt.txt -h /Users/username/Downloads/https___www.s-kaupat.fi_tilaus_product_id-generating_time.html -s S-kaupat -c ~/Downloads +kassakuitti run -t /Users/username/Downloads/cash_receipt.txt -h /Users/username/Downloads/https___www.s-kaupat.fi_tilaus_product_id-generating_time.html +``` + +#### An example #2 (maximum choices) + +``` +kassakuitti run -t /Users/username/Downloads/cash_receipt.txt -h /Users/username/Downloads/https___www.s-kaupat.fi_tilaus_product_id-generating_time.html -s S-kaupat -p ~/Downloads -f csv ``` ### Help diff --git a/bin/ean_products_2_csv.dart b/bin/ean_products_2_csv.dart index 22ea95b..7a37c14 100644 --- a/bin/ean_products_2_csv.dart +++ b/bin/ean_products_2_csv.dart @@ -1,19 +1,23 @@ import 'dart:io'; + +import 'package:path/path.dart' as p; + import 'models/ean_product.dart'; import 'utils/date_helper.dart'; +import 'utils/home_directory_helper.dart'; -void eanProducts2CSV( - List eanProductList, String csvFilePath, String shopSelector) { +void eanProducts2CSV(List eanProductList, String exportFilePath, + String shopSelector) { var csv = StringBuffer(); csv.write('Name;Quantity;Price per unit;Total price;EAN code;More details\n'); for (var item in eanProductList) { csv.write( - '${item.name};${item.quantity};${item.pricePerUnit};${item.totalPrice};${item.ean};${item.moreDetails}\n'); + '${item.name};${item.quantity};${item.pricePerUnit};${item.totalPrice};${item.eanCode};${item.moreDetails}\n'); } - var file = File( - '$csvFilePath/${shopSelector}_ean_products_${formattedDateTime()}.csv'); + var file = File(p.join(replaceTildeWithHomeDirectory(exportFilePath), + '${shopSelector}_ean_products_${formattedDateTime()}.csv')); file.writeAsString(csv.toString()); } diff --git a/bin/ean_products_2_xlsx.dart b/bin/ean_products_2_xlsx.dart new file mode 100644 index 0000000..7757a86 --- /dev/null +++ b/bin/ean_products_2_xlsx.dart @@ -0,0 +1,68 @@ +import 'dart:io'; + +import 'package:excel/excel.dart'; +import 'package:path/path.dart' as p; + +import 'models/ean_product.dart'; +import 'utils/date_helper.dart'; +import 'utils/home_directory_helper.dart'; +import 'utils/extensions/sheet_extension.dart'; + +/// Saves EAN products as an Excel (xlsx) file. +void eanProducts2Excel( + List products, String exportFilePath, String shopSelector) { + var excel = Excel.createExcel(); + var sheetObject = excel.sheets[excel.getDefaultSheet()]; + + // Write the header. + var headerDataList = [ + "Name", + "Quantity", + "Price per unit", + "Total price", + "EAN code", + "More details" + ]; + sheetObject?.insertRowIterables(headerDataList, 0); + sheetObject?.updateSelectedRowStyle(0, CellStyle(bold: true)); + + // Write the products. + for (var product in products) { + var productDataList = [ + product.name, + product.quantity, + product.pricePerUnit, + product.totalPrice, + product.eanCode, + product.moreDetails + ]; + sheetObject?.insertRowIterables( + productDataList, products.indexOf(product) + 1); + + // If the product is a fruit or vegetable, change the background color to green. + if (product.eanCode.startsWith("2") || product.eanCode.startsWith("02")) { + sheetObject?.updateSelectedRowStyle(products.indexOf(product) + 1, + CellStyle(backgroundColorHex: "#1AFF1A")); + } + // If the product is a packaging material, + // change the font color to red and the background color to green. + if (product.name == 'Pakkausmateriaalikustannukset') { + sheetObject?.updateSelectedRowStyle(products.indexOf(product) + 1, + CellStyle(fontColorHex: "#FF0000", backgroundColorHex: "#1AFF1A")); + } + + // If the product is a home delivery, change the background color to green. + if (product.name == 'Kotiinkuljetus') { + sheetObject?.updateSelectedRowStyle(products.indexOf(product) + 1, + CellStyle(backgroundColorHex: "#1AFF1A")); + } + } + + // Save to the Excel (xlsx) file: + var fileBytes = excel.save(); + + File(p.join(replaceTildeWithHomeDirectory(exportFilePath), + '${shopSelector}_ean_products_${formattedDateTime()}.xlsx')) + ..createSync(recursive: true) + ..writeAsBytesSync(fileBytes!); +} diff --git a/bin/models/ean_product.dart b/bin/models/ean_product.dart index b8ba5b0..cb0c95a 100644 --- a/bin/models/ean_product.dart +++ b/bin/models/ean_product.dart @@ -1,5 +1,5 @@ class EANProduct { - final String ean; + final String eanCode; final String name; final int quantity; final String totalPrice; @@ -7,7 +7,7 @@ class EANProduct { final String moreDetails; EANProduct( - {this.ean = '0000000000000', + {this.eanCode = '0000000000000', this.name = 'Default EAN product name', this.quantity = 1, this.totalPrice = '0', diff --git a/bin/read_ean_products.dart b/bin/read_ean_products.dart index f1777b5..b740bd0 100644 --- a/bin/read_ean_products.dart +++ b/bin/read_ean_products.dart @@ -4,7 +4,7 @@ import 'specific/k_ruoka/load_html_k_ruoka.dart' as k_ruoka; import 'utils/shop_selector_helper.dart'; Future> readEANProducts( - String filePath, ShopSelector shopSelector, String csvFilePath) async { + String filePath, ShopSelector shopSelector) async { switch (shopSelector) { case ShopSelector.sKaupat: return await s_kaupat.loadHtmlFromAssets(filePath); diff --git a/bin/run_main_program.dart b/bin/run_main_program.dart index 6a19d35..5e47bd8 100644 --- a/bin/run_main_program.dart +++ b/bin/run_main_program.dart @@ -4,12 +4,15 @@ import 'package:args/args.dart'; import 'package:hive/hive.dart'; import 'ean_products_2_csv.dart'; +import 'ean_products_2_xlsx.dart'; import 'models/hive_product.dart'; import 'read_ean_products.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 'specific/s_kaupat/receipt_products_2_xlsx.dart'; import 'utils/arg_selector_helper.dart'; +import 'utils/export_format_helper.dart'; import 'utils/printing_helper.dart'; import 'utils/shop_selector_helper.dart'; @@ -20,27 +23,60 @@ Future> runMainProgram( var selectedTextFile = argResults[ArgSelector.textFile.value] as String?; var selectedHtmlFile = argResults[ArgSelector.htmlFile.value] as String; var selectedStore = argResults[ArgSelector.foodOnlineStore.value] as String; - var csvFilesPath = argResults[ArgSelector.csvPath.value] as String; - - printSelectedValues( - selectedTextFile, selectedHtmlFile, selectedStore, csvFilesPath); + var exportFilesPath = argResults[ArgSelector.exportPath.value] as String; + var exportFilesFormat = argResults[ArgSelector.exportFormat.value] as String; + /* + Check the HTML file name (it should contain k-ruoka or s-kaupat) + (By default, selectedStore is s-kaupat) + */ + selectedStore = selectedHtmlFile.contains('k-ruoka.fi') + ? ShopSelector.kRuoka.value + : selectedStore; + printSelectedValues(selectedTextFile, selectedHtmlFile, selectedStore, + exportFilesPath, exportFilesFormat); try { if (ShopSelector.sKaupat.value == selectedStore) { - var receiptProducts = - await readReceiptProducts(selectedTextFile!, csvFilesPath); - var eanProducts = await readEANProducts( - selectedHtmlFile, ShopSelector.sKaupat, csvFilesPath); + if (selectedTextFile == null) { + throw Exception('Text file is required for S-Kaupat'); + } + var receiptProducts = await readReceiptProducts(selectedTextFile); + var eanProducts = + await readEANProducts(selectedHtmlFile, ShopSelector.sKaupat); await eanHandler(receiptProducts, eanProducts.toList(), hiveProducts); - receiptProducts2CSV(receiptProducts, csvFilesPath); - eanProducts2CSV(eanProducts, csvFilesPath, ShopSelector.sKaupat.name); + // Export products to csv files + if (exportFilesFormat == ExportFormat.csv.name) { + receiptProducts2CSV(receiptProducts, exportFilesPath); + eanProducts2CSV( + eanProducts, exportFilesPath, ShopSelector.sKaupat.name); + } + // Export products to Excel (xlsx) files + else if (exportFilesFormat == ExportFormat.excel.name) { + receiptProducts2Excel(receiptProducts, exportFilesPath); + eanProducts2Excel( + eanProducts, exportFilesPath, ShopSelector.sKaupat.name); + } else { + print('Unknow export format'); + exitCode = 1; + } } else if (ShopSelector.kRuoka.value == selectedStore) { - var eanProducts = await readEANProducts( - selectedHtmlFile, ShopSelector.kRuoka, csvFilesPath); + var eanProducts = + await readEANProducts(selectedHtmlFile, ShopSelector.kRuoka); - eanProducts2CSV(eanProducts, csvFilesPath, ShopSelector.kRuoka.name); + // Export products to csv file + if (exportFilesFormat == ExportFormat.csv.name) { + eanProducts2CSV(eanProducts, exportFilesPath, ShopSelector.kRuoka.name); + } + // Export products to Excel (xlsx) file + else if (exportFilesFormat == ExportFormat.excel.name) { + eanProducts2Excel( + eanProducts, exportFilesPath, ShopSelector.kRuoka.name); + } else { + print('Unknow export format'); + exitCode = 1; + } } else { print('Unknown store: $selectedStore'); exitCode = 1; diff --git a/bin/specific/k_ruoka/load_html_k_ruoka.dart b/bin/specific/k_ruoka/load_html_k_ruoka.dart index 359c6b5..768be4f 100644 --- a/bin/specific/k_ruoka/load_html_k_ruoka.dart +++ b/bin/specific/k_ruoka/load_html_k_ruoka.dart @@ -50,7 +50,7 @@ Future> loadHtmlFromAssets(String filePath) async { .ceil(); // e.g. 0.2 -> 1 (round up) or 0.5 -> 1 (round up) eanProducts.add(EANProduct( - ean: productEan, + eanCode: productEan, name: productName, quantity: quantity, totalPrice: productPrice, @@ -96,7 +96,7 @@ Future> loadHtmlFromAssets(String filePath) async { .ceil(); // e.g. 0.2 -> 1 (round up) or 0.5 -> 1 (round up) eanProducts.add(EANProduct( - ean: eanCode, + eanCode: eanCode, name: productName, quantity: quantity, totalPrice: productPrice, @@ -115,7 +115,7 @@ Future> loadHtmlFromAssets(String filePath) async { var homeDeliveryPrice = homeDeliveryPriceSection.children[1].text; eanProducts.add(EANProduct( - ean: '', + eanCode: '', name: homeDeliveryText, quantity: 1, totalPrice: homeDeliveryPrice, @@ -142,11 +142,13 @@ Future> loadHtmlFromAssets(String filePath) async { .trim(); eanProducts.add(EANProduct( - ean: '', + eanCode: '', name: packagingMaterialTerm, quantity: -1, totalPrice: '', pricePerUnit: packagingMaterialPrice, + moreDetails: + 'TODO: fill in the amount of packaging material and the total price.', )); return eanProducts; diff --git a/bin/specific/s_kaupat/ean_handler.dart b/bin/specific/s_kaupat/ean_handler.dart index 1103a0c..f06d00f 100644 --- a/bin/specific/s_kaupat/ean_handler.dart +++ b/bin/specific/s_kaupat/ean_handler.dart @@ -128,7 +128,7 @@ Future _handleFoundCases( print(greenPen().write('\tFound one product:')); print('\t\t${filteredEanProducts.first}'); - receiptProduct.eanCode = filteredEanProducts.first.ean; + receiptProduct.eanCode = filteredEanProducts.first.eanCode; } else if (filteredEanProducts.length > 1) { print(redPen() .write('\tFound multiple products (${filteredEanProducts.length}' @@ -147,7 +147,7 @@ Future _handleFoundCases( print(greenPen().write('\tYou selected: $selectedEanProduct')); - receiptProduct.eanCode = selectedEanProduct.ean; + receiptProduct.eanCode = selectedEanProduct.eanCode; await hiveProducts.add(HiveProduct( receiptName: receiptProduct.name, eanName: selectedEanProduct.name)); diff --git a/bin/specific/s_kaupat/load_html_s_kaupat.dart b/bin/specific/s_kaupat/load_html_s_kaupat.dart index 0789196..a7ddcdc 100644 --- a/bin/specific/s_kaupat/load_html_s_kaupat.dart +++ b/bin/specific/s_kaupat/load_html_s_kaupat.dart @@ -40,7 +40,7 @@ Future> loadHtmlFromAssets(String filePath) async { eanProducts.add( EANProduct( - ean: eanCode, + eanCode: eanCode, name: productName, quantity: quantity, totalPrice: productPrice, diff --git a/bin/specific/s_kaupat/read_receipt_products.dart b/bin/specific/s_kaupat/read_receipt_products.dart index 976a5c6..4f0fd9a 100644 --- a/bin/specific/s_kaupat/read_receipt_products.dart +++ b/bin/specific/s_kaupat/read_receipt_products.dart @@ -4,7 +4,7 @@ import '../../models/receipt_product.dart'; import 'strings_to_receipt_products.dart'; Future> readReceiptProducts( - String filePath, String csvFilePath) async { + String filePath) async { var lines = await readReceiptFile(filePath); return strings2ReceiptProducts(lines ?? []); } diff --git a/bin/specific/s_kaupat/receipt_products_2_csv.dart b/bin/specific/s_kaupat/receipt_products_2_csv.dart index a19363d..c50a53d 100644 --- a/bin/specific/s_kaupat/receipt_products_2_csv.dart +++ b/bin/specific/s_kaupat/receipt_products_2_csv.dart @@ -1,9 +1,13 @@ import 'dart:io'; + +import 'package:path/path.dart' as p; + import '../../utils/date_helper.dart'; import '../../models/receipt_product.dart'; +import '../../utils/home_directory_helper.dart'; /// Saves receipt products as a CSV file. -void receiptProducts2CSV(List products, String csvFilePath) { +void receiptProducts2CSV(List products, String exportFilePath) { var csv = StringBuffer(); var header = ''; var discountCounted = false; @@ -26,6 +30,7 @@ void receiptProducts2CSV(List products, String csvFilePath) { '${product.name};${product.quantity};${product.pricePerUnit};${product.totalPrice}${discountCounted ? ';${product.discountCounted}' : ''};${product.eanCode}\n'); } - var file = File('$csvFilePath/receipt_products_${formattedDateTime()}.csv'); + var file = File(p.join(replaceTildeWithHomeDirectory(exportFilePath), + 'receipt_products_${formattedDateTime()}.csv')); file.writeAsString(csv.toString()); } diff --git a/bin/specific/s_kaupat/receipt_products_2_xlsx.dart b/bin/specific/s_kaupat/receipt_products_2_xlsx.dart new file mode 100644 index 0000000..6212bbe --- /dev/null +++ b/bin/specific/s_kaupat/receipt_products_2_xlsx.dart @@ -0,0 +1,69 @@ +import 'dart:io'; + +import 'package:excel/excel.dart'; +import 'package:path/path.dart' as p; + +import '../../models/receipt_product.dart'; +import '../../utils/date_helper.dart'; +import '../../utils/home_directory_helper.dart'; +import '../../utils/extensions/sheet_extension.dart'; + +/// Saves receipt products as an Excel (xlsx) file. +void receiptProducts2Excel( + List products, String exportFilePath) { + var excel = Excel.createExcel(); + var sheetObject = excel.sheets[excel.getDefaultSheet()]; + + // Write the header. + var headerDataList = [ + "Name", + "Quantity", + "Price per unit", + "Total price", + "EAN code" + ]; + var discountCounted = false; + + // If there aren't any discountCounted products, don't add it to the Excel file. + if (products.every((product) => product.discountCounted.isEmpty)) { + sheetObject?.insertRowIterables(headerDataList, 0); + sheetObject?.updateSelectedRowStyle(0, CellStyle(bold: true)); + } else { + discountCounted = true; + headerDataList.add("Discount counted"); + sheetObject?.insertRowIterables(headerDataList, 0); + sheetObject?.updateSelectedRowStyle(0, CellStyle(bold: true)); + } + + // Write the products. + for (var product in products) { + var productDataList = [ + product.name, + product.quantity, + product.pricePerUnit, + product.totalPrice, + product.eanCode + ]; + + if (discountCounted) { + productDataList.add(product.discountCounted); + } + + sheetObject?.insertRowIterables( + productDataList, products.indexOf(product) + 1); + + // If the product is a fruit or vegetable, change the background color to green. + if (product.eanCode.startsWith("2") || product.eanCode.startsWith("02")) { + sheetObject?.updateSelectedRowStyle(products.indexOf(product) + 1, + CellStyle(backgroundColorHex: "#1AFF1A")); + } + } + + // Save to the Excel (xlsx) file: + var fileBytes = excel.save(); + + File(p.join(replaceTildeWithHomeDirectory(exportFilePath), + 'receipt_products_${formattedDateTime()}.xlsx')) + ..createSync(recursive: true) + ..writeAsBytesSync(fileBytes!); +} diff --git a/bin/utils/arg_selector_helper.dart b/bin/utils/arg_selector_helper.dart index 098652e..1e4be4c 100644 --- a/bin/utils/arg_selector_helper.dart +++ b/bin/utils/arg_selector_helper.dart @@ -1,9 +1,11 @@ -/// ArgSelector (textFile, htmlFile, foodOnlineStore, csvPath, help, run) +/// ArgSelector (textFile, htmlFile, foodOnlineStore, +/// exportPath, exportFormat, help, run, hive) enum ArgSelector { textFile('text'), htmlFile('html'), foodOnlineStore('store'), - csvPath('csv'), + exportPath('path'), + exportFormat('format'), help('help'), run('run'), hive('hive'); diff --git a/bin/utils/export_format_helper.dart b/bin/utils/export_format_helper.dart new file mode 100644 index 0000000..e4e0f52 --- /dev/null +++ b/bin/utils/export_format_helper.dart @@ -0,0 +1,5 @@ +/// ExportFormat +enum ExportFormat { + csv, + excel +} \ No newline at end of file diff --git a/bin/utils/extensions/sheet_extension.dart b/bin/utils/extensions/sheet_extension.dart new file mode 100644 index 0000000..c3cae6f --- /dev/null +++ b/bin/utils/extensions/sheet_extension.dart @@ -0,0 +1,13 @@ +import 'package:excel/excel.dart'; + +/// Extension for [Sheet]. +extension SheetExtension on Sheet { + /// Updates the style for a selected row. + void updateSelectedRowStyle(int rowIndex, CellStyle cellStyle) { + var rowDatas = row(rowIndex); + + for (var rowData in rowDatas) { + updateCell(rowData!.cellIndex, rowData.value, cellStyle: cellStyle); + } + } +} diff --git a/bin/utils/home_directory_helper.dart b/bin/utils/home_directory_helper.dart new file mode 100644 index 0000000..bcf65e6 --- /dev/null +++ b/bin/utils/home_directory_helper.dart @@ -0,0 +1,32 @@ +import 'dart:io'; + +import 'package:path/path.dart' as p; + +/// Returns user's home directory. +String getUserHomeDirectory() { + // Source: https://stackoverflow.com/a/25498458 + + String? homeDirectory = ""; + Map envVars = Platform.environment; + if (Platform.isMacOS || Platform.isLinux) { + homeDirectory = envVars['HOME']; + } else if (Platform.isWindows) { + homeDirectory = envVars['UserProfile']; + } + + // Throw an exception if the home directory is not found. + if (homeDirectory == null) { + throw Exception('Home directory not found.'); + } + + return homeDirectory; +} + +/// Replaces tilde with user's home directory if it exists. +String replaceTildeWithHomeDirectory(String path) { + if (path.startsWith('~/')) { + return p.join(getUserHomeDirectory(), path.replaceFirst('~/', '')); + } else { + return path; + } +} diff --git a/bin/utils/parse_kassakuitti_arguments.dart b/bin/utils/parse_kassakuitti_arguments.dart index eef6b3e..119f60b 100644 --- a/bin/utils/parse_kassakuitti_arguments.dart +++ b/bin/utils/parse_kassakuitti_arguments.dart @@ -1,6 +1,7 @@ import 'package:args/args.dart'; import 'arg_selector_helper.dart'; +import 'export_format_helper.dart'; import 'shop_selector_helper.dart'; ArgParser getParser() { @@ -26,9 +27,17 @@ ArgParser getParser() { allowed: [ShopSelector.sKaupat.value, ShopSelector.kRuoka.value], ) ..addOption( - ArgSelector.csvPath.value, - abbr: ArgSelector.csvPath.value.substring(0, 1), - help: 'Path for output CSV files', + ArgSelector.exportPath.value, + abbr: ArgSelector.exportPath.value.substring(0, 1), + help: 'Export path for output files', + defaultsTo: '~/Downloads', + ) + ..addOption( + ArgSelector.exportFormat.value, + abbr: ArgSelector.exportFormat.value.substring(0, 1), + help: 'Export format for output files', + defaultsTo: ExportFormat.csv.name, + allowed: [ExportFormat.csv.name, ExportFormat.excel.name], ); return parser; diff --git a/bin/utils/printing_helper.dart b/bin/utils/printing_helper.dart index 085700b..71de59f 100644 --- a/bin/utils/printing_helper.dart +++ b/bin/utils/printing_helper.dart @@ -6,12 +6,13 @@ import 'package:yaml/yaml.dart'; /// Prints selected values. void printSelectedValues(String? selectedTextFile, String selectedHtmlFile, - String selectedStore, String csvFilesPath) { + String selectedStore, String exportFilesPath, String exportFilesFormat) { print('Selected values:' - '${selectedTextFile != null ? '\n- Path to the cash receipt:\t\t$selectedTextFile' : ''}' - '\n- Path to the EAN products file:\t$selectedHtmlFile' - '\n- Selected store:\t\t\t$selectedStore' - '\n- Path where to save CSV files:\t\t$csvFilesPath\n'); + '${selectedTextFile != null ? '\n- Path to the cash receipt:\t\t\t\t$selectedTextFile' : ''}' + '\n- Path to the EAN products file:\t\t\t$selectedHtmlFile' + '\n- Selected store:\t\t\t\t\t$selectedStore' + '\n- Path where to save the output files:\t\t\t$exportFilesPath' + '\n- In which file format the output files will be saved:\t$exportFilesFormat\n'); } /// Prints some basic info. diff --git a/pubspec.lock b/pubspec.lock index 9ae3f8a..933ed03 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -7,14 +7,14 @@ packages: name: _fe_analyzer_shared url: "https://pub.dartlang.org" source: hosted - version: "44.0.0" + version: "47.0.0" analyzer: dependency: transitive description: name: analyzer url: "https://pub.dartlang.org" source: hosted - version: "4.4.0" + version: "4.7.0" ansicolor: dependency: "direct main" description: @@ -22,6 +22,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.0.1" + archive: + dependency: transitive + description: + name: archive + url: "https://pub.dartlang.org" + source: hosted + version: "3.3.1" args: dependency: "direct main" description: @@ -91,7 +98,7 @@ packages: name: built_value url: "https://pub.dartlang.org" source: hosted - version: "8.4.0" + version: "8.4.1" checked_yaml: dependency: transitive description: @@ -148,13 +155,27 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.2.3" + equatable: + dependency: transitive + description: + name: equatable + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.5" + excel: + dependency: "direct main" + description: + name: excel + url: "https://pub.dartlang.org" + source: hosted + version: "2.0.0-null-safety-3" file: dependency: transitive description: name: file url: "https://pub.dartlang.org" source: hosted - version: "6.1.2" + version: "6.1.4" fixnum: dependency: transitive description: @@ -295,6 +316,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.8.2" + petitparser: + dependency: transitive + description: + name: petitparser + url: "https://pub.dartlang.org" + source: hosted + version: "5.0.0" pool: dependency: transitive description: @@ -315,7 +343,7 @@ packages: name: pubspec_parse url: "https://pub.dartlang.org" source: hosted - version: "1.2.0" + version: "1.2.1" shelf: dependency: transitive description: @@ -414,6 +442,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.2.0" + xml: + dependency: transitive + description: + name: xml + url: "https://pub.dartlang.org" + source: hosted + version: "5.4.1" yaml: dependency: "direct main" description: @@ -422,4 +457,4 @@ packages: source: hosted version: "3.1.1" sdks: - dart: ">=2.17.3 <3.0.0" + dart: ">=2.18.0 <3.0.0" diff --git a/pubspec.yaml b/pubspec.yaml index b86a408..098f016 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,14 +1,10 @@ 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.15.0 +version: 1.0.0 homepage: https://github.com/areee/dart_kassakuitti_cli environment: - sdk: '>=2.17.3 <3.0.0' - - -# dependencies: -# path: ^1.8.0 + sdk: '>=2.18.0 <3.0.0' dev_dependencies: build_runner: ^2.2.0 @@ -17,11 +13,9 @@ dev_dependencies: dependencies: ansicolor: ^2.0.1 args: ^2.3.1 + excel: ^2.0.0-null-safety-3 hive: ^2.2.3 html: ^0.15.0 intl: ^0.17.0 path: ^1.8.2 - yaml: ^3.1.1 - -assets: - - assets/files/ \ No newline at end of file + yaml: ^3.1.1 \ No newline at end of file