From e45c36e6e32bebb53a58c3622a9a9431f369b089 Mon Sep 17 00:00:00 2001 From: Lynnesbian Date: Sat, 26 Sep 2020 22:45:07 +1000 Subject: [PATCH] cleaner(ish) code, support for rules --- Bank.cs | 3 +- Program.cs | 114 +++++++++++++++++++++++++++++++++------------------ Statement.cs | 12 +++++- 3 files changed, 87 insertions(+), 42 deletions(-) diff --git a/Bank.cs b/Bank.cs index 974c318..6aacf17 100644 --- a/Bank.cs +++ b/Bank.cs @@ -1,6 +1,7 @@ namespace BunyMuny { public enum Bank { ME, - NAB + NAB, + Other } } diff --git a/Program.cs b/Program.cs index 2982572..0787d93 100644 --- a/Program.cs +++ b/Program.cs @@ -2,64 +2,98 @@ using System.Collections.Generic; using System.Globalization; using System.IO; +using System.Text.Json; using System.CommandLine.DragonFruit; +using System.Text.Json.Serialization; using CsvHelper; -using CsvHelper.Configuration.Attributes; namespace BunyMuny { - class Program { + internal class Program { /// /// BunyMuny parses the CSV output of various bank statement listings and converts it to something more human readable with nice visualisations. /// /// The CSV file to read - /// The JSON file to use for rules when parsing statement descriptions + /// The JSON file to use for rules when parsing statement descriptions /// - static int Main(string file = "test.csv", string rules = "rules.json") { - Bank bank = Bank.ME; + static int Main(string file = "test.csv", string ruleFile = "rules.json") { + var bank = Bank.Other; var statements = new List(); + List rules; - using (var sr = new StreamReader(file)) { - using (var csv = new CsvReader(sr, CultureInfo.InvariantCulture)) { - csv.Read(); - csv.ReadHeader(); - // get the first line of the CSV file (the header) as a string - string header = csv.Parser.Context.RawRecord; - if (header == null) { - Console.WriteLine("File is empty 0uo"); + using (var ruleStreamReader = new StreamReader(ruleFile)) { + var jsonOptions = new JsonSerializerOptions(); + jsonOptions.Converters.Add(new JsonStringEnumConverter(JsonNamingPolicy.CamelCase)); + jsonOptions.WriteIndented = true; + rules = JsonSerializer.Deserialize>(ruleStreamReader.ReadToEnd(), jsonOptions); + } + + using var sr = new StreamReader(file); + using var csv = new CsvReader(sr, CultureInfo.InvariantCulture); + + csv.Read(); + csv.ReadHeader(); + + // 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; + } + + while (csv.Read()) { + switch (bank) { + case Bank.ME: + var value = double.Parse(csv.GetField("Debits and credits").TrimStart().Replace("$", "")); + var ruleValues = MatchAgainstRules(rules, csv.GetField("Description")); + + statements.Add(new Statement() { + Date = DateTime.ParseExact(csv.GetField("Date"), "dd/MM/yyyy", CultureInfo.InvariantCulture), + OriginalDescription = csv.GetField("Description"), + Description = ruleValues.Description, + Category = ruleValues.Category, + Value = value + }); + break; + + case Bank.NAB: + Console.WriteLine("Unimplemented"); return 1; - } - else if (header == "Date,Description,Debits and credits,Balance") { - bank = Bank.ME; - } - else if (header == "Whatever NAB uses I guess") { - bank = Bank.NAB; - } - while (csv.Read()) { - switch (bank) { - case Bank.ME: - double value = double.Parse(csv.GetField("Debits and credits").TrimStart().Replace("$", "")); - statements.Add(new Statement() { - Date = DateTime.ParseExact(csv.GetField("Date"), "dd/MM/yyyy", CultureInfo.InvariantCulture), - Description = csv.GetField("Description"), - Category = "Unknown", - Value = value - }); - break; + case Bank.Other: + Console.WriteLine("Unknown bank!"); + return 1; - default: - Console.WriteLine(":("); - return 1; - } - } - - foreach (var statement in statements) { - Console.WriteLine(statement); - } + default: + Console.WriteLine(":("); + return 1; } } + foreach (var statement in statements) { + Console.WriteLine(statement); + } + return 0; } + + private static (string Category, string Description) MatchAgainstRules(List rules, string value) { + foreach (var rule in rules) { + if (rule.Check(value)) { + return (rule.Category, rule.Description); + } + } + + return (null, null); + } } } diff --git a/Statement.cs b/Statement.cs index 855b75e..587c909 100644 --- a/Statement.cs +++ b/Statement.cs @@ -1,4 +1,5 @@ using System; +using System.Text; namespace BunyMuny { public class Statement { @@ -14,7 +15,16 @@ namespace BunyMuny { public string Category; public override string ToString() { - return $"${Math.Abs(Value)} {(Value < 0 ? "to" : "from")} {(Description)} on {Date.ToString("MMM d yyyy")}"; + // e.g.: Debit: $5.00 --> Lynnear Software (Personal) on Apr 2 2020 + return String.Format( + "{0} ${1:0.##} {2} {3} ({4}) on {5:MMM d yyyy}", + Value < 0 ? "Credit:" : "Debit: ", + Math.Abs(Value), + Value < 0 ? "-->" : "<--", + Description, + Category ?? "Unknown", + Date + ); } } }