commiting old work

main
Zynh0722 2023-10-24 06:17:34 -07:00
parent 087916a9bc
commit f6bad75438
8 changed files with 190 additions and 12 deletions

1
.gitignore vendored
View File

@ -1,2 +1,3 @@
/target
.env
Session.vim

View File

@ -1,2 +1,3 @@
pub mod models;
pub mod schema;
pub mod report;

View File

@ -1,6 +1,6 @@
use diesel::prelude::*;
#[derive(Queryable, Selectable, Debug)]
#[derive(Identifiable, Queryable, Selectable, Debug)]
#[diesel(table_name = crate::schema::shifts)]
#[diesel(check_for_backend(diesel::mysql::Mysql))]
pub struct Shift {
@ -9,7 +9,8 @@ pub struct Shift {
pub end: Option<chrono::NaiveDateTime>,
}
#[derive(Queryable, Selectable, Debug)]
#[derive(Identifiable, Queryable, Associations, PartialEq, Selectable, Debug)]
#[diesel(belongs_to(Shift, foreign_key = shift))]
#[diesel(table_name = crate::schema::drinks)]
#[diesel(check_for_backend(diesel::mysql::Mysql))]
pub struct Drink {

40
src/lib/report.rs Normal file
View File

@ -0,0 +1,40 @@
use crate::models::Drink;
use std::collections::HashMap;
pub struct DrinkReport {
pub summary: HashMap<u32, u32>,
}
impl DrinkReport {
pub fn into_sorted(self) -> impl Iterator<Item = (u32, u32)> {
let mut array: Vec<(u32, u32)> = self.summary.into_iter().collect();
array.sort_by(|(a, _), (b, _)| a.cmp(b));
array.into_iter()
}
}
pub trait GenerateDrinkReport {
fn generate_report(&self) -> DrinkReport;
}
impl GenerateDrinkReport for Vec<Drink> {
fn generate_report(&self) -> DrinkReport {
let mut summary: HashMap<u32, u32> = HashMap::new();
// init with all default drink prices
for price in [2u32, 3u32, 5u32, 8u32, 15u32].into_iter() {
summary.insert(price, 0);
}
for drink in self {
summary
.entry(drink.price)
.and_modify(|v| *v += drink.quantity)
.or_insert(drink.quantity);
}
DrinkReport { summary }
}
}

View File

@ -1,22 +1,37 @@
mod api;
mod axum_ructe;
use axum::extract::Path;
use axum_ructe::render;
use axum::{extract::State, response::IntoResponse, routing::get, Router};
use cm_lib::models::Shift;
use diesel::result::OptionalExtension;
use diesel::{ExpressionMethods, QueryDsl, SelectableHelper};
use cm_lib::{
models::{Drink, Shift},
report::GenerateDrinkReport,
schema::shifts,
};
use axum::{
extract::{Path, State},
response::IntoResponse,
routing::get,
Router,
};
use diesel::{
result::OptionalExtension, BelongingToDsl, ExpressionMethods, QueryDsl, SelectableHelper,
};
use diesel_async::{
pooled_connection::{deadpool::Pool, AsyncDieselConnectionManager},
AsyncMysqlConnection, RunQueryDsl,
};
use dotenvy::dotenv;
use std::net::SocketAddr;
use tower_http::services::{ServeDir, ServeFile};
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
use crate::templates::shift_reports_html;
include!(concat!(env!("OUT_DIR"), "/templates.rs"));
async fn establish_connection() -> Pool<AsyncMysqlConnection> {
@ -63,7 +78,9 @@ async fn main() {
let app = Router::new()
.nest("/api", api::router())
.route("/", get(root))
.route("/shift_reports", get(shift_reports))
.route("/shifts/:id/drinks", get(drinks))
.route("/shifts/:id/report", get(shift_report))
.fallback_service(fallback_handler)
.with_state(state);
@ -100,3 +117,28 @@ async fn root(State(state): State<AppState>) -> impl IntoResponse {
async fn drinks(Path(id): Path<u32>) -> impl IntoResponse {
render!(templates::drinks_html, id)
}
async fn shift_report(State(state): State<AppState>, Path(id): Path<u32>) -> impl IntoResponse {
let mut conn = state.connection.get().await.unwrap();
let shift: Shift = shifts::table.find(id).first(&mut conn).await.unwrap();
let drinks: Vec<Drink> = Drink::belonging_to(&shift).load(&mut conn).await.unwrap();
render!(
crate::templates::shift_report_html,
drinks.generate_report()
)
}
async fn shift_reports(State(state): State<AppState>) -> impl IntoResponse {
let mut conn = state.connection.get().await.unwrap();
let mut shifts: Vec<Shift> = shifts::table
.select(Shift::as_select())
.load(&mut conn)
.await
.unwrap();
shifts.sort_by(|a, b| b.start.cmp(&a.start));
render!(shift_reports_html, shifts)
}

View File

@ -17,6 +17,11 @@
</a>
</div>
}
<div hx-boost="true">
<a href="/shift_reports">
<button>Shift Reports</button>
</a>
</div>
</main>
})

View File

@ -0,0 +1,41 @@
@use super::base_html;
@use cm_lib::report::DrinkReport;
@(drinks: DrinkReport)
@:base_html({
<div style="display: flex; gap: 2rem;">
<div hx-boost="true">
<a href="/">
<button>Return to Main Page</button>
</a>
</div>
<div hx-boost="true">
<a href="/shift_reports">
<button>Return to Shift Reports</button>
</a>
</div>
</div>
<table>
<thead>
<tr>
<th>Price</th>
<th>Quantity</th>
</tr>
</thead>
<tbody>
@for (price, quantity) in drinks.into_sorted() {
<tr>
<td>@price</td>
<td>@quantity</td>
</tr>
}
</tbody>
</table>
})

View File

@ -0,0 +1,47 @@
@use super::base_html;
@use cm_lib::models::Shift;
@(shifts: Vec<Shift>)
@:base_html({
<div hx-boost="true">
<a href="/">
<button>Return to Main Page</button>
</a>
</div>
<table>
<thead>
<tr>
<th>Shift ID</th>
<th>Shift Start</th>
<th>Shift End</th>
</tr>
</thead>
<tbody>
@for shift in shifts {
<tr>
<td>@shift.id</td>
<td>@shift.start.format("%Y-%m-%d %H:%M:%S")</td>
<td>
@if let Some(end) = shift.end {
@end.format("%Y-%m-%d %H:%M:%S")
} else {
Currently Open
}
</td>
<td hx-boost="true">
<a href="/shifts/@shift.id/report">
<button>View Shift</button>
</a>
</td>
</tr>
}
</tbody>
</table>
})