diff --git a/migrations/20250502200733_status_update_history.sql b/migrations/20250502200733_status_update_history.sql new file mode 100644 index 0000000..4d9228b --- /dev/null +++ b/migrations/20250502200733_status_update_history.sql @@ -0,0 +1,14 @@ +CREATE TABLE StatusUpdateHistory ( + update_id SERIAL PRIMARY KEY, + member_id INT REFERENCES Member(member_id) ON DELETE CASCADE, + date DATE NOT NULL, + is_updated BOOLEAN NOT NULL DEFAULT FALSE, + created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, + UNIQUE (member_id, date) +); + +CREATE TRIGGER set_updated_at +BEFORE UPDATE ON StatusUpdateHistory +FOR EACH ROW +EXECUTE FUNCTION update_timestamp(); diff --git a/src/daily_task/mod.rs b/src/daily_task/mod.rs index d342f87..033e295 100644 --- a/src/daily_task/mod.rs +++ b/src/daily_task/mod.rs @@ -44,13 +44,16 @@ async fn execute_daily_task(pool: Arc) { .await; match members { - Ok(members) => update_attendance(members, &pool).await, + Ok(members) => { + update_attendance(&members, &pool).await; + update_status_history(&members, &pool).await; + } // TODO: Handle this Err(e) => error!("Failed to fetch members: {:?}", e), }; } -async fn update_attendance(members: Vec, pool: &PgPool) { +async fn update_attendance(members: &Vec, pool: &PgPool) { #[allow(deprecated)] let today = chrono::Utc::now() .with_timezone(&Kolkata) @@ -200,3 +203,40 @@ async fn update_days_attended(member_id: i32, today: NaiveDate, pool: &PgPool) { } } } + +async fn update_status_history(members: &Vec, pool: &PgPool) { + #[allow(deprecated)] + let today = chrono::Utc::now() + .with_timezone(&Kolkata) + .date() + .naive_local(); + debug!("Updating Status Update History on {}", today); + + for member in members { + let attendance = sqlx::query( + "INSERT INTO StatusUpdateHistory (member_id, date, is_updated) + VALUES ($1, $2, $3) + ON CONFLICT (member_id, date) DO NOTHING", + ) + .bind(member.member_id) + .bind(today) + .bind(false) + .execute(pool) + .await; + + match attendance { + Ok(_) => { + debug!( + "Status update record added for member ID: {}", + member.member_id + ); + } + Err(e) => { + error!( + "Failed to insert status update history for member ID: {}: {:?}", + member.member_id, e + ); + } + } + } +} diff --git a/src/graphql/mutations/streak_mutations.rs b/src/graphql/mutations/streak_mutations.rs index 5f69627..85e005c 100644 --- a/src/graphql/mutations/streak_mutations.rs +++ b/src/graphql/mutations/streak_mutations.rs @@ -4,6 +4,7 @@ use async_graphql::{Context, Object, Result}; use sqlx::PgPool; use crate::models::status_update_streak::{StatusUpdateStreak as Streak, StreakInput}; +use chrono_tz::Asia::Kolkata; #[derive(Default)] pub struct StreakMutations; @@ -30,6 +31,8 @@ impl StreakMutations { let updated_streak = query.fetch_one(pool.as_ref()).await?; + update_status_history(pool.as_ref(), input.member_id).await?; + Ok(updated_streak) } @@ -53,3 +56,27 @@ impl StreakMutations { Ok(updated_streak) } } + +async fn update_status_history(pool: &PgPool, member_id: i32) -> Result<()> { + #[allow(deprecated)] + let yesterday = chrono::Utc::now() + .with_timezone(&Kolkata) + .date() + .naive_local() + - chrono::Duration::days(1); + + sqlx::query( + " + UPDATE StatusUpdateHistory + SET is_updated = TRUE + WHERE member_id = $1 + AND date = $2 + ", + ) + .bind(member_id) + .bind(yesterday) + .execute(pool) + .await?; + + Ok(()) +} diff --git a/src/graphql/queries/streak_queries.rs b/src/graphql/queries/streak_queries.rs index 35123de..c3e9c90 100644 --- a/src/graphql/queries/streak_queries.rs +++ b/src/graphql/queries/streak_queries.rs @@ -1,5 +1,6 @@ use std::sync::Arc; +use crate::models::status_update_streak::StatusUpdateHistory; use crate::models::status_update_streak::StatusUpdateStreak as Streak; use async_graphql::{Context, Object, Result}; use sqlx::PgPool; @@ -29,4 +30,29 @@ impl StreakQueries { .await?, ) } + + async fn status_update_history_by_member_id( + &self, + ctx: &Context<'_>, + member_id: i32, + ) -> Result { + let pool = ctx.data::>().expect("Pool must be in context."); + + Ok(sqlx::query_as::<_, StatusUpdateHistory>( + "SELECT * FROM StatusUpdateHistory WHERE member_id = $1", + ) + .bind(member_id) + .fetch_one(pool.as_ref()) + .await?) + } + + async fn status_update_history(&self, ctx: &Context<'_>) -> Result> { + let pool = ctx.data::>().expect("Pool must be in context."); + + Ok( + sqlx::query_as::<_, StatusUpdateHistory>("SELECT * FROM StatusUpdateHistory") + .fetch_all(pool.as_ref()) + .await?, + ) + } } diff --git a/src/models/status_update_streak.rs b/src/models/status_update_streak.rs index c8e98e8..b944251 100644 --- a/src/models/status_update_streak.rs +++ b/src/models/status_update_streak.rs @@ -1,4 +1,5 @@ use async_graphql::{InputObject, SimpleObject}; +use chrono::NaiveDate; use sqlx::FromRow; #[derive(SimpleObject, FromRow)] @@ -18,3 +19,11 @@ pub struct StatusUpdateStreakInfo { pub struct StreakInput { pub member_id: i32, } + +#[derive(SimpleObject, FromRow)] +pub struct StatusUpdateHistory { + pub update_id: i32, + pub member_id: i32, + pub is_updated: bool, + pub date: NaiveDate, +}