diff --git a/migrations/20241126123155_adding_streaks.sql b/migrations/20241126123155_adding_streaks.sql new file mode 100644 index 0000000..11f8e60 --- /dev/null +++ b/migrations/20241126123155_adding_streaks.sql @@ -0,0 +1,2 @@ +ALTER TABLE Member ADD COLUMN streak INT; +ALTER TABLE Member ADD COLUMN max_streak INT; \ No newline at end of file diff --git a/migrations/20241126162522_add_member_group.sql b/migrations/20241126162522_add_member_group.sql new file mode 100644 index 0000000..f95c6e2 --- /dev/null +++ b/migrations/20241126162522_add_member_group.sql @@ -0,0 +1 @@ +ALTER TABLE Member ADD COLUMN group_id INT; \ No newline at end of file diff --git a/migrations/20241128224140_add_streak_relation.sql b/migrations/20241128224140_add_streak_relation.sql new file mode 100644 index 0000000..e42db4c --- /dev/null +++ b/migrations/20241128224140_add_streak_relation.sql @@ -0,0 +1,6 @@ +CREATE TABLE Streaks ( + id SERIAL PRIMARY KEY, + streak INT NOT NULL DEFAULT 0, + max_streak INT NOT NULL DEFAULT 0, + FOREIGN KEY (id) REFERENCES Member(id) ON DELETE CASCADE +); \ No newline at end of file diff --git a/migrations/20241129193435_remove_streks_from_membertable.sql b/migrations/20241129193435_remove_streks_from_membertable.sql new file mode 100644 index 0000000..a147905 --- /dev/null +++ b/migrations/20241129193435_remove_streks_from_membertable.sql @@ -0,0 +1,3 @@ +ALTER TABLE Member +DROP COLUMN streak, +DROP COLUMN max_streak; \ No newline at end of file diff --git a/migrations/20241129195135_add_streaks_table.sql b/migrations/20241129195135_add_streaks_table.sql new file mode 100644 index 0000000..36fe370 --- /dev/null +++ b/migrations/20241129195135_add_streaks_table.sql @@ -0,0 +1,6 @@ +CREATE TABLE StreakUpdate ( + id SERIAL PRIMARY KEY, + streak INT NOT NULL DEFAULT 0, + max_streak INT NOT NULL DEFAULT 0, + FOREIGN KEY (id) REFERENCES Member(id) ON DELETE CASCADE +); \ No newline at end of file diff --git a/migrations/20241129195234_drop_old_streak_table.sql b/migrations/20241129195234_drop_old_streak_table.sql new file mode 100644 index 0000000..18f205d --- /dev/null +++ b/migrations/20241129195234_drop_old_streak_table.sql @@ -0,0 +1 @@ +DROP TABLE Streaks; \ No newline at end of file diff --git a/migrations/20241129200327_drop_strek_update.sql b/migrations/20241129200327_drop_strek_update.sql new file mode 100644 index 0000000..4193851 --- /dev/null +++ b/migrations/20241129200327_drop_strek_update.sql @@ -0,0 +1 @@ +DROP TABLE IF EXISTS StreakUpdate; \ No newline at end of file diff --git a/migrations/20241129200519_recreate_streaks.sql b/migrations/20241129200519_recreate_streaks.sql new file mode 100644 index 0000000..36fe370 --- /dev/null +++ b/migrations/20241129200519_recreate_streaks.sql @@ -0,0 +1,6 @@ +CREATE TABLE StreakUpdate ( + id SERIAL PRIMARY KEY, + streak INT NOT NULL DEFAULT 0, + max_streak INT NOT NULL DEFAULT 0, + FOREIGN KEY (id) REFERENCES Member(id) ON DELETE CASCADE +); \ No newline at end of file diff --git a/src/db/member.rs b/src/db/member.rs index 4bdce7d..3306336 100644 --- a/src/db/member.rs +++ b/src/db/member.rs @@ -15,4 +15,12 @@ pub struct Member { pub year: i32, pub macaddress: String, pub discord_id: Option, + pub group_id: Option, } + +#[derive(FromRow, SimpleObject)] +pub struct StreakUpdate { + pub id: i32, + pub streak: Option, + pub max_streak: Option, +} \ No newline at end of file diff --git a/src/graphql/mutations.rs b/src/graphql/mutations.rs index 5a1848e..bb39536 100644 --- a/src/graphql/mutations.rs +++ b/src/graphql/mutations.rs @@ -11,7 +11,7 @@ use sha2::Sha256; type HmacSha256 = Hmac; -use crate::db::{member::Member, attendance::Attendance}; +use crate::db::{member::Member, attendance::Attendance, member::StreakUpdate}; pub struct MutationRoot; @@ -30,6 +30,7 @@ impl MutationRoot { year: i32, macaddress: String, discord_id: String, + group_id: i32, ) -> Result { let pool = ctx.data::>().expect("Pool not found in context"); @@ -37,7 +38,7 @@ impl MutationRoot { let member = sqlx::query_as::<_, Member>( - "INSERT INTO Member (rollno, name, hostel, email, sex, year, macaddress, discord_id) VALUES ($1, $2, $3, $4, $5, $6, $7, $8) RETURNING *" + "INSERT INTO Member (rollno, name, hostel, email, sex, year, macaddress, discord_id, group_id) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9) RETURNING *" ) .bind(rollno) .bind(name) @@ -47,6 +48,7 @@ impl MutationRoot { .bind(year) .bind(macaddress) .bind(discord_id) + .bind(group_id) .fetch_one(pool.as_ref()) .await?; @@ -61,6 +63,7 @@ impl MutationRoot { year: i32, macaddress: String, discord_id: String, + group_id: i32, hmac_signature: String, ) -> Result { let pool = ctx.data::>().expect("Pool not found in context"); @@ -69,7 +72,7 @@ impl MutationRoot { let mut mac = HmacSha256::new_from_slice(secret_key.as_bytes()).expect("HMAC can take key of any size"); - let message = format!("{}{}{}{}{}", id, hostel, year, macaddress, discord_id); + let message = format!("{}{}{}{}{}{}", id, hostel, year, macaddress, discord_id, group_id); mac.update(message.as_bytes()); let expected_signature = mac.finalize().into_bytes(); @@ -91,8 +94,9 @@ impl MutationRoot { hostel = CASE WHEN $1 = '' THEN hostel ELSE $1 END, year = CASE WHEN $2 = 0 THEN year ELSE $2 END, macaddress = CASE WHEN $3 = '' THEN macaddress ELSE $3 END, - discord_id = CASE WHEN $4 = '' THEN discord_id ELSE $4 END - WHERE id = $5 + discord_id = CASE WHEN $4 = '' THEN discord_id ELSE $4 END, + group_id = CASE WHEN $5 = 0 THEN group_id ELSE $5 END + WHERE id = $6 RETURNING * " ) @@ -101,6 +105,7 @@ impl MutationRoot { .bind(year) .bind(macaddress) .bind(discord_id) + .bind(group_id) .bind(id) .fetch_one(pool.as_ref()) .await?; @@ -158,7 +163,6 @@ impl MutationRoot { mac.update(message.as_bytes()); let expected_signature = mac.finalize().into_bytes(); - // Convert the received HMAC signature from the client to bytes for comparison let received_signature = hex::decode(hmac_signature) @@ -192,4 +196,68 @@ impl MutationRoot { Ok(attendance) } -} + async fn update_streak( + &self, + ctx: &Context<'_>, + id: i32, + has_sent_update: bool, + ) -> Result { + let pool = ctx.data::>().expect("Pool not found in context"); + + let streak_info = sqlx::query_as::<_, StreakUpdate>( + " + SELECT id, streak, max_streak + FROM StreakUpdate + WHERE id = $1 + " + ) + .bind(id) + .fetch_optional(pool.as_ref()) + .await?; + + match streak_info{ + Some(mut member) => { + let current_streak = member.streak.unwrap_or(0); + let max_streak = member.max_streak.unwrap_or(0); + let (new_streak, new_max_streak) = if has_sent_update { + let updated_streak = current_streak + 1; + let updated_max_streak = updated_streak.max(max_streak); + (updated_streak, updated_max_streak) + } else { + (0, max_streak) + }; + let updated_member = sqlx::query_as::<_, StreakUpdate>( + " + UPDATE StreakUpdate + SET streak = $1, max_streak = $2 + WHERE id = $3 + RETURNING * + " + ) + .bind(new_streak) + .bind(new_max_streak) + .bind(id) + .fetch_one(pool.as_ref()) + .await?; + + Ok(updated_member) + }, + None => { + let new_member = sqlx::query_as::<_, StreakUpdate>( + " + INSERT INTO StreakUpdate (id, streak, max_streak) + VALUES ($1, $2, $3) + RETURNING * + " + ) + .bind(id) + .bind(0) + .bind(0) + .fetch_one(pool.as_ref()) + .await?; + + Ok(new_member) + } + } + } +} \ No newline at end of file diff --git a/src/graphql/query.rs b/src/graphql/query.rs index 07120e9..cc84fe8 100644 --- a/src/graphql/query.rs +++ b/src/graphql/query.rs @@ -4,7 +4,7 @@ use std::sync::Arc; use chrono::NaiveDate; -use crate::db::{member::Member, attendance::Attendance}; +use crate::db::{member::Member, attendance::Attendance, member::StreakUpdate}; pub struct QueryRoot; @@ -36,4 +36,17 @@ impl QueryRoot { .await?; Ok(attendance_list) } + async fn get_streak( + &self, + ctx: &Context<'_>, + id: i32, + ) -> Result { + let pool = ctx.data::>().expect("Pool not found in context"); + let streak = sqlx::query_as::<_, StreakUpdate>("SELECT * FROM StreakUpdate WHERE id = $1") + .bind(id) + .fetch_one(pool.as_ref()) + .await?; + + Ok(streak) + } }