diff --git a/Cargo.toml b/Cargo.toml index 244e296..96bd6d7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,10 +6,13 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +crossbeam-channel = "0.5.8" crossterm = "0.26.1" futures = "0.3.28" log = "0.4.19" ratatui = "0.21.0" +rust_decimal = "1.31.0" simplelog = "0.12.1" -sqlx = { version = "0.6.3", features = ["postgres", "runtime-tokio-native-tls", "sqlite"] } +sqlx = { version = "0.6.3", features = ["postgres", "runtime-tokio-native-tls", "sqlite", "decimal", "time"] } +time = "0.3.25" tokio = { version = "1.28.2", features = ["full"] } diff --git a/SQL/Tablecreation.sql b/SQL/Tablecreation.sql index 2d3ef9e..b383e8c 100644 --- a/SQL/Tablecreation.sql +++ b/SQL/Tablecreation.sql @@ -37,13 +37,16 @@ CREATE TABLE rcnt.buckets ( CREATE TABLE rcnt.transaction_breakdown ( trns_brkdwn_id int4 NOT NULL GENERATED ALWAYS AS IDENTITY, - trns_brkdwn_amount numeric(10, 3) NOT NULL, + trns_brkdwn_amount money NOT NULL, trns_brkdwn_parent_transaction int4 NOT NULL, trns_brkdwn_catagory int4 NULL, trns_brkdwn_bucket int4 NULL, CONSTRAINT transaction_breakdown_pkey PRIMARY KEY (trns_brkdwn_id) ); +alter table rcnt.transaction_breakdown add constraint transaction_breakdown_parent_transaction_fkey foreign key (trns_brkdwn_parent_transaction) references rcnt.transactions(trns_id); +alter table rcnt.transaction_breakdown add constraint transaction_breakdown_catagory_fkey foreign key (trns_brkdwn_catagory) references rcnt.transaction_categories(trns_ctgry_id); +alter table rcnt.transaction_breakdown add constraint transaction_breakdown_bucket_fkey foreign key (trns_brkdwn_bucket) references rcnt.buckets(bkt_id); @@ -55,7 +58,7 @@ CREATE TABLE rcnt.transaction_breakdown ( CREATE TABLE rcnt.transactions ( trns_id int4 NOT NULL GENERATED ALWAYS AS IDENTITY, - trns_amount numeric(10, 3) NOT NULL, + trns_amount money NOT NULL, trns_description varchar(250) NULL, trns_account int4 NOT NULL, trns_bucket int4 NULL, @@ -71,7 +74,7 @@ ALTER TABLE rcnt.transactions ADD CONSTRAINT transactions_trns_bucket_fkey FOREI -- Drop table --- DROP TABLE rcnt.transaction_categories; + DROP TABLE rcnt.transaction_categories; CREATE TABLE rcnt.transaction_categories ( trns_ctgry_id int4 NOT NULL GENERATED ALWAYS AS IDENTITY, diff --git a/SQL/Test Data Poplating.sql b/SQL/Test Data Poplating.sql index b111950..3b370f7 100644 --- a/SQL/Test Data Poplating.sql +++ b/SQL/Test Data Poplating.sql @@ -1,50 +1,35 @@ INSERT INTO rcnt.accounts -(display_name, description) +(acnt_dsply_name, acnt_description) VALUES('BECU Checking', 'BECU Checking Account'); INSERT INTO rcnt.buckets -(display_code, display_name, description) -VALUES('SVNGS', 'Savings', 'Long term savings'); +(bkt_dsply_code, bkt_dsply_name, bkt_description) +values +('SVNGS', 'Savings', 'Long term savings'), +('SPEND', 'Spending', 'General Spening Category'), +('TITHE', 'Tight', ''); -INSERT INTO rcnt.buckets -(display_code, display_name, description) -VALUES('SPEND', 'Spending', 'General Spening Category'); - - -INSERT INTO rcnt.buckets -(display_code, display_name, description) -VALUES('TITHE', 'Tight', ''); - - - - -INSERT INTO rcnt.transactioncategories -(display_code, display_name, description) -VALUES('GAS', 'Gas', 'Buying gas'); - - -INSERT INTO rcnt.transactioncategories -(display_code, display_name, description) -VALUES('TAX', 'Taxes', 'One of the only constants'); +INSERT INTO rcnt.transaction_categories +(trns_ctgry_dsply_code, trns_ctgry_dsply_name, trns_ctgry_description) +values +('TAX', 'Taxes', 'One of the only constants'), +('GAS', 'Gas', 'Buying gas'); INSERT INTO rcnt.transactions -(amount, description, account, transaction_bucket) -VALUES(10.00, 'Optional Text', 1, 1); +(trns_amount, trns_description, trns_account, trns_bucket, trns_date) +VALUES(10.00, 'Optional Text', 1, 1, '8-7-2023'); -INSERT INTO rcnt.transactionbreakdown -(amount, parent_transaction, catagory, transaction_bucket) -VALUES(8.00, 1, 1, 1); - - -INSERT INTO rcnt.transactionbreakdown -(amount, parent_transaction, catagory, transaction_bucket) -VALUES(2.00, 1, 1, 2); +INSERT INTO rcnt.transaction_breakdown +(trns_brkdwn_amount, trns_brkdwn_parent_transaction, trns_brkdwn_catagory, trns_brkdwn_bucket) +values +(8.00, 1, 1, 1), +(2.00, 1, 1, 2); diff --git a/SQL/Views setup.sql b/SQL/Views setup.sql index bbc5cf2..19616d1 100644 --- a/SQL/Views setup.sql +++ b/SQL/Views setup.sql @@ -13,7 +13,8 @@ create or replace view rcnt.transaction_history_view as left join rcnt.buckets b on b.bkt_id = t.trns_bucket left join rcnt.accounts a on a.acnt_id = t.trns_account ---drop view rcnt.transaction_history_view +drop view rcnt.transaction_history_view; +drop view rcnt.transaction_detail_history_view; select * from rcnt.transaction_history_view @@ -32,3 +33,9 @@ create or replace view rcnt.transaction_detail_history_view as left join rcnt.transaction_categories tc on tc.trns_ctgry_id = t.trns_brkdwn_catagory left join rcnt.buckets b on b.bkt_id = t.trns_brkdwn_bucket +grant connect on database "Borealis" to rcntuser; +grant pg_read_all_data to rcntuser; +grant pg_write_all_data to rcntuser; + +SELECT trns_id, trns_amount, trns_description, trns_account, trns_bucket, trns_date +FROM rcnt.transactions; diff --git a/src/app.rs b/src/app.rs index d6b2a95..01b0977 100644 --- a/src/app.rs +++ b/src/app.rs @@ -6,6 +6,8 @@ use crossterm::event::{Event, self, KeyCode}; use tokio; use ratatui::widgets::ListState; +use crossbeam_channel::Receiver; + use log::warn; use crate::db::DB; @@ -79,10 +81,10 @@ pub struct App<'a> { } impl<'a> App<'a> { - pub fn new(db: DB, records: Arc>>) -> App<'a> { + pub fn new(db: DB, records: Arc>>, r: Receiver) -> App<'a> { App { running: true, - states: States::new(), + states: States::new(r), input_mode: InputMode::Normal, db: Arc::new(tokio::sync::Mutex::new(db)), records, @@ -158,16 +160,20 @@ pub struct States<'a> { pub transactions: NewTransactionTabState<'a>, pub history: HistoryState, + pub new_data: Receiver, + pub active_frame: ActiveFrame, } impl<'a> States<'a> { - pub fn new() -> States<'a> { + pub fn new(r: Receiver) -> States<'a> { States { nav_state: NavigationState::new(), transactions: NewTransactionTabState::new(), history: HistoryState::new(), + new_data: r, + active_frame: ActiveFrame::Navigation, } } diff --git a/src/db/connection.rs b/src/db/connection.rs index cb6b789..09ea734 100644 --- a/src/db/connection.rs +++ b/src/db/connection.rs @@ -2,50 +2,60 @@ use std::sync::Mutex; use std::sync::Arc; +use crossbeam_channel::Sender; + use futures::TryStreamExt; use sqlx::Error; use sqlx::PgPool; use sqlx::Row; use sqlx::postgres::PgPoolOptions; +use sqlx::postgres::types::PgMoney; +use time::Date; use crate::db::transaction::TransactionRecord; pub struct DB { conn_pool: PgPool, + new_data_notify: Sender, records: Arc>>, } impl DB { - pub async fn new(records: Arc>>) -> Result { + pub async fn new(records: Arc>>, s: Sender) -> Result { let connection_pool = PgPoolOptions::new() .max_connections(3) .connect("postgres://rcntuser:Devel@pmentPa$$w0rd@10.0.0.183/Borealis").await?; Ok(DB { conn_pool: connection_pool, + new_data_notify: s, records, }) } pub async fn get_all_records(&mut self) -> Result<(), Error> { - let mut rows = sqlx::query("SELECT trns_id, trns_amount, trns_description, trns_account, trns_bucket trns_date FROM rcnt.transactions") + let mut rows = sqlx::query("SELECT trns_id, trns_amount, trns_description, trns_account, trns_bucket, trns_date FROM rcnt.transactions") .fetch(&self.conn_pool); while let Some(row) = rows.try_next().await? { - let id: i64 = row.try_get("trns_id")?; - let amount: &str = row.try_get("trns_amount")?; - let date: &str = row.try_get("trns_date")?; + let id: i32 = row.try_get("trns_id")?; + let amount: PgMoney = row.try_get("trns_amount")?; + let date: Date = row.try_get("trns_date")?; + self.records.lock().unwrap().clear(); + self.records.lock().unwrap().push( - TransactionRecord{ + TransactionRecord { id: id.into(), - amount: amount.to_string(), + amount: amount.to_decimal(2).to_string(), date: date.to_string() } ); } + self.new_data_notify.try_send(true).unwrap(); + return Ok(()); diff --git a/src/db/transaction.rs b/src/db/transaction.rs index 511494f..f4619b6 100644 --- a/src/db/transaction.rs +++ b/src/db/transaction.rs @@ -2,6 +2,7 @@ use std::borrow::Cow; // cargo add crust_decimal +#[derive(Clone)] pub struct TransactionRecord { pub id: i64, // pub amount: Decimal, diff --git a/src/main.rs b/src/main.rs index b519503..f12a164 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,6 +4,8 @@ use std::time::Duration; use std::sync::Mutex; use std::sync::Arc; +use crossbeam_channel::bounded; + use simplelog::*; use ratatui::{ @@ -14,7 +16,7 @@ use ratatui::{ use recount::app::{App, AppResult}; use recount::tui::Tui; use recount::db::DB; -use recount::db::transaction::TransactionRecord; +//use recount::db::transaction::TransactionRecord; #[tokio::main] async fn main() -> AppResult<()> { @@ -23,10 +25,12 @@ async fn main() -> AppResult<()> { init_logger(log_file); let records = Arc::new(Mutex::new(Vec::new())); - let db = DB::new(Arc::clone(&records)).await?; + let (s, r) = bounded::(2); + let db = DB::new(Arc::clone(&records), s).await?; + // Create an application. - let mut app = App::new(db, records); + let mut app = App::new(db, records, r); // Initialize the terminal user interface. let backend = CrosstermBackend::new(io::stderr()); @@ -54,12 +58,12 @@ async fn main() -> AppResult<()> { pub fn init_logger(output_file: String) { // TODO: configure the log levels to something appropriate CombinedLogger::init(vec![ - TermLogger::new( - LevelFilter::Info, - Config::default(), - TerminalMode::Mixed, - ColorChoice::Auto, - ), + // TermLogger::new( + // LevelFilter::Info, + // Config::default(), + // TerminalMode::Mixed, + // ColorChoice::Auto, + // ), WriteLogger::new( LevelFilter::Info, Config::default(), diff --git a/src/ui.rs b/src/ui.rs index cec69af..8c81638 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -5,6 +5,7 @@ use ratatui::{ widgets::{Block, Borders}, Frame, style::Style, }; +use log; use crate::uis::{render_navigation_frame, render_new_transaction_tab}; diff --git a/src/uis/history.rs b/src/uis/history.rs index 2b87318..55e8cf9 100644 --- a/src/uis/history.rs +++ b/src/uis/history.rs @@ -31,10 +31,27 @@ impl HistoryState { } } } + + pub fn update_transactions(app: &mut App) { + app.states.history.transacts_list.items.clear(); + for i in app.records.lock().unwrap().iter() { + app.states.history.transacts_list.items.push(i.clone()); + } + } } pub fn render_history_tab (f: &mut Frame, body_rect: Rect, app: &mut App) { + let do_update = match app.states.new_data.try_recv() { + Ok(val) => val, + Err(_) => false, + }; + + if do_update { + HistoryState::update_transactions(app); + } + + let split_body = Layout::default() .direction(Direction::Horizontal) .constraints([Constraint::Percentage(50), Constraint::Percentage(50)])