support for NAB
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
Lynne Megido 2020-09-27 15:27:16 +10:00
parent 0800fa8cca
commit 42be0862ca
Signed by: lynnesbian
GPG Key ID: F0A184B5213D9F90
2 changed files with 54 additions and 29 deletions

1
.gitignore vendored
View File

@ -2,5 +2,6 @@
bin/
obj/
test.csv
test2.csv
rules.csv
.~lock*

View File

@ -22,7 +22,9 @@ using System.Globalization;
using System.IO;
using System.CommandLine.DragonFruit;
using System.Linq;
using System.Text.RegularExpressions;
using CsvHelper;
using CsvHelper.Configuration;
namespace BunyMuny {
internal class Program {
@ -38,38 +40,53 @@ namespace BunyMuny {
List<Rule> rules;
using (var ruleStreamReader = new StreamReader(ruleFile)) {
using var ruleCsv = new CsvReader(ruleStreamReader,CultureInfo.InvariantCulture);
using var ruleCsv = new CsvReader(ruleStreamReader, CultureInfo.InvariantCulture);
rules = ruleCsv.GetRecords<Rule>().ToList();
}
// get the first line of the CSV file (the header) as a string
string header;
using (var headerReader = new StreamReader(file)) {
header = headerReader.ReadLine();
}
using var sr = new StreamReader(file);
using var csv = new CsvReader(sr, CultureInfo.InvariantCulture);
csv.Configuration.HasHeaderRecord = false;
csv.Read();
csv.ReadHeader();
var nabRegex = new Regex(@"^\d\d \w{3} \d\d");
// get the first line of the CSV file (the header) as a string
var header = csv.Parser.Context.RawRecord.Trim();
switch (header) {
case null:
Console.WriteLine("File is empty 0uo");
return 1;
case "Date,Description,Debits and credits,Balance":
bank = Bank.ME;
break;
case "Whatever NAB uses I guess":
bank = Bank.NAB;
break;
default:
Console.WriteLine($"Unknown header: [{header}]");
break;
if (header == null) {
Console.WriteLine("File is empty 0uo");
return 1;
}
else if (header == "Date,Description,Debits and credits,Balance") {
bank = Bank.ME;
csv.Configuration.HasHeaderRecord = true;
// read in the header to allow for accessing fields by name rather than by index
csv.Read();
csv.ReadHeader();
}
else if (nabRegex.IsMatch(header)) {
// NAB exports don't have headers
// records look like "02 Apr 20,23.00,,,MISCELLANEOUS DEBIT,V1234 02/09 PAYPAL Sydney,1234.56
// the columns mean: date of transaction, amount, ???, ???, category,method,remaining balance
// i don't like it >:c
bank = Bank.NAB;
}
else {
Console.WriteLine($"Unknown header: [{header}]");
}
while (csv.Read()) {
decimal value;
string category;
string description;
switch (bank) {
case Bank.ME:
var value = decimal.Parse(csv.GetField("Debits and credits").TrimStart().Replace("$", ""));
var (category, description) = MatchAgainstRules(rules, csv.GetField("Description"));
value = decimal.Parse(csv.GetField("Debits and credits").TrimStart().Replace("$", ""));
(category, description) = MatchAgainstRules(rules, csv.GetField("Description"));
statements.Add(new Statement() {
Date = DateTime.ParseExact(csv.GetField("Date"), "dd/MM/yyyy", CultureInfo.InvariantCulture),
@ -81,8 +98,17 @@ namespace BunyMuny {
break;
case Bank.NAB:
Console.WriteLine("Unimplemented");
return 1;
// return 1;
value = decimal.Parse(csv.GetField(1));
(category, description) = MatchAgainstRules(rules, csv.GetField(5));
statements.Add(new Statement() {
Date = DateTime.ParseExact(csv.GetField(0), "dd MMM yy", CultureInfo.CurrentCulture),
OriginalDescription = csv.GetField(5),
Description = description,
Category = category,
Value = value
});
break;
case Bank.Other:
Console.WriteLine("Unknown bank!");
@ -102,16 +128,14 @@ namespace BunyMuny {
Console.WriteLine("Summary:");
Console.WriteLine("==================");
var summaries = statements.
GroupBy(s => s.Category). // group statements by category
Select(summary => new { // and then select:
Name = summary.First().Category?? "Other", // the name of the category...
var summaries = statements.GroupBy(s => s.Category). // group statements by category
Select(summary => new {
// and then select:
Name = summary.First().Category ?? "Other", // the name of the category...
Total = summary.Sum(s => s.Value).ToString() // ...and the sum of all the statement's values in that category
});
var longestCategoryName = summaries.
ToList().
Max(summary => summary.Name.Length);
var longestCategoryName = summaries.ToList().Max(summary => summary.Name.Length);
foreach (var summary in summaries) {
Console.WriteLine("{0}: {1}", summary.Name.PadLeft(longestCategoryName), summary.Total);