diff --git a/fixtures/transactions.csv b/fixtures/transactions.csv new file mode 100644 index 0000000..ed143eb --- /dev/null +++ b/fixtures/transactions.csv @@ -0,0 +1,3 @@ +Transaction ID,Date,Time,Type,Name,Emoji,Category,Amount,Currency,Local amount,Local currency,Notes and #tags,Address,Receipt,Description,Category split +tx_0000AhmcbFKVlkAFbeZuAk,11/05/2024,13:41:47,Pot transfer,Savings Pot,,Savings,-1.4,GBP,-1.4,GBP,,,,Round up, +tx_0000AhmgdZ0FzkVZou77Ev,11/05/2024,14:27:01,Card payment,D,💊,C,-100.42,GBP,-100.42,GBP,,B,,A,"Groceries:15.00,Personal care:10.00" diff --git a/fixtures/transactions.json b/fixtures/transactions.json new file mode 100644 index 0000000..5932bb0 --- /dev/null +++ b/fixtures/transactions.json @@ -0,0 +1,38 @@ +[ + [ + "tx_0000AhmcbFKVlkAFbeZuAk", + "2024-05-11T00:00:00.000Z", + "1899-12-30T13:41:47.000Z", + "Pot transfer", + "Savings Pot", + "", + "Savings", + -1.4, + "GBP", + -1.4, + "GBP", + "", + "", + "", + "Round up", + "" + ], + [ + "tx_0000AhmgdZ0FzkVZou77Ev", + "2024-05-11T00:00:00.000Z", + "1899-12-30T14:27:01.000Z", + "Card payment", + "D", + "💊", + "C", + -100.42, + "GBP", + -100.42, + "GBP", + "", + "B", + "", + "A", + "Groceries:15.00,Personal care:10.00" + ] +] \ No newline at end of file diff --git a/src/ingestion/ingestion_logic.rs b/src/ingestion/ingestion_logic.rs index cc316c8..aa77674 100644 --- a/src/ingestion/ingestion_logic.rs +++ b/src/ingestion/ingestion_logic.rs @@ -40,7 +40,7 @@ pub struct MonzoRow { emoji: Option, description: Option, transaction_type: String, - title: String, + title: Option, timestamp: NaiveDateTime, transaction_id: String, } @@ -153,7 +153,7 @@ pub fn from_json_row(row: Vec) -> anyhow::Result { Ok(MonzoRow { transaction_id: json_required_str(&row[headings::TRANSACTION_ID], "Transaction ID")?, - title: json_required_str(&row[headings::NAME], "Title")?, + title: json_opt(&row[headings::NAME]), transaction_type: json_required_str(&row[headings::TYPE], "Transaction type")?, description: json_opt(&row[headings::DESCRIPTION]), emoji: json_opt(&row[headings::EMOJI]), @@ -166,6 +166,32 @@ pub fn from_json_row(row: Vec) -> anyhow::Result { }) } +#[test] +fn test_json() { + let json = include_str!("../../fixtures/transactions.json"); + let csv = include_str!("../../fixtures/transactions.csv"); + + let json: Vec> = serde_json::from_str(json).unwrap(); + let mut csv_reader = csv::Reader::from_reader(csv.as_bytes()); + + let json_rows = json.iter() + .map(|row| from_json_row(row.clone())) + .collect::, anyhow::Error>>() + .unwrap(); + + let csv_rows = csv_reader.records() + .map(|record| from_csv_row(record.unwrap())) + .collect::, anyhow::Error>>() + .unwrap(); + + assert_eq!(csv_rows.len(), json_rows.len(), "Different number of rows"); + + for (i, (json_row, csv_row)) in json_rows.iter().zip(csv_rows.iter()).enumerate() { + assert_eq!(json_row, csv_row, "Row {} is different", i); + assert_eq!(json_row.compute_hash(), csv_row.compute_hash(), "Row {} hash are different", i); + } +} + fn csv_opt(s: &str) -> Option { match s { "" => None, @@ -185,7 +211,7 @@ pub fn from_csv_row(row: StringRecord) -> anyhow::Result { Ok(MonzoRow { timestamp, transaction_id: row[headings::TRANSACTION_ID].to_string(), - title: row[headings::NAME].to_string(), + title: csv_opt(&row[headings::NAME]), transaction_type: row[headings::TYPE].to_string(), description: csv_opt(&row[headings::DESCRIPTION]), emoji: csv_opt(&row[headings::EMOJI]),