Compare commits

..

2 commits

Author SHA1 Message Date
9a1272c085
don't warn about missing xml comments
All checks were successful
continuous-integration/drone/push Build is passing
2020-09-27 13:08:55 +10:00
6aa366c714
use a CSV file for the rules instead 2020-09-27 13:06:44 +10:00
4 changed files with 19 additions and 28 deletions

2
.gitignore vendored
View file

@ -2,4 +2,4 @@
bin/ bin/
obj/ obj/
test.csv test.csv
rules.json rules.csv

View file

@ -20,10 +20,8 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization; using System.Globalization;
using System.IO; using System.IO;
using System.Text.Json;
using System.CommandLine.DragonFruit; using System.CommandLine.DragonFruit;
using System.Linq; using System.Linq;
using System.Text.Json.Serialization;
using CsvHelper; using CsvHelper;
namespace BunyMuny { namespace BunyMuny {
@ -32,18 +30,16 @@ namespace BunyMuny {
/// BunyMuny parses the CSV output of various bank statement listings and converts it to something more human readable with nice visualisations. /// BunyMuny parses the CSV output of various bank statement listings and converts it to something more human readable with nice visualisations.
/// </summary> /// </summary>
/// <param name="file">The CSV file to read</param> /// <param name="file">The CSV file to read</param>
/// <param name="ruleFile">The JSON file to use for rules when parsing statement descriptions</param> /// <param name="ruleFile">The CSV file to use for rules when parsing statement descriptions</param>
/// <returns></returns> /// <returns></returns>
private static int Main(string file = "test.csv", string ruleFile = "rules.json") { private static int Main(string file = "test.csv", string ruleFile = "rules.csv") {
var bank = Bank.Other; var bank = Bank.Other;
var statements = new List<Statement>(); var statements = new List<Statement>();
List<Rule> rules; List<Rule> rules;
using (var ruleStreamReader = new StreamReader(ruleFile)) { using (var ruleStreamReader = new StreamReader(ruleFile)) {
var jsonOptions = new JsonSerializerOptions(); using var ruleCsv = new CsvReader(ruleStreamReader,CultureInfo.InvariantCulture);
jsonOptions.Converters.Add(new JsonStringEnumConverter(JsonNamingPolicy.CamelCase)); rules = ruleCsv.GetRecords<Rule>().ToList();
jsonOptions.WriteIndented = true;
rules = JsonSerializer.Deserialize<List<Rule>>(ruleStreamReader.ReadToEnd(), jsonOptions);
} }
using var sr = new StreamReader(file); using var sr = new StreamReader(file);
@ -73,14 +69,13 @@ namespace BunyMuny {
switch (bank) { switch (bank) {
case Bank.ME: case Bank.ME:
var value = double.Parse(csv.GetField("Debits and credits").TrimStart().Replace("$", "")); var value = double.Parse(csv.GetField("Debits and credits").TrimStart().Replace("$", ""));
// SHOW PET THIS var (category, description) = MatchAgainstRules(rules, csv.GetField("Description"));
var ruleValues = 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),
OriginalDescription = csv.GetField("Description"), OriginalDescription = csv.GetField("Description"),
Description = ruleValues.Description, Description = description,
Category = ruleValues.Category, Category = category,
Value = value Value = value
}); });
break; break;
@ -100,7 +95,7 @@ namespace BunyMuny {
} }
foreach (var statement in statements. foreach (var statement in statements.
Where(s => s.Category != null). // Where(s => s.Category != null).
OrderBy(s => s.Date)) { OrderBy(s => s.Date)) {
Console.WriteLine(statement); Console.WriteLine(statement);
} }

View file

@ -13,24 +13,19 @@ dotnet build
## The rules file ## The rules file
By default, BunyMuny checks for rules in `rules.json` in the current directory. An example rules file might look like this: By default, BunyMuny checks for rules in `rules.json` in the current directory. An example rules file might look like this:
```json ```csv
[ Match,Value,Category,Description,CaseSensitive
{ Start,Purchase Cash Converters,Personal,Cashies,true
"Match":"Start",
"Value": "Purchase Cash Converters",
"Category":"Personal",
"Description":"Cashies"
}
]
``` ```
This means that any statement that starts with "Purchase Cash Converters" will be assigned the category "Personal", and the description "Cashies". This means that any statement that starts with "Purchase Cash Converters" (case sensitive) will be assigned the category "Personal", and the description "Cashies".
- `"Match"` specifies the type of matching to perform. It can be any of the following: - `Match` specifies the type of matching to perform. It can be any of the following:
- "Start": Match the start of the description. - "Start": Match the start of the description.
- "End": Match the end of the description. - "End": Match the end of the description.
- "Contains": Match anywhere in the description. - "Contains": Match anywhere in the description.
- "Exact": Only match if the bank statement's description is exactly equal to the provided value. - "Exact": Only match if the bank statement's description is exactly equal to the provided value.
- "Regex": Match with [regular expressions](https://en.wikipedia.org/wiki/Regular_expression). - "Regex": Match with [regular expressions](https://en.wikipedia.org/wiki/Regular_expression).
- `"Value"` specifies the value to match against. - `Value` specifies the value to match against.
- `"Category"` is the category that the statement should be filed under. - `Category` is the category that the statement should be filed under.
- `"Description"` is a short description to distinguish the particular merchant. - `Description` is a short description to distinguish the particular merchant.
- `CaseSensitive` specifies whether or not the match should be case sensitive.

View file

@ -3,6 +3,7 @@
<PropertyGroup> <PropertyGroup>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<TargetFramework>netcoreapp3.1</TargetFramework> <TargetFramework>netcoreapp3.1</TargetFramework>
<NoWarn>1591</NoWarn>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>