From c4379835aad0c6944b5b15ce3263c92ebcfa4329 Mon Sep 17 00:00:00 2001 From: Wreck-X Date: Tue, 2 Jul 2024 18:43:45 +0530 Subject: [PATCH 1/4] Add: Implements a scheduled task that migrates all the members to attendance table --- src/main.rs | 70 ++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 67 insertions(+), 3 deletions(-) diff --git a/src/main.rs b/src/main.rs index 244f23a..39aa530 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,13 @@ -use std::{env, sync::Arc}; +use std::{env, sync::Arc}; +use tokio::task; +use tokio::time::{sleep_until, Instant}; +use std::time::Duration; use async_graphql_axum::GraphQL; use axum::{routing::get, Router}; +use chrono::{ Local, NaiveTime}; +use db::attendance::Attendance; +use db::member::Member; use sqlx::PgPool; use async_graphql::{ Schema, EmptySubscription}; @@ -18,7 +24,7 @@ struct MyState { pool: Arc, } -//This is the main method +//Main method #[shuttle_runtime::main] async fn main(#[shuttle_shared_db::Postgres] pool: PgPool) -> shuttle_axum::ShuttleAxum { env::set_var("PGOPTIONS", "-c ignore_version=true"); @@ -30,10 +36,68 @@ async fn main(#[shuttle_shared_db::Postgres] pool: PgPool) -> shuttle_axum::Shut .data(pool.clone()) .finish(); - let state = MyState { pool }; + let state = MyState { pool: pool.clone() }; + let router = Router::new() .route("/", get(graphiql).post_service(GraphQL::new(schema.clone()))) .with_state(state); + task::spawn(schedule_task_at_midnight(pool.clone())); Ok(router.into()) } + + + +//Scheduled task for moving all members to Attendance table at midnight. +async fn scheduled_task(pool: Arc) { + let members: Result, sqlx::Error> = sqlx::query_as::<_, Member>("SELECT * FROM Member") + .fetch_all(pool.as_ref()) + .await; + + match members { + Ok(members) => { + let today = Local::now().naive_local(); + + for member in members { + let timein = NaiveTime::from_hms_opt(0, 0, 0); + let timeout = NaiveTime::from_hms_opt(0, 0, 0); // Default time, can be modified as needed + + let attendance = sqlx::query_as::<_, Attendance>( + "INSERT INTO Attendance (id, date, timein, timeout, present) VALUES ($1, $2, $3, $4, $5) ON CONFLICT (id, date) DO NOTHING RETURNING *" + ) + .bind(member.id) + .bind(today) + .bind(timein) + .bind(timeout) + .bind(false) + .fetch_one(pool.as_ref()) + .await; + + match attendance { + Ok(_) => println!("Attendance record added for member ID: {}", member.id), + Err(e) => eprintln!("Failed to insert attendance for member ID: {}: {:?}", member.id, e), + } + } + }, + Err(e) => eprintln!("Failed to fetch members: {:?}", e), + } +} + +//Ticker for calling the scheduled task +async fn schedule_task_at_midnight(pool: Arc) { + loop { + let now = Local::now(); + + let tomorrow = now.date_naive().succ_opt().unwrap(); + let midnight = NaiveTime::from_hms_opt(0, 0, 0).unwrap(); + let next_midnight = tomorrow.and_time(midnight); + + let now_naive = now.naive_local(); + let duration_until_midnight = next_midnight.signed_duration_since(now_naive); + let sleep_duration = Duration::from_secs(duration_until_midnight.num_seconds() as u64); + + sleep_until(Instant::now() + sleep_duration).await; + + scheduled_task(pool.clone()).await; + } +} \ No newline at end of file From 509f018349b345665cd31ee216bf8e176df0f62d Mon Sep 17 00:00:00 2001 From: Wreck-X Date: Tue, 2 Jul 2024 18:44:41 +0530 Subject: [PATCH 2/4] Adding Documentation --- ...01161049_add_default_attendance_column.sql | 2 ++ src/db/member.rs | 1 + src/graphql/mutations.rs | 3 ++ src/graphql/query.rs | 32 ++++++++++--------- 4 files changed, 23 insertions(+), 15 deletions(-) create mode 100644 migrations/20240701161049_add_default_attendance_column.sql diff --git a/migrations/20240701161049_add_default_attendance_column.sql b/migrations/20240701161049_add_default_attendance_column.sql new file mode 100644 index 0000000..e12563e --- /dev/null +++ b/migrations/20240701161049_add_default_attendance_column.sql @@ -0,0 +1,2 @@ +ALTER TABLE Attendance +ADD COLUMN present BOOLEAN DEFAULT FALSE; diff --git a/src/db/member.rs b/src/db/member.rs index ea78a20..3d3b16b 100644 --- a/src/db/member.rs +++ b/src/db/member.rs @@ -4,6 +4,7 @@ use async_graphql::SimpleObject; //Struct for the Member table #[derive(FromRow, SimpleObject)] + pub struct Member { pub id: i32, pub rollno: String, diff --git a/src/graphql/mutations.rs b/src/graphql/mutations.rs index 186c671..337fdd1 100644 --- a/src/graphql/mutations.rs +++ b/src/graphql/mutations.rs @@ -10,6 +10,8 @@ pub struct MutationRoot; #[Object] impl MutationRoot { + + //Mutation for adding members to the Member table async fn add_member( &self, ctx: &Context<'_>, @@ -37,6 +39,7 @@ impl MutationRoot { Ok(member) } + //Mutation for adding Attendance to the Attendance table async fn add_attendance( &self, ctx: &Context<'_>, diff --git a/src/graphql/query.rs b/src/graphql/query.rs index 5944c0c..c249783 100644 --- a/src/graphql/query.rs +++ b/src/graphql/query.rs @@ -10,7 +10,9 @@ pub struct QueryRoot; #[Object] impl QueryRoot { - async fn get_users(&self, ctx: &Context<'_>) -> Result, sqlx::Error> { + + //Query for retrieving the Members + async fn get_member(&self, ctx: &Context<'_>) -> Result, sqlx::Error> { let pool = ctx.data::>().expect("Pool not found in context"); let users = sqlx::query_as::<_, Member>("SELECT * FROM Member") .fetch_all(pool.as_ref()) @@ -18,20 +20,20 @@ impl QueryRoot { Ok(users) } -async fn get_attendance( - &self, - ctx: &Context<'_>, - date: NaiveDate, -) -> Result, sqlx::Error> { - let pool = ctx.data::>().expect("Pool not found in context"); - - let attendance_list = sqlx::query_as::<_, Attendance>( - "SELECT id, date, timein, timeout FROM Attendance WHERE date = $1" - ) - .bind(date) - .fetch_all(pool.as_ref()) - .await?; + //Query for retrieving the Attendance based on date + async fn get_attendance( + &self, + ctx: &Context<'_>, + date: NaiveDate, + ) -> Result, sqlx::Error> { + let pool = ctx.data::>().expect("Pool not found in context"); - Ok(attendance_list) + let attendance_list = sqlx::query_as::<_, Attendance>( + "SELECT id, date, timein, timeout FROM Attendance WHERE date = $1" + ) + .bind(date) + .fetch_all(pool.as_ref()) + .await?; + Ok(attendance_list) } } From 9ecca65adefd84b1b461bba735b42eae8e757216 Mon Sep 17 00:00:00 2001 From: Wreck-X Date: Tue, 2 Jul 2024 19:00:56 +0530 Subject: [PATCH 3/4] Fix: Row not identified when adding members to the attendance table --- src/main.rs | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/src/main.rs b/src/main.rs index 39aa530..c9fa020 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,7 @@ use std::{env, sync::Arc}; use tokio::task; -use tokio::time::{sleep_until, Instant}; +use tokio::time::{sleep, sleep_until, Instant}; use std::time::Duration; use async_graphql_axum::GraphQL; use axum::{routing::get, Router}; @@ -41,8 +41,12 @@ async fn main(#[shuttle_shared_db::Postgres] pool: PgPool) -> shuttle_axum::Shut let router = Router::new() .route("/", get(graphiql).post_service(GraphQL::new(schema.clone()))) .with_state(state); + task::spawn(async move { + + schedule_task_at_midnight(pool.clone()).await; // Call the function after 10 seconds + }); + - task::spawn(schedule_task_at_midnight(pool.clone())); Ok(router.into()) } @@ -62,7 +66,7 @@ async fn scheduled_task(pool: Arc) { let timein = NaiveTime::from_hms_opt(0, 0, 0); let timeout = NaiveTime::from_hms_opt(0, 0, 0); // Default time, can be modified as needed - let attendance = sqlx::query_as::<_, Attendance>( + let attendance = sqlx::query( "INSERT INTO Attendance (id, date, timein, timeout, present) VALUES ($1, $2, $3, $4, $5) ON CONFLICT (id, date) DO NOTHING RETURNING *" ) .bind(member.id) @@ -70,9 +74,9 @@ async fn scheduled_task(pool: Arc) { .bind(timein) .bind(timeout) .bind(false) - .fetch_one(pool.as_ref()) + .execute(pool.as_ref()) .await; - + match attendance { Ok(_) => println!("Attendance record added for member ID: {}", member.id), Err(e) => eprintln!("Failed to insert attendance for member ID: {}: {:?}", member.id, e), @@ -94,10 +98,10 @@ async fn schedule_task_at_midnight(pool: Arc) { let now_naive = now.naive_local(); let duration_until_midnight = next_midnight.signed_duration_since(now_naive); - let sleep_duration = Duration::from_secs(duration_until_midnight.num_seconds() as u64); + let sleep_duration = Duration::from_secs(duration_until_midnight.num_seconds() as u64 + 60); sleep_until(Instant::now() + sleep_duration).await; - scheduled_task(pool.clone()).await; + print!("done"); } } \ No newline at end of file From eae3c9e08c2f2c5054c34353f891e8b6e8745cc4 Mon Sep 17 00:00:00 2001 From: Wreck-X Date: Tue, 2 Jul 2024 20:40:25 +0530 Subject: [PATCH 4/4] Add: Update mutation for attendance record --- migrations/20240711150547_add_mac.sql | 1 + ...0813125650_rename_present_to_ispresent.sql | 1 + ...813132052_rename_present_to_is_present.sql | 1 + src/db/attendance.rs | 1 + src/graphql/mutations.rs | 24 ++++++++++++++++++- src/graphql/query.rs | 4 ++-- 6 files changed, 29 insertions(+), 3 deletions(-) create mode 100644 migrations/20240711150547_add_mac.sql create mode 100644 migrations/20240813125650_rename_present_to_ispresent.sql create mode 100644 migrations/20240813132052_rename_present_to_is_present.sql diff --git a/migrations/20240711150547_add_mac.sql b/migrations/20240711150547_add_mac.sql new file mode 100644 index 0000000..3808373 --- /dev/null +++ b/migrations/20240711150547_add_mac.sql @@ -0,0 +1 @@ +ALTER TABLE Member ADD COLUMN macaddress TEXT; \ No newline at end of file diff --git a/migrations/20240813125650_rename_present_to_ispresent.sql b/migrations/20240813125650_rename_present_to_ispresent.sql new file mode 100644 index 0000000..79aab75 --- /dev/null +++ b/migrations/20240813125650_rename_present_to_ispresent.sql @@ -0,0 +1 @@ +ALTER TABLE Attendance RENAME COLUMN present TO ispresent; diff --git a/migrations/20240813132052_rename_present_to_is_present.sql b/migrations/20240813132052_rename_present_to_is_present.sql new file mode 100644 index 0000000..4c2936f --- /dev/null +++ b/migrations/20240813132052_rename_present_to_is_present.sql @@ -0,0 +1 @@ +ALTER TABLE Attendance RENAME COLUMN ispresent TO is_present; diff --git a/src/db/attendance.rs b/src/db/attendance.rs index 5f4b80d..5ba3a3f 100644 --- a/src/db/attendance.rs +++ b/src/db/attendance.rs @@ -9,4 +9,5 @@ pub struct Attendance { pub date: NaiveDate, pub timein: NaiveTime, pub timeout: NaiveTime, + pub is_present: bool, } diff --git a/src/graphql/mutations.rs b/src/graphql/mutations.rs index 337fdd1..d001672 100644 --- a/src/graphql/mutations.rs +++ b/src/graphql/mutations.rs @@ -39,7 +39,8 @@ impl MutationRoot { Ok(member) } - //Mutation for adding Attendance to the Attendance table + + //Mutation for adding attendance to the Attendance table async fn add_attendance( &self, ctx: &Context<'_>, @@ -62,4 +63,25 @@ impl MutationRoot { Ok(attendance) } + + async fn mark_attendance( + &self, + ctx: &Context<'_>, + id: i32, + date: NaiveDate, + is_present: bool, + ) -> Result { + let pool = ctx.data::>().expect("Pool not found in context"); + + let attendance = sqlx::query_as::<_, Attendance>( + "UPDATE Attendance SET is_present = $1 WHERE id = $2 AND date = $3 RETURNING *" + ) + .bind(is_present) + .bind(id) + .bind(date) + .fetch_one(pool.as_ref()) + .await?; + + Ok(attendance) + } } diff --git a/src/graphql/query.rs b/src/graphql/query.rs index c249783..d8ebfb7 100644 --- a/src/graphql/query.rs +++ b/src/graphql/query.rs @@ -11,7 +11,7 @@ pub struct QueryRoot; #[Object] impl QueryRoot { - //Query for retrieving the Members + //Query for retrieving the members async fn get_member(&self, ctx: &Context<'_>) -> Result, sqlx::Error> { let pool = ctx.data::>().expect("Pool not found in context"); let users = sqlx::query_as::<_, Member>("SELECT * FROM Member") @@ -20,7 +20,7 @@ impl QueryRoot { Ok(users) } - //Query for retrieving the Attendance based on date + //Query for retrieving the attendance based on date async fn get_attendance( &self, ctx: &Context<'_>,