diff options
| author | Joe Carstairs <me@joeac.net> | 2024-10-22 07:46:43 +0100 |
|---|---|---|
| committer | Joe Carstairs <me@joeac.net> | 2024-10-22 07:46:43 +0100 |
| commit | 5824c353f978a5b9ab9696451ee3014c6a614ffc (patch) | |
| tree | bc39754ca417e84eb00b0aa9a1ae2330da0bf282 /backend/importers | |
| parent | 7adaace61a4fe983eba5ded3aaaf624ce6fb9014 (diff) | |
Refactors importer to use core stuff
Diffstat (limited to 'backend/importers')
39 files changed, 610 insertions, 577 deletions
diff --git a/backend/importers/actualbudget/Cargo.lock b/backend/importers/actualbudget/Cargo.lock index 63a53b9..1a461d5 100644 --- a/backend/importers/actualbudget/Cargo.lock +++ b/backend/importers/actualbudget/Cargo.lock @@ -6,8 +6,11 @@ version = 3 name = "actualbudget" version = "0.1.0" dependencies = [ + "anyhow", + "budgeting_app_core", "chrono", - "sqlite", + "diesel", + "libsqlite3-sys", ] [[package]] @@ -26,12 +29,29 @@ dependencies = [ ] [[package]] +name = "anyhow" +version = "1.0.90" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37bf3594c4c988a53154954629820791dde498571819ae4ca50ca811e060cc95" + +[[package]] name = "autocfg" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] +name = "budgeting_app_core" +version = "0.1.0" +dependencies = [ + "anyhow", + "chrono", + "diesel", + "diesel_migrations", + "serde", +] + +[[package]] name = "bumpalo" version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -74,6 +94,138 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] +name = "darling" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn", +] + +[[package]] +name = "darling_macro" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" +dependencies = [ + "darling_core", + "quote", + "syn", +] + +[[package]] +name = "deranged" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +dependencies = [ + "powerfmt", +] + +[[package]] +name = "diesel" +version = "2.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "158fe8e2e68695bd615d7e4f3227c0727b151330d3e253b525086c348d055d5e" +dependencies = [ + "diesel_derives", + "libsqlite3-sys", + "time", +] + +[[package]] +name = "diesel_derives" +version = "2.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7f2c3de51e2ba6bf2a648285696137aaf0f5f487bcbea93972fe8a364e131a4" +dependencies = [ + "diesel_table_macro_syntax", + "dsl_auto_type", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "diesel_migrations" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a73ce704bad4231f001bff3314d91dce4aba0770cee8b233991859abc15c1f6" +dependencies = [ + "diesel", + "migrations_internals", + "migrations_macros", +] + +[[package]] +name = "diesel_table_macro_syntax" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "209c735641a413bc68c4923a9d6ad4bcb3ca306b794edaa7eb0b3228a99ffb25" +dependencies = [ + "syn", +] + +[[package]] +name = "dsl_auto_type" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5d9abe6314103864cc2d8901b7ae224e0ab1a103a0a416661b4097b0779b607" +dependencies = [ + "darling", + "either", + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "either" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "hashbrown" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] name = "iana-time-zone" version = "0.1.60" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -97,6 +249,28 @@ dependencies = [ ] [[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "indexmap" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "itoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" + +[[package]] name = "js-sys" version = "0.3.70" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -112,12 +286,56 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d8adc4bb1803a324070e64a98ae98f38934d91957a99cfb3a43dcbc01bc56439" [[package]] +name = "libsqlite3-sys" +version = "0.30.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e99fb7a497b1e3339bc746195567ed8d3e24945ecd636e3619d20b9de9e9149" +dependencies = [ + "cc", + "pkg-config", + "vcpkg", +] + +[[package]] name = "log" version = "0.4.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" [[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "migrations_internals" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd01039851e82f8799046eabbb354056283fb265c8ec0996af940f4e85a380ff" +dependencies = [ + "serde", + "toml", +] + +[[package]] +name = "migrations_macros" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffb161cc72176cb37aa47f1fc520d3ef02263d67d661f44f05d05a079e1237fd" +dependencies = [ + "migrations_internals", + "proc-macro2", + "quote", +] + +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + +[[package]] name = "num-traits" version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -139,6 +357,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" [[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] name = "proc-macro2" version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -177,48 +401,100 @@ dependencies = [ ] [[package]] +name = "serde_spanned" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" +dependencies = [ + "serde", +] + +[[package]] name = "shlex" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] -name = "sqlite" -version = "0.36.1" +name = "strsim" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dfe6fb16f2bee6452feeb4d12bfa404fbcd3cfc121b2950e501d1ae9cae718e" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "syn" +version = "2.0.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed" dependencies = [ - "sqlite3-sys", + "proc-macro2", + "quote", + "unicode-ident", ] [[package]] -name = "sqlite3-src" -version = "0.6.1" +name = "time" +version = "0.3.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "174d4a6df77c27db281fb23de1a6d968f3aaaa4807c2a1afa8056b971f947b4a" +checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" dependencies = [ - "cc", - "pkg-config", + "deranged", + "itoa", + "num-conv", + "powerfmt", + "serde", + "time-core", + "time-macros", ] [[package]] -name = "sqlite3-sys" -version = "0.17.0" +name = "time-core" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" + +[[package]] +name = "time-macros" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3901ada7090c3c3584dc92ec7ef1b7091868d13bfe6d7de9f0bcaffee7d0ade5" +checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" dependencies = [ - "sqlite3-src", + "num-conv", + "time-core", ] [[package]] -name = "syn" -version = "2.0.77" +name = "toml" +version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f35bcdf61fd8e7be6caf75f429fdca8beb3ed76584befb503b1569faee373ed" +checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.22.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", ] [[package]] @@ -228,6 +504,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe" [[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] name = "wasm-bindgen" version = "0.2.93" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -354,3 +636,12 @@ name = "windows_x86_64_msvc" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "winnow" +version = "0.6.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b" +dependencies = [ + "memchr", +] diff --git a/backend/importers/actualbudget/Cargo.toml b/backend/importers/actualbudget/Cargo.toml index 7cc3e1b..6453e0a 100644 --- a/backend/importers/actualbudget/Cargo.toml +++ b/backend/importers/actualbudget/Cargo.toml @@ -4,5 +4,8 @@ version = "0.1.0" edition = "2021" [dependencies] -sqlite = "0.36.1" +anyhow = { version = "1.0.0" } +budgeting_app_core = { path = "../../core" } chrono = { version = "0.4.38", features = ["serde"] } +diesel = { version = "2.2.0", features = ["sqlite"] } +libsqlite3-sys = { version = "0.30.1", features = ["bundled"] } diff --git a/backend/importers/actualbudget/src/getters.rs b/backend/importers/actualbudget/src/getters.rs deleted file mode 100644 index 88a660d..0000000 --- a/backend/importers/actualbudget/src/getters.rs +++ /dev/null @@ -1,61 +0,0 @@ -use crate::models::actualbudget::{ - account::Account, category::Category, date::Date, transaction::Transaction, - zero_budget::ZeroBudget, -}; -use sqlite::Connection; - -pub fn get_first_transaction_date(connection: &Connection) -> Date { - let mut statement = connection - .prepare("select min(date) as min_date from transactions;") - .unwrap(); - match statement.next() { - Ok(sqlite::State::Row) => Date::from_date_row(&statement, "min_date"), - err => panic!("Could not find first transaction date: {:?}", err), - } -} - -pub fn get_accounts(connection: &Connection) -> Vec<Account> { - let mut statement = connection.prepare("select * from accounts;").unwrap(); - let mut accounts = Vec::<Account>::new(); - while let Ok(sqlite::State::Row) = statement.next() { - accounts.push(Account::from_row(&statement)); - } - accounts -} - -pub fn get_categories(connection: &Connection) -> Vec<Category> { - let mut statement = connection.prepare("select * from categories;").unwrap(); - let mut categories = Vec::<Category>::new(); - while let Ok(sqlite::State::Row) = statement.next() { - categories.push(Category::from_row(&statement)); - } - categories -} - -pub fn get_zero_budgets(connection: &Connection) -> Vec<ZeroBudget> { - let mut statement = connection.prepare("select * from zero_budgets;").unwrap(); - let mut zero_budgets = Vec::<ZeroBudget>::new(); - while let Ok(sqlite::State::Row) = statement.next() { - zero_budgets.push(ZeroBudget::from_row(&statement)); - } - zero_budgets -} - -pub fn get_category_transfers_account(connection: &Connection) -> Account { - let mut statement = connection - .prepare("select * from accounts where name = 'Category transfers';") - .unwrap(); - match statement.next() { - Ok(sqlite::State::Row) => Account::from_row(&statement), - _ => panic!("Could not determine category transfers account"), - } -} - -pub fn get_transactions(connection: &Connection) -> Vec<Transaction> { - let mut statement = connection.prepare("select * from transactions;").unwrap(); - let mut transactions = Vec::<Transaction>::new(); - while let Ok(sqlite::State::Row) = statement.next() { - transactions.push(Transaction::from_row(&statement)); - } - transactions -} diff --git a/backend/importers/actualbudget/src/inserters.rs b/backend/importers/actualbudget/src/inserters.rs deleted file mode 100644 index 179de97..0000000 --- a/backend/importers/actualbudget/src/inserters.rs +++ /dev/null @@ -1,120 +0,0 @@ -use crate::models::budgeting_app::{ - account::Account, budget_update::BudgetUpdate, category::Category, - category_transfer::CategoryTransfer, transaction::Transaction, - transaction_categorisation::TransactionCategorisation, -}; -use sqlite::Connection; - -pub fn insert_accounts(connection: &Connection, accounts: &[Account]) { - for account in accounts { - let str = format!( - " - insert into - accounts(id, name, opening_balance, opening_date) - values({0}, '{1}', {2}, '{3}'); - ", - account.id, - escape_str(&account.name), - account.opening_balance, - account.opening_date - ); - connection.execute(str).unwrap(); - } -} - -pub fn insert_categories(connection: &Connection, categories: &[Category]) { - for category in categories { - let str = format!( - " - insert into - categories(id, name) - values({0}, '{1}'); - ", - category.id, - escape_str(&category.name), - ); - connection.execute(str).unwrap(); - } -} - -pub fn insert_budget_updates(connection: &Connection, budget_updates: &[BudgetUpdate]) { - for budget_update in budget_updates { - let str = format!( - " - insert into - budget_updates(id, category_id, date, new_budget, new_period) - values({0}, {1}, '{2}', {3}, {4}); - ", - budget_update.id, - budget_update.category_id, - budget_update.date, - budget_update.new_budget, - budget_update.new_period, - ); - connection.execute(str).unwrap(); - } -} - -pub fn insert_transactions(connection: &Connection, transactions: &[Transaction]) { - for transaction in transactions { - let str = format!( - " - insert into - transactions(id, description, payee, quantity, date, account_id) - values({0}, '{1}', '{2}', {3}, '{4}', {5}); - ", - transaction.id, - escape_str(&transaction.description), - escape_str(&transaction.payee), - transaction.quantity, - transaction.date, - transaction.account_id, - ); - connection - .execute(str.clone()) - .unwrap_or_else(|_| panic!("{}", &str.clone())); - } -} - -pub fn insert_transaction_categorisations( - connection: &Connection, - transaction_categorisations: &[TransactionCategorisation], -) { - for transaction_categorisation in transaction_categorisations { - let str = format!( - " - insert into - transaction_categorisations(id, description, quantity, transaction_id, category_id) - values({0}, '{1}', {2}, {3}, {4}); - ", - transaction_categorisation.id, - escape_str(&transaction_categorisation.description), - transaction_categorisation.quantity, - transaction_categorisation.transaction_id, - transaction_categorisation.category_id, - ); - connection.execute(str).unwrap(); - } -} - -pub fn insert_category_transfers(connection: &Connection, category_transfers: &[CategoryTransfer]) { - for category_transfer in category_transfers { - let str = format!( - " - insert into - category_transfers(id, description, quantity, from_category_id, to_category_id) - values({0}, '{1}', {2}, {3}, {4}); - ", - category_transfer.id, - escape_str(&category_transfer.description), - category_transfer.quantity, - category_transfer.from_category_id, - category_transfer.to_category_id, - ); - connection.execute(str).unwrap(); - } -} - -fn escape_str(str: &str) -> String { - str.replace("'", "''") -} diff --git a/backend/importers/actualbudget/src/main.rs b/backend/importers/actualbudget/src/main.rs index 495216e..84f5102 100644 --- a/backend/importers/actualbudget/src/main.rs +++ b/backend/importers/actualbudget/src/main.rs @@ -1,57 +1,45 @@ -pub mod getters; -pub mod inserters; pub mod models; +pub mod queries; +pub mod schema; pub mod transformers; -use getters::*; -use inserters::*; -use sqlite::Connection; +use anyhow::Result; +use budgeting_app_core::queries as budgeting_app_queries; +use diesel::{Connection, SqliteConnection}; +use queries::{actualbudget_accounts::{get_all_actualbudget_accounts, get_category_transfers_account}, actualbudget_categories::get_all_actualbudget_categories, actualbudget_transactions::{get_all_actualbudget_transactions, get_first_actualbudget_transaction_date}, actualbudget_zero_budgets::get_all_actualbudget_zero_budgets}; -fn main() { - let connection_src = sqlite::open("in.sqlite").unwrap(); - let connection_dest = sqlite::open("out.sqlite").unwrap(); +fn main() -> Result<()> { + let connection_src = &mut SqliteConnection::establish("in.sqlite").unwrap(); + let connection_dest = &mut SqliteConnection::establish("out.sqlite").unwrap(); - let actualbudget_first_transaction_date = get_first_transaction_date(&connection_src); - let actualbudget_accounts = get_accounts(&connection_src); - let actualbudget_categories = get_categories(&connection_src); - let actualbudget_zero_budgets = get_zero_budgets(&connection_src); - let actualbudget_category_transfers_account = get_category_transfers_account(&connection_src); - let actualbudget_transactions = get_transactions(&connection_src); + let actualbudget_first_transaction_date = get_first_actualbudget_transaction_date(connection_src).map_err(|e| e.to_string())?; + let actualbudget_accounts = get_all_actualbudget_accounts(connection_src).map_err(|e| e.to_string())?; + let actualbudget_categories = get_all_actualbudget_categories(connection_src).map_err(|e| e.to_string())?; + let actualbudget_zero_budgets = get_all_actualbudget_zero_budgets(connection_src).map_err(|e| e.to_string())?; + let actualbudget_category_transfers_account = get_category_transfers_account(connection_src).map_err(|e| e.to_string())?; + let actualbudget_transactions = get_all_actualbudget_transactions(connection_src).map_err(|e| e.to_string())?; - let first_transaction_date = transformers::transform_date(&actualbudget_first_transaction_date); + let first_transaction_date = transformers::transform_date(&actualbudget_first_transaction_date)?; let accounts = transformers::transform_accounts(&actualbudget_accounts, &first_transaction_date); let categories = transformers::transform_categories(&actualbudget_categories); - let budget_updates = transformers::transform_budget_updates(&actualbudget_zero_budgets); + let budget_updates = transformers::transform_budget_updates(&actualbudget_zero_budgets)?; let (transactions, transaction_categorisations) = transformers::transform_transactions( &actualbudget_transactions, &actualbudget_category_transfers_account, - ); + )?; let category_transfers = transformers::transform_category_transfers( &actualbudget_transactions, &actualbudget_category_transfers_account, ); - clear_destination(&connection_dest); - insert_accounts(&connection_dest, &accounts); - insert_categories(&connection_dest, &categories); - insert_budget_updates(&connection_dest, &budget_updates); - insert_transactions(&connection_dest, &transactions); - insert_transaction_categorisations(&connection_dest, &transaction_categorisations); - insert_category_transfers(&connection_dest, &category_transfers); -} + budgeting_app_queries::clear::clear(connection_dest).map_err(|e| e.to_string())?; + budgeting_app_queries::accounts::insert_accounts(&accounts, connection_dest).map_err(|e| e.to_string())?; + budgeting_app_queries::categories::insert_categories(&categories, connection_dest).map_err(|e| e.to_string())?; + budgeting_app_queries::budget_updates::insert_budget_updates(&budget_updates, connection_dest).map_err(|e| e.to_string())?; + budgeting_app_queries::transactions::insert_transactions(&transactions, connection_dest).map_err(|e| e.to_string())?; + budgeting_app_queries::transaction_categorisations::insert_transaction_categorisations(&transaction_categorisations, connection_dest).map_err(|e| e.to_string())?; + budgeting_app_queries::category_transfers::insert_category_transfers(&category_transfers, connection_dest).map_err(|e| e.to_string())?; -fn clear_destination(connection: &Connection) { - connection - .execute( - " - delete from accounts; - delete from budget_updates; - delete from categories; - delete from transaction_categorisations; - delete from category_transfers; - delete from transactions; - ", - ) - .unwrap(); + Ok(()) } diff --git a/backend/importers/actualbudget/src/models/actualbudget/account.rs b/backend/importers/actualbudget/src/models/actualbudget/account.rs deleted file mode 100644 index 727c082..0000000 --- a/backend/importers/actualbudget/src/models/actualbudget/account.rs +++ /dev/null @@ -1,15 +0,0 @@ -use sqlite::Statement; - -pub struct Account { - pub id: String, - pub name: String, -} - -impl Account { - pub fn from_row(statement: &Statement) -> Account { - Account { - id: statement.read("id").unwrap(), - name: statement.read("name").unwrap(), - } - } -} diff --git a/backend/importers/actualbudget/src/models/actualbudget/category.rs b/backend/importers/actualbudget/src/models/actualbudget/category.rs deleted file mode 100644 index 21fdf80..0000000 --- a/backend/importers/actualbudget/src/models/actualbudget/category.rs +++ /dev/null @@ -1,15 +0,0 @@ -use sqlite::Statement; - -pub struct Category { - pub id: String, - pub name: String, -} - -impl Category { - pub fn from_row(statement: &Statement) -> Category { - Category { - id: statement.read("id").unwrap(), - name: statement.read("name").unwrap(), - } - } -} diff --git a/backend/importers/actualbudget/src/models/actualbudget/date.rs b/backend/importers/actualbudget/src/models/actualbudget/date.rs deleted file mode 100644 index bd37c46..0000000 --- a/backend/importers/actualbudget/src/models/actualbudget/date.rs +++ /dev/null @@ -1,69 +0,0 @@ -use sqlite::Statement; - -#[derive(Clone, PartialEq, Eq)] -pub struct Date { - value: i64, - last_date_part: DatePart, -} - -#[derive(Clone, PartialOrd, PartialEq, Eq)] -enum DatePart { - MonthOfYear, - DayOfMonth, -} - -// actualbudget represents dates as numbers which "look like" the corresponding -// ISO string when written in decimal. E.g. 20240901 represents 1 Sep 2024. It -// also uses month specifiers, e.g. 202409 to represent Sep 2024. In this case, -// we interpret this as the first day of the month, e.g. 2024-09-01. - -impl Date { - pub fn from_date_row(statement: &Statement, column: &str) -> Date { - Date { - value: statement.read::<i64, _>(column).unwrap(), - last_date_part: DatePart::DayOfMonth, - } - } - - pub fn from_month_row(statement: &Statement, column: &str) -> Date { - Date { - value: statement.read::<i64, _>(column).unwrap() * 100 + 1, - last_date_part: DatePart::MonthOfYear, - } - } - - pub fn to_iso(&self) -> String { - format!("{0}-{1}-{2}", self.year(), self.month(), self.day()) - } - - pub fn year(&self) -> String { - self.value.to_string()[0..4].to_string() - } - - pub fn month(&self) -> String { - self.value.to_string()[4..6].to_string() - } - - pub fn day(&self) -> String { - self.dayified_value().to_string()[6..8].to_string() - } - - fn dayified_value(&self) -> i64 { - match self.last_date_part { - DatePart::DayOfMonth => self.value, - DatePart::MonthOfYear => self.value * 100 + 1, - } - } -} - -impl Ord for Date { - fn cmp(&self, other: &Self) -> std::cmp::Ordering { - self.dayified_value().cmp(&other.dayified_value()) - } -} - -impl PartialOrd for Date { - fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> { - Some(self.cmp(other)) - } -} diff --git a/backend/importers/actualbudget/src/models/actualbudget/mod.rs b/backend/importers/actualbudget/src/models/actualbudget/mod.rs deleted file mode 100644 index 5ca472f..0000000 --- a/backend/importers/actualbudget/src/models/actualbudget/mod.rs +++ /dev/null @@ -1,5 +0,0 @@ -pub mod account; -pub mod category; -pub mod date; -pub mod transaction; -pub mod zero_budget; diff --git a/backend/importers/actualbudget/src/models/actualbudget/transaction.rs b/backend/importers/actualbudget/src/models/actualbudget/transaction.rs deleted file mode 100644 index 0a21cfe..0000000 --- a/backend/importers/actualbudget/src/models/actualbudget/transaction.rs +++ /dev/null @@ -1,43 +0,0 @@ -use sqlite::Statement; - -use super::date::Date; - -#[derive(Clone)] -pub struct Transaction { - pub id: String, - pub is_parent: bool, - pub is_child: bool, - pub account_id: String, - pub category_id: String, - pub amount: i64, - pub payee: String, - pub notes: String, - pub date: Date, - pub parent_id: Option<String>, -} - -impl Transaction { - pub fn from_row(statement: &Statement) -> Transaction { - Transaction { - id: statement.read("id").unwrap(), - is_parent: statement.read::<i64, &str>("isParent").unwrap() == 1, - is_child: statement.read::<i64, &str>("isChild").unwrap() == 1, - account_id: statement.read("acct").unwrap(), - category_id: statement - .read::<Option<String>, &str>("category") - .map(Option::<String>::unwrap_or_default) - .unwrap(), - amount: statement.read("amount").unwrap(), - payee: statement - .read::<Option<String>, &str>("description") - .map(Option::<String>::unwrap_or_default) - .unwrap(), - notes: statement - .read::<Option<String>, &str>("notes") - .map(Option::<String>::unwrap_or_default) - .unwrap(), - date: Date::from_date_row(statement, "date"), - parent_id: statement.read("parent_id").unwrap(), - } - } -} diff --git a/backend/importers/actualbudget/src/models/actualbudget/zero_budget.rs b/backend/importers/actualbudget/src/models/actualbudget/zero_budget.rs deleted file mode 100644 index 87c88e7..0000000 --- a/backend/importers/actualbudget/src/models/actualbudget/zero_budget.rs +++ /dev/null @@ -1,30 +0,0 @@ -use sqlite::Statement; - -use super::date::Date; - -#[derive(Clone)] -pub struct ZeroBudget { - pub id: String, - pub month: Date, - pub category_id: String, - pub amount: i64, - pub do_carry_over: bool, -} - -impl ZeroBudget { - pub fn from_row(statement: &Statement) -> ZeroBudget { - ZeroBudget { - id: statement.read("id").unwrap(), - month: Date::from_month_row(statement, "month"), - category_id: statement.read("category").unwrap(), - amount: statement.read("amount").unwrap(), - do_carry_over: match statement.read::<i64, &str>("carryover").unwrap() { - 0 => false, - 1 => true, - carryover => { - panic!("carryover column was neither 0 nor 1, but instead {carryover}") - } - }, - } - } -} diff --git a/backend/importers/actualbudget/src/models/actualbudget_account.rs b/backend/importers/actualbudget/src/models/actualbudget_account.rs new file mode 100644 index 0000000..b3d00ac --- /dev/null +++ b/backend/importers/actualbudget/src/models/actualbudget_account.rs @@ -0,0 +1,8 @@ +use diesel::prelude::{Queryable, Selectable}; + +#[derive(Queryable, Selectable)] +#[diesel(table_name = crate::schema::accounts)] +pub struct ActualbudgetAccount { + pub id: String, + pub name: String, +} diff --git a/backend/importers/actualbudget/src/models/actualbudget_category.rs b/backend/importers/actualbudget/src/models/actualbudget_category.rs new file mode 100644 index 0000000..cbea99b --- /dev/null +++ b/backend/importers/actualbudget/src/models/actualbudget_category.rs @@ -0,0 +1,8 @@ +use diesel::{prelude::Queryable, Selectable}; + +#[derive(Queryable, Selectable)] +#[diesel(table_name = crate::schema::categories)] +pub struct ActualbudgetCategory { + pub id: String, + pub name: String, +} diff --git a/backend/importers/actualbudget/src/models/actualbudget_date.rs b/backend/importers/actualbudget/src/models/actualbudget_date.rs new file mode 100644 index 0000000..bef160c --- /dev/null +++ b/backend/importers/actualbudget/src/models/actualbudget_date.rs @@ -0,0 +1,75 @@ +use diesel::{backend::Backend, deserialize::{FromSql, FromSqlRow}, expression::AsExpression, sql_types::Integer, sqlite::Sqlite}; + +#[derive(Clone, PartialEq, Eq, Debug, AsExpression, FromSqlRow)] +#[diesel(sql_type=Integer)] +pub struct ActualbudgetDate(i32); + +#[derive(Clone, PartialOrd, PartialEq, Eq)] +enum DatePart { + MonthOfYear, + DayOfMonth, +} + +// actualbudget represents dates as numbers which "look like" the corresponding +// ISO string when written in decimal. E.g. 20240901 represents 1 Sep 2024. It +// also uses month specifiers, e.g. 202409 to represent Sep 2024. In this case, +// we interpret this as the first day of the month, e.g. 2024-09-01. + +impl ActualbudgetDate { + pub fn to_iso(&self) -> String { + format!("{0}-{1}-{2}", self.year(), self.month(), self.day()) + } + + pub fn year(&self) -> String { + self.0.to_string()[0..4].to_string() + } + + pub fn month(&self) -> String { + self.0.to_string()[4..6].to_string() + } + + pub fn day(&self) -> String { + match self.date_part() { + DatePart::DayOfMonth => self.0.to_string()[6..8].to_string(), + DatePart::MonthOfYear => String::from("01"), + } + } + + pub fn from_i32(i: i32) -> Self { + ActualbudgetDate(i) + } + + fn date_part(&self) -> DatePart { + if self.0 < 10000000 { + DatePart::MonthOfYear + } else { + DatePart::DayOfMonth + } + } + + fn dayified_value(&self) -> i32 { + match self.date_part() { + DatePart::DayOfMonth => self.0, + DatePart::MonthOfYear => self.0 * 100 + 1, + } + } +} + +impl Ord for ActualbudgetDate { + fn cmp(&self, other: &Self) -> std::cmp::Ordering { + self.dayified_value().cmp(&other.dayified_value()) + } +} + +impl PartialOrd for ActualbudgetDate { + fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> { + Some(self.cmp(other)) + } +} + +impl FromSql<Integer, Sqlite> for ActualbudgetDate { + fn from_sql(bytes: <Sqlite as Backend>::RawValue<'_>) -> diesel::deserialize::Result<Self> { + let int: i32 = FromSql::<Integer, Sqlite>::from_sql(bytes)?; + Ok(ActualbudgetDate(int)) + } +} diff --git a/backend/importers/actualbudget/src/models/actualbudget_transaction.rs b/backend/importers/actualbudget/src/models/actualbudget_transaction.rs new file mode 100644 index 0000000..be2ca1a --- /dev/null +++ b/backend/importers/actualbudget/src/models/actualbudget_transaction.rs @@ -0,0 +1,18 @@ +use diesel::prelude::{Identifiable, Queryable, QueryableByName, Selectable}; + +use crate::models::actualbudget_date::ActualbudgetDate; + +#[derive(Identifiable, QueryableByName, Queryable, Selectable, Clone)] +#[diesel(table_name = crate::schema::transactions)] +pub struct ActualbudgetTransaction { + pub id: String, + pub is_parent: bool, + pub is_child: bool, + pub account_id: String, + pub category_id: String, + pub amount: i32, + pub payee: String, + pub notes: String, + pub date: ActualbudgetDate, + pub parent_id: Option<String>, +} diff --git a/backend/importers/actualbudget/src/models/actualbudget_zero_budget.rs b/backend/importers/actualbudget/src/models/actualbudget_zero_budget.rs new file mode 100644 index 0000000..6478d34 --- /dev/null +++ b/backend/importers/actualbudget/src/models/actualbudget_zero_budget.rs @@ -0,0 +1,13 @@ +use crate::models::actualbudget_date::ActualbudgetDate; +use diesel::{prelude::{Identifiable, Queryable, QueryableByName, Selectable}, sqlite::Sqlite}; + +#[derive(Identifiable, QueryableByName, Queryable, Selectable, Clone)] +#[diesel(table_name = crate::schema::zero_budgets)] +#[diesel(check_for_backend(Sqlite))] +pub struct ActualbudgetZeroBudget { + pub id: String, + pub month: ActualbudgetDate, + pub category_id: String, + pub amount: i32, + pub do_carry_over: i32, +} diff --git a/backend/importers/actualbudget/src/models/budgeting_app/DateTimeUtc.rs b/backend/importers/actualbudget/src/models/budgeting_app/DateTimeUtc.rs deleted file mode 100644 index e44fa8c..0000000 --- a/backend/importers/actualbudget/src/models/budgeting_app/DateTimeUtc.rs +++ /dev/null @@ -1,57 +0,0 @@ -use chrono::{DateTime, Datelike, Utc}; -use diesel::{ - deserialize::{FromSql, FromSqlRow}, - expression::AsExpression, - serialize::ToSql, - sql_types::Text, - sqlite::Sqlite, -}; -use serde::{Deserialize, Serialize}; - -#[derive( - Eq, - PartialEq, - Ord, - PartialOrd, - Serialize, - Deserialize, - Copy, - Clone, - Debug, - AsExpression, - FromSqlRow, -)] -#[diesel(sql_type=Text)] -pub struct DateTimeUtc(DateTime<Utc>); - -impl DateTimeUtc { - pub fn day_diff(&self, other: &DateTimeUtc) -> u32 { - self.0 - .num_days_from_ce() - .abs_diff(other.0.num_days_from_ce()) - } - - pub fn new(date_time_utc: DateTime<Utc>) -> Self { - Self(date_time_utc) - } -} - -impl ToSql<Text, Sqlite> for DateTimeUtc { - fn to_sql<'b>( - &'b self, - out: &mut diesel::serialize::Output<'b, '_, Sqlite>, - ) -> diesel::serialize::Result { - out.set_value((*self).0.to_rfc3339()); - Ok(diesel::serialize::IsNull::No) - } -} - -impl FromSql<Text, Sqlite> for DateTimeUtc { - fn from_sql( - bytes: <Sqlite as diesel::backend::Backend>::RawValue<'_>, - ) -> diesel::deserialize::Result<Self> { - let str: String = FromSql::<Text, Sqlite>::from_sql(bytes)?; - let date_time = str.as_str().parse::<DateTime<Utc>>()?; - Ok(DateTimeUtc(date_time)) - } -} diff --git a/backend/importers/actualbudget/src/models/budgeting_app/account.rs b/backend/importers/actualbudget/src/models/budgeting_app/account.rs deleted file mode 100644 index 165b55c..0000000 --- a/backend/importers/actualbudget/src/models/budgeting_app/account.rs +++ /dev/null @@ -1,8 +0,0 @@ -use super::date_time_utc::DateTimeUtc; - -pub struct Account { - pub id: i32, - pub name: String, - pub opening_balance: i32, - pub opening_date: DateTimeUtc, -} diff --git a/backend/importers/actualbudget/src/models/budgeting_app/budget_update.rs b/backend/importers/actualbudget/src/models/budgeting_app/budget_update.rs deleted file mode 100644 index 9958446..0000000 --- a/backend/importers/actualbudget/src/models/budgeting_app/budget_update.rs +++ /dev/null @@ -1,10 +0,0 @@ -use super::date_time_utc::DateTimeUtc; - -#[derive(Clone)] -pub struct BudgetUpdate { - pub id: i32, - pub category_id: i32, - pub date: DateTimeUtc, - pub new_budget: i32, - pub new_period: i32, -} diff --git a/backend/importers/actualbudget/src/models/budgeting_app/category.rs b/backend/importers/actualbudget/src/models/budgeting_app/category.rs deleted file mode 100644 index 3f51dfd..0000000 --- a/backend/importers/actualbudget/src/models/budgeting_app/category.rs +++ /dev/null @@ -1,4 +0,0 @@ -pub struct Category { - pub id: i32, - pub name: String, -} diff --git a/backend/importers/actualbudget/src/models/budgeting_app/category_transfer.rs b/backend/importers/actualbudget/src/models/budgeting_app/category_transfer.rs deleted file mode 100644 index 0097429..0000000 --- a/backend/importers/actualbudget/src/models/budgeting_app/category_transfer.rs +++ /dev/null @@ -1,7 +0,0 @@ -pub struct CategoryTransfer { - pub id: i32, - pub description: String, - pub quantity: i32, - pub from_category_id: i32, - pub to_category_id: i32, -} diff --git a/backend/importers/actualbudget/src/models/budgeting_app/date_time_utc.rs b/backend/importers/actualbudget/src/models/budgeting_app/date_time_utc.rs deleted file mode 100644 index 69cdfe2..0000000 --- a/backend/importers/actualbudget/src/models/budgeting_app/date_time_utc.rs +++ /dev/null @@ -1,20 +0,0 @@ -use std::{fmt::Display, str::FromStr}; - -use chrono::{DateTime, Utc}; - -#[derive(Clone)] -pub struct DateTimeUtc(DateTime<Utc>); - -impl DateTimeUtc { - pub fn new(date: &str) -> Self { - let mut date_time = String::from(date); - date_time.push_str("T00:00:00.000Z"); - Self(DateTime::<Utc>::from_str(&date_time).unwrap()) - } -} - -impl Display for DateTimeUtc { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - self.0.fmt(f) - } -} diff --git a/backend/importers/actualbudget/src/models/budgeting_app/mod.rs b/backend/importers/actualbudget/src/models/budgeting_app/mod.rs deleted file mode 100644 index bca0f99..0000000 --- a/backend/importers/actualbudget/src/models/budgeting_app/mod.rs +++ /dev/null @@ -1,7 +0,0 @@ -pub mod account; -pub mod budget_update; -pub mod category; -pub mod category_transfer; -pub mod date_time_utc; -pub mod transaction; -pub mod transaction_categorisation; diff --git a/backend/importers/actualbudget/src/models/budgeting_app/transaction.rs b/backend/importers/actualbudget/src/models/budgeting_app/transaction.rs deleted file mode 100644 index e679e1e..0000000 --- a/backend/importers/actualbudget/src/models/budgeting_app/transaction.rs +++ /dev/null @@ -1,11 +0,0 @@ -use super::date_time_utc::DateTimeUtc; - -#[derive(Clone)] -pub struct Transaction { - pub id: i32, - pub description: String, - pub payee: String, - pub quantity: i32, - pub date: DateTimeUtc, - pub account_id: i32, -} diff --git a/backend/importers/actualbudget/src/models/budgeting_app/transaction_categorisation.rs b/backend/importers/actualbudget/src/models/budgeting_app/transaction_categorisation.rs deleted file mode 100644 index ca7e7f5..0000000 --- a/backend/importers/actualbudget/src/models/budgeting_app/transaction_categorisation.rs +++ /dev/null @@ -1,7 +0,0 @@ -pub struct TransactionCategorisation { - pub id: i32, - pub description: String, - pub quantity: i32, - pub transaction_id: i32, - pub category_id: i32, -} diff --git a/backend/importers/actualbudget/src/models/mod.rs b/backend/importers/actualbudget/src/models/mod.rs index cf1b71a..1c97c65 100644 --- a/backend/importers/actualbudget/src/models/mod.rs +++ b/backend/importers/actualbudget/src/models/mod.rs @@ -1,2 +1,5 @@ -pub mod actualbudget; -pub mod budgeting_app; +pub mod actualbudget_account; +pub mod actualbudget_category; +pub mod actualbudget_date; +pub mod actualbudget_transaction; +pub mod actualbudget_zero_budget; diff --git a/backend/importers/actualbudget/src/queries/actualbudget_accounts/get_all.rs b/backend/importers/actualbudget/src/queries/actualbudget_accounts/get_all.rs new file mode 100644 index 0000000..6f667de --- /dev/null +++ b/backend/importers/actualbudget/src/queries/actualbudget_accounts/get_all.rs @@ -0,0 +1,6 @@ +use crate::{models::actualbudget_account::ActualbudgetAccount, schema::accounts::dsl::accounts}; +use diesel::{result::Error, QueryDsl, RunQueryDsl, SelectableHelper, SqliteConnection}; + +pub fn get_all_actualbudget_accounts(connection: &mut SqliteConnection) -> Result<Vec<ActualbudgetAccount>, Error> { + accounts.select(ActualbudgetAccount::as_select()).load(connection) +} diff --git a/backend/importers/actualbudget/src/queries/actualbudget_accounts/get_category_transfers_account.rs b/backend/importers/actualbudget/src/queries/actualbudget_accounts/get_category_transfers_account.rs new file mode 100644 index 0000000..f05dc78 --- /dev/null +++ b/backend/importers/actualbudget/src/queries/actualbudget_accounts/get_category_transfers_account.rs @@ -0,0 +1,13 @@ +use diesel::{result::Error, ExpressionMethods, QueryDsl, RunQueryDsl, SelectableHelper, SqliteConnection}; + +use crate::{models::actualbudget_account::ActualbudgetAccount, schema::accounts::{ + self as accounts_schema, + dsl::accounts as accounts_table, +}}; + +pub fn get_category_transfers_account(connection: &mut SqliteConnection) -> Result<ActualbudgetAccount, Error> { + accounts_table + .filter(accounts_schema::name.eq("Category transfers")) + .select(ActualbudgetAccount::as_select()) + .first(connection) +} diff --git a/backend/importers/actualbudget/src/queries/actualbudget_accounts/mod.rs b/backend/importers/actualbudget/src/queries/actualbudget_accounts/mod.rs new file mode 100644 index 0000000..c6d6015 --- /dev/null +++ b/backend/importers/actualbudget/src/queries/actualbudget_accounts/mod.rs @@ -0,0 +1,5 @@ +mod get_all; +mod get_category_transfers_account; + +pub use get_all::get_all_actualbudget_accounts; +pub use get_category_transfers_account::get_category_transfers_account; diff --git a/backend/importers/actualbudget/src/queries/actualbudget_categories/get_all.rs b/backend/importers/actualbudget/src/queries/actualbudget_categories/get_all.rs new file mode 100644 index 0000000..4cb7ebc --- /dev/null +++ b/backend/importers/actualbudget/src/queries/actualbudget_categories/get_all.rs @@ -0,0 +1,6 @@ +use crate::{models::actualbudget_category::ActualbudgetCategory, schema::categories::dsl::categories}; +use diesel::{result::Error, QueryDsl, RunQueryDsl, SelectableHelper, SqliteConnection}; + +pub fn get_all_actualbudget_categories(connection: &mut SqliteConnection) -> Result<Vec<ActualbudgetCategory>, Error> { + categories.select(ActualbudgetCategory::as_select()).load(connection) +} diff --git a/backend/importers/actualbudget/src/queries/actualbudget_categories/mod.rs b/backend/importers/actualbudget/src/queries/actualbudget_categories/mod.rs new file mode 100644 index 0000000..0c04e53 --- /dev/null +++ b/backend/importers/actualbudget/src/queries/actualbudget_categories/mod.rs @@ -0,0 +1,3 @@ +mod get_all; + +pub use get_all::get_all_actualbudget_categories; diff --git a/backend/importers/actualbudget/src/queries/actualbudget_transactions/get_all.rs b/backend/importers/actualbudget/src/queries/actualbudget_transactions/get_all.rs new file mode 100644 index 0000000..84c8d08 --- /dev/null +++ b/backend/importers/actualbudget/src/queries/actualbudget_transactions/get_all.rs @@ -0,0 +1,6 @@ +use crate::{models::actualbudget_transaction::ActualbudgetTransaction, schema::transactions::dsl::transactions}; +use diesel::{result::Error, QueryDsl, RunQueryDsl, SelectableHelper, SqliteConnection}; + +pub fn get_all_actualbudget_transactions(connection: &mut SqliteConnection) -> Result<Vec<ActualbudgetTransaction>, Error> { + transactions.select(ActualbudgetTransaction::as_select()).load(connection) +} diff --git a/backend/importers/actualbudget/src/queries/actualbudget_transactions/get_first_date.rs b/backend/importers/actualbudget/src/queries/actualbudget_transactions/get_first_date.rs new file mode 100644 index 0000000..dabfbd6 --- /dev/null +++ b/backend/importers/actualbudget/src/queries/actualbudget_transactions/get_first_date.rs @@ -0,0 +1,13 @@ +use crate::{models::actualbudget_date::ActualbudgetDate, schema::transactions::{ + self as transactions_schema, + dsl::transactions as transactions_table, +}}; +use diesel::{dsl::min, result::Error, QueryDsl, RunQueryDsl, SqliteConnection}; + +pub fn get_first_actualbudget_transaction_date(connection: &mut SqliteConnection) -> Result<ActualbudgetDate, Error> { + let date_as_int = transactions_table + .select(min(transactions_schema::date)) + .first::<Option<i32>>(connection) + .map_or_else(|e| Err(e), |o| o.map_or_else(|| Err(Error::NotFound), |i| Ok(i)))?; + Ok(ActualbudgetDate::from_i32(date_as_int)) +} diff --git a/backend/importers/actualbudget/src/queries/actualbudget_transactions/mod.rs b/backend/importers/actualbudget/src/queries/actualbudget_transactions/mod.rs new file mode 100644 index 0000000..b4ed0dd --- /dev/null +++ b/backend/importers/actualbudget/src/queries/actualbudget_transactions/mod.rs @@ -0,0 +1,5 @@ +mod get_all; +mod get_first_date; + +pub use get_all::get_all_actualbudget_transactions; +pub use get_first_date::get_first_actualbudget_transaction_date; diff --git a/backend/importers/actualbudget/src/queries/actualbudget_zero_budgets/get_all.rs b/backend/importers/actualbudget/src/queries/actualbudget_zero_budgets/get_all.rs new file mode 100644 index 0000000..0c3f695 --- /dev/null +++ b/backend/importers/actualbudget/src/queries/actualbudget_zero_budgets/get_all.rs @@ -0,0 +1,6 @@ +use crate::{models::actualbudget_zero_budget::ActualbudgetZeroBudget, schema::zero_budgets::dsl::zero_budgets}; +use diesel::{result::Error, QueryDsl, RunQueryDsl, SelectableHelper, SqliteConnection}; + +pub fn get_all_actualbudget_zero_budgets(connection: &mut SqliteConnection) -> Result<Vec<ActualbudgetZeroBudget>, Error> { + zero_budgets.select(ActualbudgetZeroBudget::as_select()).load(connection) +} diff --git a/backend/importers/actualbudget/src/queries/actualbudget_zero_budgets/mod.rs b/backend/importers/actualbudget/src/queries/actualbudget_zero_budgets/mod.rs new file mode 100644 index 0000000..ba7e21b --- /dev/null +++ b/backend/importers/actualbudget/src/queries/actualbudget_zero_budgets/mod.rs @@ -0,0 +1,3 @@ +mod get_all; + +pub use get_all::get_all_actualbudget_zero_budgets; diff --git a/backend/importers/actualbudget/src/queries/mod.rs b/backend/importers/actualbudget/src/queries/mod.rs new file mode 100644 index 0000000..e16dafc --- /dev/null +++ b/backend/importers/actualbudget/src/queries/mod.rs @@ -0,0 +1,4 @@ +pub mod actualbudget_accounts; +pub mod actualbudget_categories; +pub mod actualbudget_transactions; +pub mod actualbudget_zero_budgets; diff --git a/backend/importers/actualbudget/src/schema.rs b/backend/importers/actualbudget/src/schema.rs new file mode 100644 index 0000000..a872231 --- /dev/null +++ b/backend/importers/actualbudget/src/schema.rs @@ -0,0 +1,47 @@ +diesel::table! { + accounts (id) { + id -> Text, + name -> Text, + } +} + +diesel::table! { + categories (id) { + id -> Text, + name -> Text, + } +} + +diesel::table! { + transactions (id) { + id -> Text, + #[sql_name = "isParent"] + is_parent -> Bool, + #[sql_name = "isChild"] + is_child -> Bool, + #[sql_name = "acct"] + account_id -> Text, + #[sql_name = "category"] + category_id -> Text, + amount -> Integer, + #[sql_name = "description"] + payee -> Text, + notes -> Text, + date -> Integer, + parent_id -> Nullable<Text>, + } +} + +diesel::table! { + zero_budgets (id) { + id -> Text, + month -> Integer, + #[sql_name = "category"] + category_id -> Text, + amount -> Integer, + #[sql_name = "carryover"] + do_carry_over -> Integer, + // goal -> Integer, + // long_goal -> Integer, + } +} diff --git a/backend/importers/actualbudget/src/transformers.rs b/backend/importers/actualbudget/src/transformers.rs index 678ea05..76001d1 100644 --- a/backend/importers/actualbudget/src/transformers.rs +++ b/backend/importers/actualbudget/src/transformers.rs @@ -1,21 +1,19 @@ +use budgeting_app_core::models::{ + account::Account, budget_update::BudgetUpdate, category::Category, + category_transfer::CategoryTransfer, date_time_utc::DateTimeUtc, transaction::Transaction, + transaction_categorisation::TransactionCategorisation, +}; use core::panic; use std::hash::{DefaultHasher, Hash, Hasher}; -use crate::models::{ - actualbudget, - budgeting_app::{ - account::Account, budget_update::BudgetUpdate, category::Category, - category_transfer::CategoryTransfer, date_time_utc::DateTimeUtc, transaction::Transaction, - transaction_categorisation::TransactionCategorisation, - }, -}; +use crate::models::{actualbudget_account::ActualbudgetAccount, actualbudget_category::ActualbudgetCategory, actualbudget_date::ActualbudgetDate, actualbudget_transaction::ActualbudgetTransaction, actualbudget_zero_budget::ActualbudgetZeroBudget}; -pub fn transform_date(actualbudget_date: &actualbudget::date::Date) -> DateTimeUtc { - DateTimeUtc::new(&actualbudget_date.to_iso()) +pub fn transform_date(actualbudget_date: &ActualbudgetDate) -> Result<DateTimeUtc, String> { + DateTimeUtc::from_date_str(&actualbudget_date.to_iso()).map_err(|e| e.to_string()) } pub fn transform_accounts( - actualbudget_accounts: &[actualbudget::account::Account], + actualbudget_accounts: &[ActualbudgetAccount], first_transaction_date: &DateTimeUtc, ) -> Vec<Account> { let mut accounts = Vec::<Account>::new(); @@ -26,7 +24,7 @@ pub fn transform_accounts( } fn transform_account( - actualbudget_account: &actualbudget::account::Account, + actualbudget_account: &ActualbudgetAccount, opening_date: &DateTimeUtc, ) -> Account { Account { @@ -38,7 +36,7 @@ fn transform_account( } pub fn transform_categories( - actualbudget_categories: &[actualbudget::category::Category], + actualbudget_categories: &[ActualbudgetCategory], ) -> Vec<Category> { let mut categories = Vec::<Category>::new(); for actualbudget_category in actualbudget_categories { @@ -59,8 +57,8 @@ fn transform_id(actualbudget_id: &str) -> i32 { } pub fn transform_budget_updates( - actualbudget_zero_budgets: &[actualbudget::zero_budget::ZeroBudget], -) -> Vec<BudgetUpdate> { + actualbudget_zero_budgets: &[ActualbudgetZeroBudget], +) -> Result<Vec<BudgetUpdate>, String> { let mut budget_updates = Vec::<BudgetUpdate>::new(); for zero_budget in actualbudget_zero_budgets { let first_zero_budget_in_category = get_first_zero_budget_in_category( @@ -73,18 +71,18 @@ pub fn transform_budget_updates( budget_updates.push(BudgetUpdate { id: transform_id(&zero_budget.id.clone()), category_id: transform_id(&zero_budget.category_id.clone()), - date: transform_date(&zero_budget.month.clone()), + date: transform_date(&zero_budget.month.clone()).map_err(|e| e.to_string())?, new_budget: zero_budget.amount.try_into().unwrap(), new_period: 30, }) } - budget_updates + Ok(budget_updates) } fn get_first_zero_budget_in_category( - actualbudget_zero_budgets: &[actualbudget::zero_budget::ZeroBudget], + actualbudget_zero_budgets: &[ActualbudgetZeroBudget], actualbudget_category_id: &str, -) -> Option<actualbudget::zero_budget::ZeroBudget> { +) -> Option<ActualbudgetZeroBudget> { let mut category_zero_budgets = actualbudget_zero_budgets.to_vec(); category_zero_budgets.retain(|zb| zb.category_id == actualbudget_category_id); category_zero_budgets @@ -94,9 +92,9 @@ fn get_first_zero_budget_in_category( } pub fn transform_transactions( - actualbudget_transactions: &[actualbudget::transaction::Transaction], - actualbudget_category_transfers_account: &actualbudget::account::Account, -) -> (Vec<Transaction>, Vec<TransactionCategorisation>) { + actualbudget_transactions: &[ActualbudgetTransaction], + actualbudget_category_transfers_account: &ActualbudgetAccount, +) -> Result<(Vec<Transaction>, Vec<TransactionCategorisation>), String> { let mut transactions = Vec::<Transaction>::new(); let mut transaction_categorisations = Vec::<TransactionCategorisation>::new(); @@ -118,7 +116,7 @@ pub fn transform_transactions( description: atomic_actualbudget_transaction.notes, payee: atomic_actualbudget_transaction.payee, quantity: atomic_actualbudget_transaction.amount.try_into().unwrap(), - date: transform_date(&atomic_actualbudget_transaction.date), + date: transform_date(&atomic_actualbudget_transaction.date).map_err(|e| e.to_string())?, account_id: transform_id(&atomic_actualbudget_transaction.account_id), }); @@ -146,7 +144,7 @@ pub fn transform_transactions( description: parent_actualbudget_transaction.notes, payee: parent_actualbudget_transaction.payee, quantity: parent_actualbudget_transaction.amount.try_into().unwrap(), - date: transform_date(&parent_actualbudget_transaction.date), + date: transform_date(&parent_actualbudget_transaction.date).map_err(|e| e.to_string())?, account_id: transform_id(&parent_actualbudget_transaction.account_id), }); @@ -161,12 +159,12 @@ pub fn transform_transactions( } } - (transactions, transaction_categorisations) + Ok((transactions, transaction_categorisations)) } pub fn transform_category_transfers( - actualbudget_transactions: &[actualbudget::transaction::Transaction], - actualbudget_category_transfers_account: &actualbudget::account::Account, + actualbudget_transactions: &[ActualbudgetTransaction], + actualbudget_category_transfers_account: &ActualbudgetAccount, ) -> Vec<CategoryTransfer> { let mut category_transfers = Vec::<CategoryTransfer>::new(); |
