diff --git a/api/source/api_modules/classroom_compliance.d b/api/source/api_modules/classroom_compliance.d index b925ed1..df85f43 100644 --- a/api/source/api_modules/classroom_compliance.d +++ b/api/source/api_modules/classroom_compliance.d @@ -65,6 +65,7 @@ void registerApiEndpoints(PathHandler handler) { handler.addMapping(Method.PUT, STUDENT_PATH, &updateStudent); handler.addMapping(Method.DELETE, STUDENT_PATH, &deleteStudent); handler.addMapping(Method.GET, STUDENT_PATH ~ "/entries", &getStudentEntries); + handler.addMapping(Method.GET, STUDENT_PATH ~ "/overview", &getStudentOverview); handler.addMapping(Method.GET, CLASS_PATH ~ "/entries", &getEntries); handler.addMapping(Method.POST, CLASS_PATH ~ "/entries", &saveEntries); @@ -783,3 +784,63 @@ void getStudentEntries(ref HttpRequestContext ctx) { } ctx.response.writeBodyString(response.toJSON(), "application/json"); } + +void getStudentOverview(ref HttpRequestContext ctx) { + auto db = getDb(); + User user = getUserOrThrow(ctx, db); + auto student = getStudentOrThrow(ctx, db, user); + + const ulong entryCount = findOne!ulong( + db, + "SELECT COUNT(*) FROM classroom_compliance_entry WHERE student_id = ?", + student.id + ).orElse(0); + if (entryCount == 0) { + ctx.response.status = HttpStatus.NOT_FOUND; + ctx.response.writeBodyString("No entries for this student."); + return; + } + const ulong absenceCount = findOne!ulong( + db, + "SELECT COUNT(*) FROM classroom_compliance_entry WHERE student_id = ? AND absent = true", + student.id + ).orElse(0); + const ulong phoneNoncomplianceCount = findOne!ulong( + db, + " + SELECT COUNT(*) + FROM classroom_compliance_entry_phone p + LEFT JOIN classroom_compliance_entry e + ON e.id = p.entry_id + WHERE p.compliant = false AND e.student_id = ? + ", + student.id + ).orElse(0); + const behaviorCountQuery = " + SELECT COUNT(*) + FROM classroom_compliance_entry_behavior b + LEFT JOIN classroom_compliance_entry e + ON e.id = b.entry_id + WHERE e.student_id = ? AND b.rating = ? + "; + + const ulong behaviorGoodCount = findOne!ulong(db, behaviorCountQuery, student.id, 3).orElse(0); + const ulong behaviorMediocreCount = findOne!ulong(db, behaviorCountQuery, student.id, 2).orElse(0); + const ulong behaviorPoorCount = findOne!ulong(db, behaviorCountQuery, student.id, 1).orElse(0); + + // Calculate derived statistics. + const ulong attendanceCount = entryCount - absenceCount; + double attendanceRate = attendanceCount / cast(double) entryCount; + double phoneComplianceRate = (attendanceCount - phoneNoncomplianceCount) / cast(double) attendanceCount; + double behaviorScore = ( + behaviorGoodCount * 1.0 + + behaviorMediocreCount * 0.5 + ) / attendanceCount; + + JSONValue response = JSONValue.emptyObject; + response.object["attendanceRate"] = JSONValue(attendanceRate); + response.object["phoneComplianceRate"] = JSONValue(phoneComplianceRate); + response.object["behaviorScore"] = JSONValue(behaviorScore); + response.object["entryCount"] = JSONValue(entryCount); + ctx.response.writeBodyString(response.toJSON(), "application/json"); +} diff --git a/app/src/api/classroom_compliance.ts b/app/src/api/classroom_compliance.ts index e41ab8a..c151d18 100644 --- a/app/src/api/classroom_compliance.ts +++ b/app/src/api/classroom_compliance.ts @@ -92,6 +92,13 @@ export interface ScoresResponse { scores: StudentScore[] } +export interface StudentStatisticsOverview { + attendanceRate: number + phoneComplianceRate: number + behaviorScore: number + entryCount: number +} + export class ClassroomComplianceAPIClient extends APIClient { constructor(authStore: AuthStoreType) { super(BASE_URL, authStore) @@ -162,4 +169,11 @@ export class ClassroomComplianceAPIClient extends APIClient { getStudentEntries(classId: number, studentId: number): APIResponse { return super.get(`/classes/${classId}/students/${studentId}/entries`) } + + getStudentStatisticsOverview( + classId: number, + studentId: number, + ): APIResponse { + return super.get(`/classes/${classId}/students/${studentId}/overview`) + } } diff --git a/app/src/apps/classroom_compliance/StudentView.vue b/app/src/apps/classroom_compliance/StudentView.vue index 8080d28..d473498 100644 --- a/app/src/apps/classroom_compliance/StudentView.vue +++ b/app/src/apps/classroom_compliance/StudentView.vue @@ -1,5 +1,5 @@