Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 0 additions & 24 deletions docs/attendance.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,6 @@ struct Attendance {
```
The final two fields are not exposed in the interface for obvious reasons.

### AttendanceSummary
Monthly attendance summary for each member.
```rust
struct AttendanceSummary {
member_id: i32,
year: i32,
month: i32,
days_attended: i32,
}
```

## Queries

### Get Attendance
Expand Down Expand Up @@ -85,19 +74,6 @@ mutation {
}
```

### Get Attendance Summary
Get monthly attendance summary for a member.

```graphql
query {
attendanceSummary(memberId: 1) {
year
month
daysAttended
}
}
```

## Daily Task

The `src/daily_task/daily_task.rs` system automatically updates attendance summaries at midnight.
3 changes: 3 additions & 0 deletions migrations/20250929191135_drop_unnecessary_tables.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
-- Add migration script here
DROP TABLE statusupdatestreak;
DROP TABLE project;
3 changes: 3 additions & 0 deletions migrations/20251006152634_rename_status_is_updated.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
-- Add migration script here
ALTER table statusupdatehistory
RENAME COLUMN is_updated TO is_sent;
4 changes: 1 addition & 3 deletions src/daily_task/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ pub async fn run_daily_task_at_midnight(pool: Arc<PgPool>) {

/// This function does a number of things, including:
/// * Insert new attendance records everyday for [`presense`](https://www.github.com/amfoss/presense) to update them later in the day.
/// * Update the AttendanceSummary table
async fn execute_daily_task(pool: Arc<PgPool>) {
// Members is queried outside of each function to avoid repetition
let members = sqlx::query_as::<_, Member>("SELECT * FROM Member")
Expand Down Expand Up @@ -90,7 +89,6 @@ async fn update_attendance(members: &Vec<Member>, pool: &PgPool) {
}
}
// This could have been called in `execute_daily_task()` but that would require us to loop through members twice.
// Whether or not inserting attendance failed, Root will attempt to update AttendanceSummary. This can potentially fail too since insertion failed earlier. However, these two do not depend on each other and one of them failing is no reason to avoid trying the other.
}
}

Expand All @@ -104,7 +102,7 @@ async fn update_status_history(members: &Vec<Member>, pool: &PgPool) {

for member in members {
let status_update = sqlx::query(
"INSERT INTO StatusUpdateHistory (member_id, date, is_updated)
"INSERT INTO StatusUpdateHistory (member_id, date, is_sent)
VALUES ($1, $2, $3)
ON CONFLICT (member_id, date) DO NOTHING",
)
Expand Down
53 changes: 1 addition & 52 deletions src/database_seeder/seed.sql
Original file line number Diff line number Diff line change
Expand Up @@ -58,60 +58,9 @@ WHERE (random() < 0.75)
ON CONFLICT (member_id, date) DO NOTHING;


-- AttendanceSummary
INSERT INTO AttendanceSummary (
member_id, year, month, days_attended
)
SELECT
m.member_id,
2025,
(i % 12) + 1,
FLOOR(random() * 26 + 3)::INT
FROM generate_series(1, 400) AS i
JOIN (
SELECT generate_series(1, 60) AS idx, member_id
FROM member
) AS m ON (i % 60) + 1 = m.idx
ON CONFLICT (member_id, year, month) DO NOTHING;


-- StatusUpdateStreak
INSERT INTO StatusUpdateStreak (
member_id, current_streak, max_streak
)
SELECT
member_id,
FLOOR(random() * 10 + 1)::INT,
FLOOR(random() * 30 + 10)::INT
FROM member
ON CONFLICT (member_id) DO NOTHING;


-- Project
INSERT INTO Project (
member_id, title
)
SELECT
(i % 60) + 1,
CASE
WHEN i % 3 = 0 THEN 'Machine Learning Project ' || i
WHEN i % 3 = 1 THEN 'Web Development Project ' || i
ELSE 'Data Analysis Project ' || i
END
FROM generate_series(1, 200) AS i
WHERE NOT EXISTS (
SELECT 1 FROM Project
WHERE member_id = (i % 60) + 1 AND title = CASE
WHEN i % 3 = 0 THEN 'Machine Learning Project ' || i
WHEN i % 3 = 1 THEN 'Web Development Project ' || i
ELSE 'Data Analysis Project ' || i
END
);


-- StatusUpdateHistory
INSERT INTO StatusUpdateHistory (
member_id, date, is_updated
member_id, date, is_sent
)
SELECT
m.member_id,
Expand Down
18 changes: 4 additions & 14 deletions src/graphql/mod.rs
Original file line number Diff line number Diff line change
@@ -1,22 +1,12 @@
use async_graphql::MergedObject;
use mutations::{AttendanceMutations, MemberMutations, ProjectMutations, StreakMutations};
use queries::{AttendanceQueries, MemberQueries, ProjectQueries, StreakQueries};
use mutations::{AttendanceMutations, MemberMutations, StatusMutations};
use queries::MemberQueries;

pub mod mutations;
pub mod queries;

#[derive(MergedObject, Default)]
pub struct Query(
MemberQueries,
AttendanceQueries,
StreakQueries,
ProjectQueries,
);
pub struct Query(MemberQueries);

#[derive(MergedObject, Default)]
pub struct Mutation(
MemberMutations,
AttendanceMutations,
StreakMutations,
ProjectMutations,
);
pub struct Mutation(MemberMutations, AttendanceMutations, StatusMutations);
6 changes: 3 additions & 3 deletions src/graphql/mutations/attendance_mutations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use hmac::{Hmac, Mac};
use sha2::Sha256;
use sqlx::PgPool;

use crate::models::attendance::{Attendance, MarkAttendanceInput};
use crate::models::attendance::{AttendanceRecord, MarkAttendanceInput};

type HmacSha256 = Hmac<Sha256>;

Expand All @@ -21,7 +21,7 @@ impl AttendanceMutations {
&self,
ctx: &Context<'_>,
input: MarkAttendanceInput,
) -> Result<Attendance> {
) -> Result<AttendanceRecord> {
let pool = ctx
.data::<Arc<PgPool>>()
.expect("Pool not found in context");
Expand All @@ -43,7 +43,7 @@ impl AttendanceMutations {
}

let now = Local::now().with_timezone(&Kolkata).time();
let attendance = sqlx::query_as::<_, Attendance>(
let attendance = sqlx::query_as::<_, AttendanceRecord>(
"UPDATE Attendance SET time_in = CASE
WHEN time_in IS NULL THEN $1
ELSE time_in END,
Expand Down
6 changes: 2 additions & 4 deletions src/graphql/mutations/mod.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
pub mod attendance_mutations;
pub mod member_mutations;
pub mod project_mutations;
pub mod streak_mutations;
pub mod status_mutations;

pub use attendance_mutations::AttendanceMutations;
pub use member_mutations::MemberMutations;
pub use project_mutations::ProjectMutations;
pub use streak_mutations::StreakMutations;
pub use status_mutations::StatusMutations;
28 changes: 0 additions & 28 deletions src/graphql/mutations/project_mutations.rs

This file was deleted.

41 changes: 41 additions & 0 deletions src/graphql/mutations/status_mutations.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
use async_graphql::{Context, Object, Result};
use chrono_tz::Asia::Kolkata;
use sqlx::PgPool;
use std::sync::Arc;

use crate::models::status_update::StatusUpdateRecord;

#[derive(Default)]
pub struct StatusMutations;

#[Object]
impl StatusMutations {
async fn mark_status_update(
&self,
ctx: &Context<'_>,
emails: Vec<String>,
) -> Result<Vec<StatusUpdateRecord>> {
let pool = ctx.data::<Arc<PgPool>>().expect("Pool must be in context");
#[allow(deprecated)]
let yesterday = chrono::Utc::now()
.with_timezone(&Kolkata)
.date()
.naive_local()
- chrono::Duration::days(1);

let status = sqlx::query_as::<_, StatusUpdateRecord>(
"UPDATE StatusUpdateHistory SET
is_sent = true
WHERE member_id IN (SELECT member_id from Member where email = ANY($1))
AND date = $2
RETURNING *
",
)
.bind(emails)
.bind(yesterday)
.fetch_all(pool.as_ref())
.await?;

Ok(status)
}
}
82 changes: 0 additions & 82 deletions src/graphql/mutations/streak_mutations.rs

This file was deleted.

Loading