Added nice security page.
This commit is contained in:
1 parent
18e7b0df1c
commit
509794f02d
12 files changed
+103
-25
No files matched your search
@@ -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
|
||||
|
||||
|
||||
@@ -8,8 +8,8 @@
|
||||
|
||||
<table class="uk-table uk-table-divider uk-table-striped uk-table-hover uk-table-small">
|
||||
<caption>Summary</caption>
|
||||
<tr><th>Micro ID</th><td>{{ account.id | micro_id }}</td></tr>
|
||||
<tr><th>Account ID</th><td>{{ account.id }}</td></tr>
|
||||
<tr><th>Micro ID</th><td style="font-family: monospace;">{{ account.id | micro_id }}</td></tr>
|
||||
<tr><th>Account ID</th><td style="font-family: monospace;">{{ account.id }}</td></tr>
|
||||
<tr><th>Label</th><td>{{ account.label }}</td></tr>
|
||||
<tr><th>Amount</th><td>{{ account.amount | thousand_separator }}</td></tr>
|
||||
<tr><th>Account Type</th><td>{{ account.account_type | replace("_", " ") | lower}}</td></tr>
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
{% for account in accounts %}
|
||||
<tr>
|
||||
<td><a href="/accounts/{{ account.id }}"><span uk-icon="info"></span></a></td>
|
||||
<td>{{ account.id | micro_id}}</td>
|
||||
<td style="font-family: monospace;">{{ account.id | micro_id}}</td>
|
||||
<td>{{ account.label }}</td>
|
||||
<td>{{ account.amount | thousand_separator }}</td>
|
||||
<td style="font-variant: small-caps;">{{ account.account_type | replace("_", " ") | lower}}</td>
|
||||
|
||||
@@ -20,10 +20,10 @@
|
||||
{% for sec in securities %}
|
||||
<tr>
|
||||
<td><a href="/securities/{{ sec.id }}"><span uk-icon="info"></span></a></td>
|
||||
<td>{{ sec.id | micro_id }}</td>
|
||||
<td style="font-family: monospace;">{{ sec.id | micro_id }}</td>
|
||||
<td>{{ sec.name }}</td>
|
||||
<td>{{ sec.symbol }}</td>
|
||||
<td>{{ sec.security_type }}</td>
|
||||
<td style="font-variant: small-caps;">{{ sec.symbol | lower }}</td>
|
||||
<td style="font-variant: small-caps;">{{ sec.security_type }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
|
||||
@@ -4,7 +4,18 @@
|
||||
|
||||
{% block body %}
|
||||
|
||||
<h1>Security!</h1>
|
||||
<table class="uk-table uk-table-divider uk-table-striped uk-table-hover uk-table-small">
|
||||
<caption>Summary</caption>
|
||||
<tr><th>Micro ID</th><td style="font-family: monospace;">{{ security.id | micro_id }}</td></tr>
|
||||
<tr><th>Account ID</th><td style="font-family: monospace;">{{ security.id }}</td></tr>
|
||||
<tr><th>Name</th><td>{{ security.name }}</td></tr>
|
||||
<tr><th>Symbol</th><td style="font-variant: small-caps;">{{ security.symbol | lower }}</td></tr>
|
||||
<tr><th>Security Type</th><td style="font-variant: small-caps;">{{ security.security_type }}</td></tr>
|
||||
<tr><th>Number of Checking Accounts</th><td>{{ account_numbers.checking }}</td></tr>
|
||||
<tr><th>Number of Security Emitter Accounts</th><td>{{ account_numbers.security_emitter }}</td></tr>
|
||||
<tr><th>Market Cap</th><td>{{ market_cap | thousand_separator}}</td></tr>
|
||||
</table>
|
||||
|
||||
|
||||
|
||||
{% endblock %}
|
||||
@@ -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<i64>{
|
||||
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<i64> {
|
||||
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};
|
||||
|
||||
@@ -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<Security> {
|
||||
pub fn create_security(tx: &Connection, sec: &NewSecurity) -> CustomResult<Security> {
|
||||
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<Security> {
|
||||
pub fn get_security_by_id(tx: &Connection, id: & SecurityId) -> CustomResult<Option<Security>> {
|
||||
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<Security> {
|
||||
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<Vec<Security>> {
|
||||
|
||||
+2
-1
@@ -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
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -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<AccountNumbers>{
|
||||
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})
|
||||
}
|
||||
}
|
||||
@@ -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<SecurityDTO> {
|
||||
list_securities(self.conn)
|
||||
pub fn list_securities(&self) -> Vec<SecurityDTO> {
|
||||
securities::list_securities(self.conn)
|
||||
.unwrap()
|
||||
.iter()
|
||||
.map(SecurityDTO::from_entity)
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn get_security_by_id(&self, sec_id: &SecurityId) -> CustomResult<Option<SecurityDTO>> {
|
||||
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<i64> {
|
||||
accounts::get_security_market_cap(self.conn, sec_id)
|
||||
}
|
||||
}
|
||||
@@ -65,6 +65,5 @@ impl ServicesFactory {
|
||||
conn: &'a PooledConnection<SqliteConnectionManager>,
|
||||
)-> Result<AccountService> {
|
||||
Ok(AccountService::new(conn))
|
||||
|
||||
}
|
||||
}
|
||||
+20
-3
@@ -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<ServicesFactory>) -> actix_web::Result<impl Responder> {
|
||||
pub async fn security(req: HttpRequest,
|
||||
tmpl_env: MiniJinjaRenderer,
|
||||
service_factory: web::Data<ServicesFactory>) -> actix_web::Result<impl Responder> {
|
||||
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)
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user