This commit is contained in:
parent
0800fa8cca
commit
42be0862ca
2 changed files with 54 additions and 29 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -2,5 +2,6 @@
|
||||||
bin/
|
bin/
|
||||||
obj/
|
obj/
|
||||||
test.csv
|
test.csv
|
||||||
|
test2.csv
|
||||||
rules.csv
|
rules.csv
|
||||||
.~lock*
|
.~lock*
|
||||||
|
|
82
Program.cs
82
Program.cs
|
@ -22,7 +22,9 @@ using System.Globalization;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.CommandLine.DragonFruit;
|
using System.CommandLine.DragonFruit;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
using CsvHelper;
|
using CsvHelper;
|
||||||
|
using CsvHelper.Configuration;
|
||||||
|
|
||||||
namespace BunyMuny {
|
namespace BunyMuny {
|
||||||
internal class Program {
|
internal class Program {
|
||||||
|
@ -38,38 +40,53 @@ namespace BunyMuny {
|
||||||
List<Rule> rules;
|
List<Rule> rules;
|
||||||
|
|
||||||
using (var ruleStreamReader = new StreamReader(ruleFile)) {
|
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();
|
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 sr = new StreamReader(file);
|
||||||
using var csv = new CsvReader(sr, CultureInfo.InvariantCulture);
|
using var csv = new CsvReader(sr, CultureInfo.InvariantCulture);
|
||||||
|
csv.Configuration.HasHeaderRecord = false;
|
||||||
|
|
||||||
csv.Read();
|
var nabRegex = new Regex(@"^\d\d \w{3} \d\d");
|
||||||
csv.ReadHeader();
|
|
||||||
|
|
||||||
// get the first line of the CSV file (the header) as a string
|
if (header == null) {
|
||||||
var header = csv.Parser.Context.RawRecord.Trim();
|
Console.WriteLine("File is empty 0uo");
|
||||||
switch (header) {
|
return 1;
|
||||||
case null:
|
}
|
||||||
Console.WriteLine("File is empty 0uo");
|
else if (header == "Date,Description,Debits and credits,Balance") {
|
||||||
return 1;
|
bank = Bank.ME;
|
||||||
case "Date,Description,Debits and credits,Balance":
|
csv.Configuration.HasHeaderRecord = true;
|
||||||
bank = Bank.ME;
|
// read in the header to allow for accessing fields by name rather than by index
|
||||||
break;
|
csv.Read();
|
||||||
case "Whatever NAB uses I guess":
|
csv.ReadHeader();
|
||||||
bank = Bank.NAB;
|
}
|
||||||
break;
|
else if (nabRegex.IsMatch(header)) {
|
||||||
default:
|
// NAB exports don't have headers
|
||||||
Console.WriteLine($"Unknown header: [{header}]");
|
// records look like "02 Apr 20,23.00,,,MISCELLANEOUS DEBIT,V1234 02/09 PAYPAL Sydney,1234.56
|
||||||
break;
|
// 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()) {
|
while (csv.Read()) {
|
||||||
|
decimal value;
|
||||||
|
string category;
|
||||||
|
string description;
|
||||||
|
|
||||||
switch (bank) {
|
switch (bank) {
|
||||||
case Bank.ME:
|
case Bank.ME:
|
||||||
var value = decimal.Parse(csv.GetField("Debits and credits").TrimStart().Replace("$", ""));
|
value = decimal.Parse(csv.GetField("Debits and credits").TrimStart().Replace("$", ""));
|
||||||
var (category, description) = MatchAgainstRules(rules, csv.GetField("Description"));
|
(category, description) = MatchAgainstRules(rules, csv.GetField("Description"));
|
||||||
|
|
||||||
statements.Add(new Statement() {
|
statements.Add(new Statement() {
|
||||||
Date = DateTime.ParseExact(csv.GetField("Date"), "dd/MM/yyyy", CultureInfo.InvariantCulture),
|
Date = DateTime.ParseExact(csv.GetField("Date"), "dd/MM/yyyy", CultureInfo.InvariantCulture),
|
||||||
|
@ -81,8 +98,17 @@ namespace BunyMuny {
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Bank.NAB:
|
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:
|
case Bank.Other:
|
||||||
Console.WriteLine("Unknown bank!");
|
Console.WriteLine("Unknown bank!");
|
||||||
|
@ -102,16 +128,14 @@ namespace BunyMuny {
|
||||||
|
|
||||||
Console.WriteLine("Summary:");
|
Console.WriteLine("Summary:");
|
||||||
Console.WriteLine("==================");
|
Console.WriteLine("==================");
|
||||||
var summaries = statements.
|
var summaries = statements.GroupBy(s => s.Category). // group statements by category
|
||||||
GroupBy(s => s.Category). // group statements by category
|
Select(summary => new {
|
||||||
Select(summary => new { // and then select:
|
// and then select:
|
||||||
Name = summary.First().Category?? "Other", // the name of the category...
|
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
|
Total = summary.Sum(s => s.Value).ToString() // ...and the sum of all the statement's values in that category
|
||||||
});
|
});
|
||||||
|
|
||||||
var longestCategoryName = summaries.
|
var longestCategoryName = summaries.ToList().Max(summary => summary.Name.Length);
|
||||||
ToList().
|
|
||||||
Max(summary => summary.Name.Length);
|
|
||||||
|
|
||||||
foreach (var summary in summaries) {
|
foreach (var summary in summaries) {
|
||||||
Console.WriteLine("{0}: {1}", summary.Name.PadLeft(longestCategoryName), summary.Total);
|
Console.WriteLine("{0}: {1}", summary.Name.PadLeft(longestCategoryName), summary.Total);
|
||||||
|
|
Loading…
Reference in a new issue