Skip to content

Commit

Permalink
Add documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
aviguptatx committed Mar 6, 2024
1 parent 60e7a6d commit 2bd4d6b
Show file tree
Hide file tree
Showing 3 changed files with 252 additions and 117 deletions.
68 changes: 68 additions & 0 deletions src/database.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,16 @@ use crate::models::{
};
use crate::util::compute_percentiles;

/// Fetches the results for a given date from the database.
///
/// # Arguments
///
/// * `date` - A string representing the date in "YYYY-MM-DD" format.
/// * `client` - A reference to the Postgrest client.
///
/// # Returns
///
/// A `Result` containing a vector of `ResultEntry` structs, or an error if the database query fails.
pub async fn fetch_results(
date: &str,
client: &Postgrest,
Expand All @@ -29,6 +39,15 @@ pub async fn fetch_results(
Ok(result_data)
}

/// Fetches the most recent crossword date from the database.
///
/// # Arguments
///
/// * `client` - A reference to the Postgrest client.
///
/// # Returns
///
/// A `Result` containing the most recent crossword date as a `NaiveDate`, or an error if the database query fails.
pub async fn fetch_most_recent_crossword_date(
client: &Postgrest,
) -> Result<NaiveDate, Box<dyn Error>> {
Expand All @@ -54,6 +73,15 @@ pub async fn fetch_most_recent_crossword_date(
)?)
}

/// Fetches the usernames sorted by ELO rating from the database.
///
/// # Arguments
///
/// * `client` - A reference to the Postgrest client.
///
/// # Returns
///
/// A `Result` containing a vector of usernames as strings, or an error if the database query fails.
pub async fn fetch_usernames_sorted_by_elo(
client: &Postgrest,
) -> Result<Vec<String>, Box<dyn Error>> {
Expand All @@ -74,6 +102,15 @@ pub async fn fetch_usernames_sorted_by_elo(
.collect())
}

/// Fetches the top 10 results from the database, sorted by time.
///
/// # Arguments
///
/// * `client` - A reference to the Postgrest client.
///
/// # Returns
///
/// A `Result` containing a vector of `ResultEntry` structs, or an error if the database query fails.
pub async fn fetch_podium_data(client: &Postgrest) -> Result<Vec<ResultEntry>, Box<dyn Error>> {
let body = client
.from("results_rust")
Expand All @@ -92,6 +129,16 @@ pub async fn fetch_podium_data(client: &Postgrest) -> Result<Vec<ResultEntry>, B
Ok(podium_data)
}

/// Fetches the user data for a given username from the database.
///
/// # Arguments
///
/// * `username` - A reference to the username as a string.
/// * `client` - A reference to the Postgrest client.
///
/// # Returns
///
/// A `Result` containing a `UserData` struct, or an error if the database query fails.
pub async fn fetch_user_data(
username: &str,
client: &Postgrest,
Expand Down Expand Up @@ -131,6 +178,16 @@ pub async fn fetch_user_data(
})
}

/// Fetches the leaderboard data from the database.
///
/// # Arguments
///
/// * `db_name` - A string representing the name of the database table to query.
/// * `client` - A reference to the Postgrest client.
///
/// # Returns
///
/// A `Result` containing a vector of `LeaderboardEntry` structs, or an error if the database query fails.
pub async fn fetch_leaderboard_from_db(
db_name: &str,
client: &Postgrest,
Expand All @@ -151,6 +208,17 @@ pub async fn fetch_leaderboard_from_db(
Ok(leaderboard_data)
}

/// Fetches the head-to-head data for two users from the database.
///
/// # Arguments
///
/// * `user1` - A string representing the username of the first user.
/// * `user2` - A string representing the username of the second user.
/// * `client` - A reference to the Postgrest client.
///
/// # Returns
///
/// A `Result` containing a `HeadToHeadData` struct, or an error if the database query fails.
pub async fn fetch_h2h_data(
user1: String,
user2: String,
Expand Down
124 changes: 58 additions & 66 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,25 +1,22 @@
use askama::Template;

use chrono::Duration;
use postgrest::Postgrest;
use worker::{event, Context, Env, Request, Response, Result, RouteContext, Router};

mod database;
mod models;
mod templates;
mod util;

use database::{
use crate::database::{
fetch_h2h_data, fetch_leaderboard_from_db, fetch_most_recent_crossword_date, fetch_podium_data,
fetch_results, fetch_user_data, fetch_usernames_sorted_by_elo,
};
use models::HeadToHeadData;
use postgrest::Postgrest;
use templates::{
use crate::templates::{
HeadToHeadTemplate, HistoryTemplate, LeaderboardTemplate, PodiumTemplate, RecentTemplate,
TodayTemplate, UserTemplate, CSS_STYLES,
};
use util::{fetch_live_leaderboard, generate_box_plot_html, generate_scatter_plot_html};

use worker::{event, Context, Env, Request, Response, Result, RouteContext, Router};
use crate::util::{fetch_live_leaderboard, generate_box_plot_html, generate_scatter_plot_html};

fn get_db_client<T>(ctx: &RouteContext<T>) -> Result<Postgrest> {
let url = ctx.secret("SUPABASE_API_URL")?.to_string();
Expand Down Expand Up @@ -72,9 +69,10 @@ async fn main(req: Request, env: Env, _ctx: Context) -> Result<Response> {
async fn handle_index<T>(ctx: &RouteContext<T>, client: &Postgrest) -> Result<Response> {
let db_name = ctx.param("db_name").map_or("all", |str| str);

let Ok(leaderboard_entries) = fetch_leaderboard_from_db(db_name, client).await else {
return Response::error("Couldn't fetch leaderboard from database", 500);
};
let leaderboard_entries = fetch_leaderboard_from_db(db_name, client)
.await
.map_err(|e| format!("Couldn't fetch leaderboard from database: {}", e))?;

Response::from_html(
LeaderboardTemplate {
data: leaderboard_entries,
Expand All @@ -85,51 +83,46 @@ async fn handle_index<T>(ctx: &RouteContext<T>, client: &Postgrest) -> Result<Re
}

async fn handle_podium(client: &Postgrest) -> Result<Response> {
let Ok(podium_data) = fetch_podium_data(client).await else {
return Response::error("Couldn't fetch results from database", 500);
};
let podium_data = fetch_podium_data(client)
.await
.map_err(|e| format!("Couldn't fetch results from database: {}", e))?;

Response::from_html(PodiumTemplate { data: podium_data }.render().unwrap())
}

async fn handle_user<T>(ctx: &RouteContext<T>, client: &Postgrest) -> Result<Response> {
let username = match ctx.param("username") {
Some(username) => username.replace("%20", " "),
None => return Response::error("Couldn't process username parameter", 500),
};
let Ok(mut data) = fetch_user_data(&username, client).await else {
return Response::error("Couldn't fetch user data from database", 500);
None => return Err("Couldn't process username parameter".into()),
};

let scatter_plot_html = generate_scatter_plot_html(vec![&mut data.all_times]).map_or_else(
|_| String::from("Need more times before we can plot!"),
|scatter_plot_html| scatter_plot_html,
);
let mut user_data = fetch_user_data(&username, client)
.await
.map_err(|e| format!("Couldn't fetch user data from database: {}", e))?;

let box_plot_html = generate_box_plot_html(vec![&mut data.times_excluding_saturday])
.map_or_else(
|_| String::from("Need more times before we can plot!"),
|box_plot_html| box_plot_html,
);
let scatter_plot_html = generate_scatter_plot_html(vec![&mut user_data.all_times])
.unwrap_or_else(|_| String::from("Need more times before we can plot!"));

let box_plot_html = generate_box_plot_html(vec![&mut user_data.times_excluding_saturday])
.unwrap_or_else(|_| String::from("Need more times before we can plot!"));

Response::from_html(
UserTemplate {
username: username.to_string(),
scatter_plot_html,
box_plot_html,
data,
data: user_data,
}
.render()
.unwrap(),
)
}

async fn handle_history<T>(ctx: &RouteContext<T>, client: &Postgrest) -> Result<Response> {
let Some(date) = ctx.param("date") else {
return Response::error("Couldn't process date parameter", 500);
};
let Ok(data) = fetch_results(date, client).await else {
return Response::error("Couldn't fetch results from database", 500);
};
let date = ctx.param("date").ok_or("Couldn't process date parameter")?;
let data = fetch_results(date, client)
.await
.map_err(|e| format!("Couldn't fetch results from database: {}", e))?;

Response::from_html(
HistoryTemplate {
Expand All @@ -142,19 +135,23 @@ async fn handle_history<T>(ctx: &RouteContext<T>, client: &Postgrest) -> Result<
}

async fn handle_today<T>(ctx: &RouteContext<T>) -> Result<Response> {
let Ok(data) = fetch_live_leaderboard(ctx.secret("NYT_S_TOKEN")?.to_string()).await else {
return Response::error("Couldn't fetch live leaderboard from NYT API", 500);
};
let data = fetch_live_leaderboard(ctx.secret("NYT_S_TOKEN")?.to_string())
.await
.map_err(|e| format!("Couldn't fetch live leaderboard from NYT API: {}", e))?;

Response::from_html(TodayTemplate { data }.render().unwrap())
}

async fn handle_recent(client: &Postgrest) -> Result<Response> {
let Ok(most_recent_date) = fetch_most_recent_crossword_date(client).await else {
return Response::error(
"Couldn't fetch most recent crossword date from database",
500,
);
};
let most_recent_date = fetch_most_recent_crossword_date(client)
.await
.map_err(|e| {
format!(
"Couldn't fetch most recent crossword date from database: {}",
e
)
})?;

let dates: Vec<String> = (0..10)
.map(|i| {
(most_recent_date - Duration::days(i))
Expand All @@ -167,16 +164,16 @@ async fn handle_recent(client: &Postgrest) -> Result<Response> {
}

async fn handle_h2h<T>(ctx: &RouteContext<T>, client: &Postgrest) -> Result<Response> {
let Ok(users) = fetch_usernames_sorted_by_elo(client).await else {
return Response::error("Couldn't fetch usernames from database!", 500);
};
let usernames = fetch_usernames_sorted_by_elo(client)
.await
.map_err(|e| format!("Couldn't fetch usernames from database: {}", e))?;

let (user1, user2) = match (ctx.param("user1"), ctx.param("user2")) {
(Some(u1), Some(u2)) => (u1.replace("%20", " "), u2.replace("%20", " ")),
_ => {
return Response::from_html(
HeadToHeadTemplate {
users,
users: usernames,
..Default::default()
}
.render()
Expand All @@ -185,35 +182,30 @@ async fn handle_h2h<T>(ctx: &RouteContext<T>, client: &Postgrest) -> Result<Resp
}
};

let Ok(mut user1_data) = fetch_user_data(&user1, client).await else {
return Response::error("Couldn't fetch user1 data from database", 500);
};
let Ok(mut user2_data) = fetch_user_data(&user2, client).await else {
return Response::error("Couldn't fetch user2 data from database", 500);
};
let mut user1_data = fetch_user_data(&user1, client)
.await
.map_err(|e| format!("Couldn't fetch user1 data from database: {}", e))?;

let mut user2_data = fetch_user_data(&user2, client)
.await
.map_err(|e| format!("Couldn't fetch user2 data from database: {}", e))?;

let box_plot_html =
generate_box_plot_html(vec![&mut user1_data.all_times, &mut user2_data.all_times])
.map_or_else(
|_| String::from("Need more times before we can generate box plot!"),
|box_plot_html| box_plot_html,
);
.unwrap_or_else(|_| String::from("Need more times before we can generate box plot!"));

let scatter_plot_html =
generate_scatter_plot_html(vec![&mut user1_data.all_times, &mut user2_data.all_times])
.map_or_else(
|_| String::from("Need more times before we can generate scatter plot!"),
|scatter_plot_html| scatter_plot_html,
);
.unwrap_or_else(|_| {
String::from("Need more times before we can generate scatter plot!")
});

let data: Option<HeadToHeadData> = fetch_h2h_data(user1.clone(), user2.clone(), client)
.await
.ok();
let h2h_data = fetch_h2h_data(user1, user2, client).await.ok();

Response::from_html(
HeadToHeadTemplate {
users,
data,
users: usernames,
data: h2h_data,
box_plot_html,
scatter_plot_html,
}
Expand Down
Loading

0 comments on commit 2bd4d6b

Please sign in to comment.