got basic requests from the database

This commit is contained in:
Nickiel12 2023-08-09 22:39:00 -07:00
parent a9476556b1
commit 386e119326
10 changed files with 94 additions and 57 deletions

View file

@ -6,10 +6,13 @@ edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
crossbeam-channel = "0.5.8"
crossterm = "0.26.1" crossterm = "0.26.1"
futures = "0.3.28" futures = "0.3.28"
log = "0.4.19" log = "0.4.19"
ratatui = "0.21.0" ratatui = "0.21.0"
rust_decimal = "1.31.0"
simplelog = "0.12.1" 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"] } tokio = { version = "1.28.2", features = ["full"] }

View file

@ -37,13 +37,16 @@ CREATE TABLE rcnt.buckets (
CREATE TABLE rcnt.transaction_breakdown ( CREATE TABLE rcnt.transaction_breakdown (
trns_brkdwn_id int4 NOT NULL GENERATED ALWAYS AS IDENTITY, 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_parent_transaction int4 NOT NULL,
trns_brkdwn_catagory int4 NULL, trns_brkdwn_catagory int4 NULL,
trns_brkdwn_bucket int4 NULL, trns_brkdwn_bucket int4 NULL,
CONSTRAINT transaction_breakdown_pkey PRIMARY KEY (trns_brkdwn_id) 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 ( CREATE TABLE rcnt.transactions (
trns_id int4 NOT NULL GENERATED ALWAYS AS IDENTITY, 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_description varchar(250) NULL,
trns_account int4 NOT NULL, trns_account int4 NOT NULL,
trns_bucket int4 NULL, trns_bucket int4 NULL,
@ -71,7 +74,7 @@ ALTER TABLE rcnt.transactions ADD CONSTRAINT transactions_trns_bucket_fkey FOREI
-- Drop table -- Drop table
-- DROP TABLE rcnt.transaction_categories; DROP TABLE rcnt.transaction_categories;
CREATE TABLE rcnt.transaction_categories ( CREATE TABLE rcnt.transaction_categories (
trns_ctgry_id int4 NOT NULL GENERATED ALWAYS AS IDENTITY, trns_ctgry_id int4 NOT NULL GENERATED ALWAYS AS IDENTITY,

View file

@ -1,50 +1,35 @@
INSERT INTO rcnt.accounts INSERT INTO rcnt.accounts
(display_name, description) (acnt_dsply_name, acnt_description)
VALUES('BECU Checking', 'BECU Checking Account'); VALUES('BECU Checking', 'BECU Checking Account');
INSERT INTO rcnt.buckets INSERT INTO rcnt.buckets
(display_code, display_name, description) (bkt_dsply_code, bkt_dsply_name, bkt_description)
VALUES('SVNGS', 'Savings', 'Long term savings'); values
('SVNGS', 'Savings', 'Long term savings'),
('SPEND', 'Spending', 'General Spening Category'),
('TITHE', 'Tight', '<Random Bible Verse About Tithes>');
INSERT INTO rcnt.buckets INSERT INTO rcnt.transaction_categories
(display_code, display_name, description) (trns_ctgry_dsply_code, trns_ctgry_dsply_name, trns_ctgry_description)
VALUES('SPEND', 'Spending', 'General Spening Category'); values
('TAX', 'Taxes', 'One of the only constants'),
('GAS', 'Gas', 'Buying gas');
INSERT INTO rcnt.buckets
(display_code, display_name, description)
VALUES('TITHE', 'Tight', '<Random Bible Verse About Tithes>');
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.transactions INSERT INTO rcnt.transactions
(amount, description, account, transaction_bucket) (trns_amount, trns_description, trns_account, trns_bucket, trns_date)
VALUES(10.00, 'Optional Text', 1, 1); VALUES(10.00, 'Optional Text', 1, 1, '8-7-2023');
INSERT INTO rcnt.transactionbreakdown INSERT INTO rcnt.transaction_breakdown
(amount, parent_transaction, catagory, transaction_bucket) (trns_brkdwn_amount, trns_brkdwn_parent_transaction, trns_brkdwn_catagory, trns_brkdwn_bucket)
VALUES(8.00, 1, 1, 1); values
(8.00, 1, 1, 1),
(2.00, 1, 1, 2);
INSERT INTO rcnt.transactionbreakdown
(amount, parent_transaction, catagory, transaction_bucket)
VALUES(2.00, 1, 1, 2);

View file

@ -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.buckets b on b.bkt_id = t.trns_bucket
left join rcnt.accounts a on a.acnt_id = t.trns_account 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 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.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 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;

View file

@ -6,6 +6,8 @@ use crossterm::event::{Event, self, KeyCode};
use tokio; use tokio;
use ratatui::widgets::ListState; use ratatui::widgets::ListState;
use crossbeam_channel::Receiver;
use log::warn; use log::warn;
use crate::db::DB; use crate::db::DB;
@ -79,10 +81,10 @@ pub struct App<'a> {
} }
impl<'a> App<'a> { impl<'a> App<'a> {
pub fn new(db: DB, records: Arc<Mutex<Vec<TransactionRecord>>>) -> App<'a> { pub fn new(db: DB, records: Arc<Mutex<Vec<TransactionRecord>>>, r: Receiver<bool>) -> App<'a> {
App { App {
running: true, running: true,
states: States::new(), states: States::new(r),
input_mode: InputMode::Normal, input_mode: InputMode::Normal,
db: Arc::new(tokio::sync::Mutex::new(db)), db: Arc::new(tokio::sync::Mutex::new(db)),
records, records,
@ -158,16 +160,20 @@ pub struct States<'a> {
pub transactions: NewTransactionTabState<'a>, pub transactions: NewTransactionTabState<'a>,
pub history: HistoryState, pub history: HistoryState,
pub new_data: Receiver<bool>,
pub active_frame: ActiveFrame, pub active_frame: ActiveFrame,
} }
impl<'a> States<'a> { impl<'a> States<'a> {
pub fn new() -> States<'a> { pub fn new(r: Receiver<bool>) -> States<'a> {
States { States {
nav_state: NavigationState::new(), nav_state: NavigationState::new(),
transactions: NewTransactionTabState::new(), transactions: NewTransactionTabState::new(),
history: HistoryState::new(), history: HistoryState::new(),
new_data: r,
active_frame: ActiveFrame::Navigation, active_frame: ActiveFrame::Navigation,
} }
} }

View file

@ -2,50 +2,60 @@
use std::sync::Mutex; use std::sync::Mutex;
use std::sync::Arc; use std::sync::Arc;
use crossbeam_channel::Sender;
use futures::TryStreamExt; use futures::TryStreamExt;
use sqlx::Error; use sqlx::Error;
use sqlx::PgPool; use sqlx::PgPool;
use sqlx::Row; use sqlx::Row;
use sqlx::postgres::PgPoolOptions; use sqlx::postgres::PgPoolOptions;
use sqlx::postgres::types::PgMoney;
use time::Date;
use crate::db::transaction::TransactionRecord; use crate::db::transaction::TransactionRecord;
pub struct DB { pub struct DB {
conn_pool: PgPool, conn_pool: PgPool,
new_data_notify: Sender<bool>,
records: Arc<Mutex<Vec<TransactionRecord>>>, records: Arc<Mutex<Vec<TransactionRecord>>>,
} }
impl DB { impl DB {
pub async fn new(records: Arc<Mutex<Vec<TransactionRecord>>>) -> Result<DB, sqlx::Error> { pub async fn new(records: Arc<Mutex<Vec<TransactionRecord>>>, s: Sender<bool>) -> Result<DB, sqlx::Error> {
let connection_pool = PgPoolOptions::new() let connection_pool = PgPoolOptions::new()
.max_connections(3) .max_connections(3)
.connect("postgres://rcntuser:Devel@pmentPa$$w0rd@10.0.0.183/Borealis").await?; .connect("postgres://rcntuser:Devel@pmentPa$$w0rd@10.0.0.183/Borealis").await?;
Ok(DB { Ok(DB {
conn_pool: connection_pool, conn_pool: connection_pool,
new_data_notify: s,
records, records,
}) })
} }
pub async fn get_all_records(&mut self) -> Result<(), Error> { 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); .fetch(&self.conn_pool);
while let Some(row) = rows.try_next().await? { while let Some(row) = rows.try_next().await? {
let id: i64 = row.try_get("trns_id")?; let id: i32 = row.try_get("trns_id")?;
let amount: &str = row.try_get("trns_amount")?; let amount: PgMoney = row.try_get("trns_amount")?;
let date: &str = row.try_get("trns_date")?; let date: Date = row.try_get("trns_date")?;
self.records.lock().unwrap().clear();
self.records.lock().unwrap().push( self.records.lock().unwrap().push(
TransactionRecord { TransactionRecord {
id: id.into(), id: id.into(),
amount: amount.to_string(), amount: amount.to_decimal(2).to_string(),
date: date.to_string() date: date.to_string()
} }
); );
} }
self.new_data_notify.try_send(true).unwrap();
return Ok(()); return Ok(());

View file

@ -2,6 +2,7 @@ use std::borrow::Cow;
// cargo add crust_decimal // cargo add crust_decimal
#[derive(Clone)]
pub struct TransactionRecord { pub struct TransactionRecord {
pub id: i64, pub id: i64,
// pub amount: Decimal, // pub amount: Decimal,

View file

@ -4,6 +4,8 @@ use std::time::Duration;
use std::sync::Mutex; use std::sync::Mutex;
use std::sync::Arc; use std::sync::Arc;
use crossbeam_channel::bounded;
use simplelog::*; use simplelog::*;
use ratatui::{ use ratatui::{
@ -14,7 +16,7 @@ use ratatui::{
use recount::app::{App, AppResult}; use recount::app::{App, AppResult};
use recount::tui::Tui; use recount::tui::Tui;
use recount::db::DB; use recount::db::DB;
use recount::db::transaction::TransactionRecord; //use recount::db::transaction::TransactionRecord;
#[tokio::main] #[tokio::main]
async fn main() -> AppResult<()> { async fn main() -> AppResult<()> {
@ -23,10 +25,12 @@ async fn main() -> AppResult<()> {
init_logger(log_file); init_logger(log_file);
let records = Arc::new(Mutex::new(Vec::new())); let records = Arc::new(Mutex::new(Vec::new()));
let db = DB::new(Arc::clone(&records)).await?; let (s, r) = bounded::<bool>(2);
let db = DB::new(Arc::clone(&records), s).await?;
// Create an application. // Create an application.
let mut app = App::new(db, records); let mut app = App::new(db, records, r);
// Initialize the terminal user interface. // Initialize the terminal user interface.
let backend = CrosstermBackend::new(io::stderr()); let backend = CrosstermBackend::new(io::stderr());
@ -54,12 +58,12 @@ async fn main() -> AppResult<()> {
pub fn init_logger(output_file: String) { pub fn init_logger(output_file: String) {
// TODO: configure the log levels to something appropriate // TODO: configure the log levels to something appropriate
CombinedLogger::init(vec![ CombinedLogger::init(vec![
TermLogger::new( // TermLogger::new(
LevelFilter::Info, // LevelFilter::Info,
Config::default(), // Config::default(),
TerminalMode::Mixed, // TerminalMode::Mixed,
ColorChoice::Auto, // ColorChoice::Auto,
), // ),
WriteLogger::new( WriteLogger::new(
LevelFilter::Info, LevelFilter::Info,
Config::default(), Config::default(),

View file

@ -5,6 +5,7 @@ use ratatui::{
widgets::{Block, Borders}, widgets::{Block, Borders},
Frame, style::Style, Frame, style::Style,
}; };
use log;
use crate::uis::{render_navigation_frame, render_new_transaction_tab}; use crate::uis::{render_navigation_frame, render_new_transaction_tab};

View file

@ -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<B: Backend> (f: &mut Frame<B>, body_rect: Rect, app: &mut App) { pub fn render_history_tab<B: Backend> (f: &mut Frame<B>, 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() let split_body = Layout::default()
.direction(Direction::Horizontal) .direction(Direction::Horizontal)
.constraints([Constraint::Percentage(50), Constraint::Percentage(50)]) .constraints([Constraint::Percentage(50), Constraint::Percentage(50)])