You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
fast-insiders/server/src/route/transaction.rs

191 lines
5.2 KiB

use axum::extract::{Json, Path, Query, State};
use chrono::{NaiveDate, NaiveDateTime};
use sea_orm::{
prelude::*, DbBackend, FromQueryResult, ItemsAndPagesNumber, JoinType, Order, QueryOrder,
QuerySelect, Statement,
};
use serde::{Deserialize, Serialize};
use crate::db::paginate::{paginate_also_related, PaginatedResponse};
use crate::error::AppError;
use crate::{model, AppState};
use super::{CompanySlug, Pagination};
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct TransactionCompany {
pub id: i32,
pub foreign_id: String,
pub date_published: NaiveDate,
pub date_executed: NaiveDate,
pub person: String,
pub exchange: String,
pub nature: String,
pub isin: Option<String>,
pub instrument: String,
pub volume: i32,
pub unit_price: f32,
pub created_at_utc: NaiveDateTime,
pub company: Option<model::company::Model>,
}
pub async fn get_all(
state: State<AppState>,
Query(CompanySlug { company_slug }): Query<CompanySlug>,
Query(Pagination { page, size }): Query<Pagination>,
) -> Result<Json<PaginatedResponse<TransactionCompany>>, AppError> {
let db = &state.db;
let mut filters = vec![];
if let Some(c) = company_slug {
filters.push(model::company::Column::Slug.eq(c))
}
let res = paginate_also_related::<
model::transaction::Entity,
model::company::Entity,
model::transaction::Model,
model::company::Model,
model::transaction::Column,
>(
db,
page,
size,
Some(model::transaction::Column::DatePublished),
Some(Order::Desc),
Some(filters),
)
.await?;
let list = res
.list
.iter()
.map(|t| TransactionCompany {
id: t.0.id,
foreign_id: t.0.foreign_id.to_owned(),
date_published: t.0.date_published,
date_executed: t.0.date_executed,
person: t.0.person.to_owned(),
exchange: t.0.exchange.to_owned(),
nature: t.0.nature.to_owned(),
isin: t.0.isin.clone(),
instrument: t.0.instrument.to_owned(),
volume: t.0.volume,
unit_price: t.0.unit_price,
created_at_utc: t.0.created_at_utc,
company: t.1.to_owned(),
})
.collect();
let res = PaginatedResponse {
count: res.count,
num_pages: res.num_pages,
list,
};
Ok(Json(res))
}
#[derive(FromQueryResult, Serialize)]
pub struct LatestTransaction {
company_name: String,
slug: String,
nature: String,
total: f32,
}
pub async fn get_latest_transactions(
state: State<AppState>,
Query(pagination): Query<Pagination>,
) -> Result<Json<PaginatedResponse<LatestTransaction>>, AppError> {
let db = &state.db;
let s = pagination.size.unwrap_or(20).min(50);
let query_raw = "SELECT
company.name as company_name,
company.slug,
transaction.nature,
SUM(transaction.volume * transaction.unit_price) as total
FROM transaction
JOIN company ON transaction.company_id = company.id
WHERE DATE(created_at_utc) IN (SELECT DATE(MAX(created_at_utc)) FROM transaction)
GROUP BY company.name, transaction.nature
ORDER BY transaction.nature, total DESC"
.to_string();
let query = model::transaction::Entity::find()
.from_raw_sql(Statement::from_string(
DbBackend::MySql,
query_raw.to_string(),
))
.into_model::<LatestTransaction>();
let pages = query.paginate(db, s);
let ItemsAndPagesNumber {
number_of_items: count,
number_of_pages: num_pages,
} = pages.num_items_and_pages().await?;
let p = pagination.page.unwrap_or(0).min(num_pages);
let list = pages.fetch_page(p).await?;
let res = PaginatedResponse {
count,
num_pages,
list,
};
Ok(Json(res))
}
pub async fn get_aggregated_transactions(
state: State<AppState>,
pagination: Query<Pagination>,
) -> Result<Json<PaginatedResponse<TransactionsAggregated>>, AppError> {
let db = &state.db;
let s = pagination.0.size.unwrap_or(20).min(50);
let query = model::company::Entity::find()
.select_only()
.join(
JoinType::InnerJoin,
model::company::Relation::Transaction.def(),
)
.column(model::company::Column::Id)
.column(model::company::Column::Name)
.column(model::company::Column::Slug)
.column_as(model::transaction::Column::Id.count(), "transaction_count");
let pages = query
.group_by(model::company::Column::Name)
.order_by(model::transaction::Column::Id.count(), Order::Desc)
.into_model::<TransactionsAggregated>()
.paginate(db, s);
let ItemsAndPagesNumber {
number_of_items: count,
number_of_pages: num_pages,
} = pages.num_items_and_pages().await?;
let p = pagination.0.page.unwrap_or(0).min(num_pages);
let list = pages.fetch_page(p).await?;
let res = PaginatedResponse {
count,
num_pages,
list,
};
Ok(Json(res))
}
#[derive(Serialize, FromQueryResult, Debug)]
pub struct TransactionsAggregated {
id: i32,
name: String,
slug: String,
transaction_count: i32,
}