Skip to content

Commit

Permalink
Activity page
Browse files Browse the repository at this point in the history
  • Loading branch information
Celeo committed Mar 15, 2024
1 parent e4f6e3c commit 319ef07
Show file tree
Hide file tree
Showing 4 changed files with 150 additions and 1 deletion.
2 changes: 2 additions & 0 deletions src/bin/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,13 +78,15 @@ fn load_router(
// https://github.com/tokio-rs/axum/blob/main/examples/graceful-shutdown/src/main.rs
async fn shutdown_signal() {
let ctrl_c = async {
debug!("Got terminate signal");
signal::ctrl_c()
.await
.expect("failed to install Ctrl+C handler");
};

#[cfg(unix)]
let terminate = async {
debug!("Got terminate signal");
signal::unix::signal(signal::unix::SignalKind::terminate())
.expect("failed to install signal handler")
.recv()
Expand Down
103 changes: 102 additions & 1 deletion src/endpoints/facility.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
use crate::shared::{
sql::{self, Certification, Controller},
sql::{self, Activity, Certification, Controller},
AppError, AppState, Config, UserInfo, SESSION_USER_INFO_KEY,
};
use axum::{extract::State, response::Html, routing::get, Router};
use chrono::{Months, Utc};
use itertools::Itertools;
use log::warn;
use minijinja::{context, Environment};
Expand Down Expand Up @@ -233,15 +234,115 @@ async fn page_staff(
Ok(Html(rendered))
}

/// View all controller's recent (summarized) controlling activity.
async fn page_activity(
State(state): State<Arc<AppState>>,
session: Session,
) -> Result<Html<String>, AppError> {
#[derive(Debug, Serialize)]
struct ControllerActivity {
name: String,
ois: String,
month_0: u32,
month_1: u32,
month_2: u32,
month_3: u32,
month_4: u32,
}

let controllers: Vec<Controller> = sqlx::query_as(sql::GET_ALL_CONTROLLERS)
.fetch_all(&state.db)
.await?;
let activity: Vec<Activity> = sqlx::query_as(sql::GET_ALL_ACTIVITY)
.fetch_all(&state.db)
.await?;

let now = Utc::now();
let months: [String; 5] = [
now.format("%Y-%m").to_string(),
now.checked_sub_months(Months::new(1))
.unwrap()
.format("%Y-%m")
.to_string(),
now.checked_sub_months(Months::new(2))
.unwrap()
.format("%Y-%m")
.to_string(),
now.checked_sub_months(Months::new(3))
.unwrap()
.format("%Y-%m")
.to_string(),
now.checked_sub_months(Months::new(4))
.unwrap()
.format("%Y-%m")
.to_string(),
];
let activity_data: Vec<ControllerActivity> = controllers
.iter()
.map(|controller| {
let this_controller: Vec<_> = activity
.iter()
.filter(|a| a.cid == controller.cid)
.collect();
ControllerActivity {
name: format!("{} {}", controller.first_name, controller.last_name),
ois: match &controller.operating_initials {
Some(ois) => ois.to_owned(),
None => String::new(),
},
month_0: this_controller
.iter()
.filter(|a| a.month == months[0])
.map(|a| a.minutes)
.sum(),
month_1: this_controller
.iter()
.filter(|a| a.month == months[1])
.map(|a| a.minutes)
.sum(),
month_2: this_controller
.iter()
.filter(|a| a.month == months[2])
.map(|a| a.minutes)
.sum(),
month_3: this_controller
.iter()
.filter(|a| a.month == months[3])
.map(|a| a.minutes)
.sum(),
month_4: this_controller
.iter()
.filter(|a| a.month == months[4])
.map(|a| a.minutes)
.sum(),
}
})
.collect();

let user_info: Option<UserInfo> = session.get(SESSION_USER_INFO_KEY).await?;
let template = state.templates.get_template("activity")?;
let rendered = template.render(context! { user_info, activity_data })?;
Ok(Html(rendered))
}

pub fn router(templates: &mut Environment) -> Router<Arc<AppState>> {
templates
.add_template("roster", include_str!("../../templates/roster.jinja"))
.unwrap();
templates
.add_template("staff", include_str!("../../templates/staff.jinja"))
.unwrap();
templates
.add_template("activity", include_str!("../../templates/activity.jinja"))
.unwrap();
templates.add_filter("minutes_to_hm", |total_minutes: u32| {
let hours = total_minutes / 60;
let minutes = total_minutes % 60;
format!("{hours}h{minutes}m")
});

Router::new()
.route("/facility/roster", get(page_roster))
.route("/facility/staff", get(page_staff))
.route("/facility/activity", get(page_activity))
}
10 changes: 10 additions & 0 deletions src/shared/sql.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,14 @@ pub struct Certification {
pub set_by: u32,
}

#[derive(Debug, FromRow, Serialize)]
pub struct Activity {
pub id: u32,
pub cid: u32,
pub month: String,
pub minutes: u32,
}

/// Statements to create tables. Only ran when the DB file does not exist,
/// so no migration or "IF NOT EXISTS" conditions need to be added.
pub const CREATE_TABLES: &str = r#"
Expand Down Expand Up @@ -156,3 +164,5 @@ VALUES
";

pub const DELETE_FROM_ROSTER: &str = "DELETE FROM controller WHERE cid=$1";

pub const GET_ALL_ACTIVITY: &str = "SELECT * FROM activity";
36 changes: 36 additions & 0 deletions templates/activity.jinja
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
{% extends "_layout" %}

{% block title %}Activity | {{ super() }}{% endblock %}

{% block body %}

<h2>Activity</h2>

<table class="table table-striped table-hover">
<thead>
<tr>
<th>Who</th>
<th>This month</th>
<th>Last month</th>
<th>2 months ago</th>
<th>3 months ago</th>
<th>4 months ago</th>
</tr>
</thead>
<tbody>
{% for row in activity_data %}
<tr>
<td>
{{ row.name }} {% if row.ois %}({{ row.ois }}){% endif %}
</td>
<td>{{ row.month_0|minutes_to_hm }}</td>
<td>{{ row.month_1|minutes_to_hm }}</td>
<td>{{ row.month_2|minutes_to_hm }}</td>
<td>{{ row.month_3|minutes_to_hm }}</td>
<td>{{ row.month_4|minutes_to_hm }}</td>
</tr>
{% endfor %}
</tbody>
</table>

{% endblock %}

0 comments on commit 319ef07

Please sign in to comment.