From 509794f02d20a0d3a5be7d748c234c1579fc03a3 Mon Sep 17 00:00:00 2001 From: didier amyot Date: Tue, 11 Apr 2023 09:05:31 -0400 Subject: [PATCH] Added nice security page. --- README.md | 7 +++++-- resources/templates/account.html | 4 ++-- resources/templates/accounts.html | 2 +- resources/templates/securities.html | 6 +++--- resources/templates/security.html | 13 ++++++++++++- src/db/_accounts.rs | 25 +++++++++++++++++++++++++ src/db/_securities.rs | 13 ++++++++----- src/db/mod.rs | 3 ++- src/services/_accounts.rs | 14 +++++++++++--- src/services/_securities.rs | 17 ++++++++++++++--- src/webui/services.rs | 1 - src/webui/views.rs | 23 ++++++++++++++++++++--- 12 files changed, 103 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index 8fc7ae1..842f2a5 100644 --- a/README.md +++ b/README.md @@ -39,13 +39,16 @@ nix develop #### Architecture -* [x] put all the ressources (migrations, templates and statics) into a ressources directory +* [x] put all the resources (migrations, templates and statics) into a resources directory #### Backend * [ ] Parametrize `CURRENT_TIMESTAMP` * [ ] Make certain the user that creates the event is allowed to do it. -* [ ] sd +* [ ] Add interest rates to currency accounts +* [ ] Add market cap table to be able to track market cap by time +* [ ] Add a minimum amount of security to exchange + #### Front end diff --git a/resources/templates/account.html b/resources/templates/account.html index 4a785b3..abe7213 100644 --- a/resources/templates/account.html +++ b/resources/templates/account.html @@ -8,8 +8,8 @@ - - + + diff --git a/resources/templates/accounts.html b/resources/templates/accounts.html index 0d27a1e..fbc1247 100644 --- a/resources/templates/accounts.html +++ b/resources/templates/accounts.html @@ -21,7 +21,7 @@ {% for account in accounts %} - + diff --git a/resources/templates/securities.html b/resources/templates/securities.html index 51f9a34..3f520f3 100644 --- a/resources/templates/securities.html +++ b/resources/templates/securities.html @@ -20,10 +20,10 @@ {% for sec in securities %} - + - - + + {% endfor %} diff --git a/resources/templates/security.html b/resources/templates/security.html index a9eb4d9..a2a48a3 100644 --- a/resources/templates/security.html +++ b/resources/templates/security.html @@ -4,7 +4,18 @@ {% block body %} -

Security!

+
Summary
Micro ID{{ account.id | micro_id }}
Account ID{{ account.id }}
Micro ID{{ account.id | micro_id }}
Account ID{{ account.id }}
Label{{ account.label }}
Amount{{ account.amount | thousand_separator }}
Account Type{{ account.account_type | replace("_", " ") | lower}}
{{ account.id | micro_id}}{{ account.id | micro_id}} {{ account.label }} {{ account.amount | thousand_separator }} {{ account.account_type | replace("_", " ") | lower}}
{{ sec.id | micro_id }}{{ sec.id | micro_id }} {{ sec.name }}{{ sec.symbol }}{{ sec.security_type }}{{ sec.symbol | lower }}{{ sec.security_type }}
+ + + + + + + + + +
Summary
Micro ID{{ security.id | micro_id }}
Account ID{{ security.id }}
Name{{ security.name }}
Symbol{{ security.symbol | lower }}
Security Type{{ security.security_type }}
Number of Checking Accounts{{ account_numbers.checking }}
Number of Security Emitter Accounts{{ account_numbers.security_emitter }}
Market Cap{{ market_cap | thousand_separator}}
+ {% endblock %} \ No newline at end of file diff --git a/src/db/_accounts.rs b/src/db/_accounts.rs index 8aed5d6..83481b4 100644 --- a/src/db/_accounts.rs +++ b/src/db/_accounts.rs @@ -397,6 +397,31 @@ pub fn add_to_account( return ret; } +pub fn get_number_of_accounts(tx: &Connection, + sec: &SecurityId, + account_type: AccountType) -> CustomResult{ + let q = " + SELECT COUNT(*) AS count + FROM accounts + WHERE security = ? + AND account_type = ? + "; + let params = params![sec, account_type.to_string()]; + tx.query_row(q, params, |row| row.get("count")).map_err(Error::SqlError) +} + +pub fn get_security_market_cap(tx: &Connection, + sec: &SecurityId) -> CustomResult { + let q = " + SELECT -1 * SUM(amount) AS count + FROM accounts + WHERE security = ? + AND account_type = ? + "; + let params = params![sec, AccountType::SecurityEmitter.to_string()]; + tx.query_row(q, params, |row| row.get("count")).map_err(Error::SqlError) +} + #[cfg(test)] mod test_accounts { use rusqlite::{Connection, Transaction}; diff --git a/src/db/_securities.rs b/src/db/_securities.rs index 6ad6aa1..ec5cc3a 100644 --- a/src/db/_securities.rs +++ b/src/db/_securities.rs @@ -1,4 +1,4 @@ -use rusqlite::{params, Connection, Result, Row, ToSql, Transaction}; +use rusqlite::{params, Connection, Result, Row, ToSql, Transaction, OptionalExtension}; use serde::Serialize; use crate::db::_external_id::{Id, SecurityId}; @@ -104,18 +104,19 @@ pub struct NewSecurity { pub name: String, } -pub fn create_security(tx: &Transaction, sec: &NewSecurity) -> CustomResult { +pub fn create_security(tx: &Connection, sec: &NewSecurity) -> CustomResult { let q = "INSERT INTO securities(name, symbol, security_type) VALUES(?1, ?2, ?3)"; let params = (&sec.name, &sec.symbol, &sec.security_type); tx.execute(q, params).map_err(Error::SqlError)?; - let new_sec_id = tx.last_insert_rowid() as i32; + let new_sec_id:i64 = tx.last_insert_rowid(); - return get_security_by_id(tx, new_sec_id); + let ret = get_security_by_id(tx, &SecurityId::from_id(new_sec_id))?; + Ok(ret.unwrap()) } -pub fn get_security_by_id(tx: &Transaction, id: i32) -> CustomResult { +pub fn get_security_by_id(tx: &Connection, id: & SecurityId) -> CustomResult> { let q = "SELECT id AS sec_id, security_type AS sec_security_type, symbol AS sec_symbol, @@ -123,7 +124,9 @@ pub fn get_security_by_id(tx: &Transaction, id: i32) -> CustomResult { FROM securities WHERE id = ?1"; tx.query_row(q, params![id], Security::from_row) + .optional() .map_err(Error::SqlError) + } pub fn list_securities(conn: &Connection) -> CustomResult> { diff --git a/src/db/mod.rs b/src/db/mod.rs index 799f65b..dd02678 100644 --- a/src/db/mod.rs +++ b/src/db/mod.rs @@ -30,7 +30,8 @@ pub mod accounts { pub use super::_accounts::{ add_to_account, create_checking_account, create_security_emitter_account, get_account_by_id, is_owner, list_accounts, Account, AccountChangeReceipt, - list_user_accounts, AccountType, get_checking_accounts_for_sec_id + list_user_accounts, AccountType, get_checking_accounts_for_sec_id, get_number_of_accounts, + get_security_market_cap }; } diff --git a/src/services/_accounts.rs b/src/services/_accounts.rs index 66312bf..4ca9070 100644 --- a/src/services/_accounts.rs +++ b/src/services/_accounts.rs @@ -11,6 +11,11 @@ use crate::services::_securities::SecurityDTO; pub struct AccountService<'a> { conn: &'a Connection, } +#[derive(Deserialize, Serialize, Debug)] +pub struct AccountNumbers { + checking: i64, + security_emitter: i64, +} #[derive(Deserialize, Serialize, Debug)] pub struct AccountDTO { @@ -42,8 +47,6 @@ impl AccountDTO { } - - impl AccountService<'_> { pub fn new(conn: & Connection) -> AccountService{ @@ -80,7 +83,12 @@ impl AccountService<'_> { amounts: ac.amount().clone(), timestamps: ac.timestamps().clone(), }; - Ok(ret) } + + pub fn get_number_of_accounts(&self, sec_id: &SecurityId) -> CustomResult{ + let checking = accounts::get_number_of_accounts(self.conn, sec_id, AccountType::Checking)?; + let security_emitter = accounts::get_number_of_accounts(self.conn, sec_id, AccountType::SecurityEmitter)?; + Ok(AccountNumbers{checking, security_emitter}) + } } \ No newline at end of file diff --git a/src/services/_securities.rs b/src/services/_securities.rs index 849fc10..4c8f0fe 100644 --- a/src/services/_securities.rs +++ b/src/services/_securities.rs @@ -1,7 +1,9 @@ use crate::db::external_id::SecurityId; -use crate::db::securities::{list_securities, Security}; +use crate::db::securities::{Security}; use rusqlite::Connection; use serde::{Deserialize, Serialize}; +use crate::db::{accounts, securities}; +use crate::util::CustomResult; #[derive(Serialize, Deserialize, Debug)] pub struct SecurityDTO { @@ -31,11 +33,20 @@ impl SecurityService<'_> { SecurityService { conn } } - pub fn list_securities(self) -> Vec { - list_securities(self.conn) + pub fn list_securities(&self) -> Vec { + securities::list_securities(self.conn) .unwrap() .iter() .map(SecurityDTO::from_entity) .collect() } + + pub fn get_security_by_id(&self, sec_id: &SecurityId) -> CustomResult> { + let ret = securities::get_security_by_id(self.conn, sec_id)?; + Ok(ret.map(|sec|SecurityDTO::from_entity(&sec))) + } + + pub fn get_security_market_cap(&self, sec_id: &SecurityId)-> CustomResult { + accounts::get_security_market_cap(self.conn, sec_id) + } } diff --git a/src/webui/services.rs b/src/webui/services.rs index 8cef64b..a9e53e6 100644 --- a/src/webui/services.rs +++ b/src/webui/services.rs @@ -65,6 +65,5 @@ impl ServicesFactory { conn: &'a PooledConnection, )-> Result { Ok(AccountService::new(conn)) - } } diff --git a/src/webui/views.rs b/src/webui/views.rs index 0404412..45c5f18 100644 --- a/src/webui/views.rs +++ b/src/webui/views.rs @@ -65,10 +65,27 @@ pub async fn securities(tmpl_env: MiniJinjaRenderer, } #[actix_web::get("/securities/{sec_id}")] -pub async fn security(tmpl_env: MiniJinjaRenderer, - service_factory: web::Data) -> actix_web::Result { +pub async fn security(req: HttpRequest, + tmpl_env: MiniJinjaRenderer, + service_factory: web::Data) -> actix_web::Result { + let conn = service_factory.get_connection()?; + let security_service = service_factory.get_security_service(& conn)?; + let account_service = service_factory.get_account_service(& conn)?; + let sec_id = &req.match_info() + .get("sec_id") + .and_then(|param|SecurityId::from_string(¶m.to_string())) + .ok_or(error::ErrorNotFound("Invalid sec-id parameters"))?; - let ctx = context!{}; + + let sec = security_service.get_security_by_id(sec_id).map_err(error::ErrorInternalServerError)?; + if sec.is_none() { + return Err(error::ErrorNotFound(format!("No security with id {:}", sec_id))) + } + let ctx = context!{ + security => sec.unwrap(), + account_numbers => account_service.get_number_of_accounts(sec_id).map_err(error::ErrorInternalServerError)?, + market_cap => security_service.get_security_market_cap(sec_id).map_err(error::ErrorInternalServerError)?, + }; tmpl_env.render("security.html", ctx) }