Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Query to fetch attendance summary #87

Open
wants to merge 2 commits into
base: develop
Choose a base branch
from
Open
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
48 changes: 48 additions & 0 deletions docs/attendance.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,35 @@ struct AttendanceSummary {
}
```

### Daily Count
Total Lab count for each date
```rust
pub struct DailyCount {
pub date: String,
pub count: i64,
}
```

### Member Summary
Total lab members attended in the past 6 months
```rust
pub struct MemberAttendanceSummary {
pub id: i32,
pub name: String,
pub present_days: i64,
}
```

### Attendance Report
Attendance report of the club members
```rust
pub struct AttendanceReport {
pub daily_count: Vec<DailyCount>,
pub member_attendance: Vec<MemberAttendanceSummary>,
pub max_days: i64,
}
```

## Queries

### Get Attendance
Expand Down Expand Up @@ -64,6 +93,25 @@ query {
}
```

### Get Attendance Report
Get Attendance report containing lab count and members attendance report of the past 6 months.
`maxDays returns the count of days when lab was open in the past 6 months`
```graphql
query{
getAttendanceSummary(startDate:"2024-12-20", endDate: "2024-12-27"){
memberAttendance{
name,
presentDays
}
dailyCount{
date,
count
}
maxDays
}
}
```

### Mark Attendance
Record a member's attendance for the day.

Expand Down
86 changes: 85 additions & 1 deletion src/graphql/queries/attendance_queries.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use std::sync::Arc;

use crate::models::attendance::{Attendance, AttendanceWithMember};
use crate::models::attendance::{
Attendance, AttendanceReport, AttendanceWithMember, DailyCount, MemberAttendanceSummary,
};
use async_graphql::{Context, Object, Result};
use chrono::NaiveDate;
use sqlx::PgPool;
Expand All @@ -21,6 +23,88 @@ impl AttendanceQueries {
)
}

async fn get_attendance_summary(
&self,
ctx: &Context<'_>,
start_date: String,
end_date: String,
) -> Result<AttendanceReport> {
let pool = ctx.data::<Arc<PgPool>>().expect("Pool must be in context.");

let start = NaiveDate::parse_from_str(&start_date, "%Y-%m-%d")
.map_err(|_| async_graphql::Error::new("Invalid start_date format. Use YYYY-MM-DD"))?;
let end = NaiveDate::parse_from_str(&end_date, "%Y-%m-%d")
.map_err(|_| async_graphql::Error::new("Invalid end_date format. Use YYYY-MM-DD"))?;
if start > end {
return Err(async_graphql::Error::new(
"startDate cannot be greater than endDate.",
));
}

let daily_count_result = sqlx::query!(
r#"
SELECT
attendace.date,
COUNT(CASE WHEN attendace.is_present = true THEN attendace.member_id END) as total_present
FROM Attendance attendace
WHERE attendace.date BETWEEN $1 AND $2
GROUP BY attendace.date
ORDER BY attendace.date
"#,
start,
end
)
.fetch_all(pool.as_ref())
.await;

let daily_count_rows = daily_count_result?;

let daily_count = daily_count_rows
.into_iter()
.map(|row| DailyCount {
date: row.date.to_string(),
count: row.total_present.unwrap_or(0),
})
.collect();

let member_attendance_query = sqlx::query!(
r#"
SELECT member.member_id as "id!", member.name as "name!",
COUNT(attendance.is_present)::int as "present_days!"
FROM Member member
LEFT JOIN Attendance attendance
ON member.member_id = attendance.member_id
AND attendance.is_present AND attendance.date >= CURRENT_DATE - INTERVAL '6 months'
GROUP BY member.member_id, member.name
ORDER BY member.member_id
"#
)
.fetch_all(pool.as_ref())
.await;

let member_attendance = member_attendance_query?
.into_iter()
.map(|row| MemberAttendanceSummary {
id: row.id,
name: row.name,
present_days: row.present_days as i64,
})
.collect();

let max_days = sqlx::query_scalar::<_, i64>(
"SELECT COUNT(DISTINCT date) FROM Attendance
WHERE date >= CURRENT_DATE - INTERVAL '6 months' AND is_present",
)
.fetch_one(pool.as_ref())
.await?;

Ok(AttendanceReport {
daily_count,
member_attendance,
max_days,
})
}

async fn attendance_by_date(
&self,
ctx: &Context<'_>,
Expand Down
23 changes: 23 additions & 0 deletions src/models/attendance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,3 +57,26 @@ pub struct AttendanceWithMember {
pub name: String,
pub year: i32,
}

// This struct is used to get the Lab count of a date
#[derive(SimpleObject)]
pub struct DailyCount {
pub date: String,
pub count: i64,
}

// This struct is used to fetch the attended lab of each member
#[derive(SimpleObject)]
pub struct MemberAttendanceSummary {
pub id: i32,
pub name: String,
pub present_days: i64,
}

// This struct is used for getting the combined Attendance report
#[derive(SimpleObject)]
pub struct AttendanceReport {
pub daily_count: Vec<DailyCount>,
pub member_attendance: Vec<MemberAttendanceSummary>,
pub max_days: i64,
}
Loading