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/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/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..d001672 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,8 @@ impl MutationRoot { Ok(member) } + + //Mutation for adding attendance to the Attendance table async fn add_attendance( &self, ctx: &Context<'_>, @@ -59,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 5944c0c..d8ebfb7 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) } } diff --git a/src/main.rs b/src/main.rs index 244f23a..c9fa020 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, 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,72 @@ 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(async move { + + schedule_task_at_midnight(pool.clone()).await; // Call the function after 10 seconds + }); + 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( + "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) + .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), + } + } + }, + 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 + 60); + + sleep_until(Instant::now() + sleep_duration).await; + scheduled_task(pool.clone()).await; + print!("done"); + } +} \ No newline at end of file