cleaner(ish) code, support for rules

This commit is contained in:
Lynne Megido 2020-09-26 22:45:07 +10:00
parent 8b60f2b602
commit e45c36e6e3
Signed by: lynnesbian
GPG Key ID: F0A184B5213D9F90
3 changed files with 87 additions and 42 deletions

View File

@ -1,6 +1,7 @@
namespace BunyMuny {
public enum Bank {
ME,
NAB
NAB,
Other
}
}

View File

@ -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 {
/// <summary>
/// BunyMuny parses the CSV output of various bank statement listings and converts it to something more human readable with nice visualisations.
/// </summary>
/// <param name="file">The CSV file to read</param>
/// <param name="rules">The JSON file to use for rules when parsing statement descriptions</param>
/// <param name="ruleFile">The JSON file to use for rules when parsing statement descriptions</param>
/// <returns></returns>
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<Statement>();
List<Rule> 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<List<Rule>>(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<Rule> rules, string value) {
foreach (var rule in rules) {
if (rule.Check(value)) {
return (rule.Category, rule.Description);
}
}
return (null, null);
}
}
}

View File

@ -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
);
}
}
}