using System.Data; using Dapper; using MySql.Data.MySqlClient; using WinStudentGoalTracker.Models; using WinStudentGoalTracker.Services; namespace WinStudentGoalTracker.DataAccess; public class StudentRepository { private IDbConnection Connection => new MySqlConnection(DatabaseManager.ConnectionString); public async Task> GetMyStudentsAsync(Guid userId, Guid programId, string role) { using var db = Connection; using var multi = await db.QueryMultipleAsync( "sp_Student_GetWithAssignments", new { p_id_program = programId.ToString(), p_id_user = userId.ToString() }, commandType: CommandType.StoredProcedure); var students = await multi.ReadAsync(); var assignments = await multi.ReadAsync(); var myStudents = students.Where(s => PermissionService.IsAllowed(role, EntityType.Student, PermissionAction.Read, assignments.Any(a => a.IdStudent == s.StudentId && a.IdUser == userId)) ); return myStudents; } public async Task GetByIdAsync(Guid idStudent) { using var db = Connection; return await db.QuerySingleOrDefaultAsync( "sp_Student_GetById", new { p_id_student = idStudent.ToString() }, commandType: CommandType.StoredProcedure); } public async Task InsertAsync(CreateStudentDto dto, Guid newStudentGuid, Guid programId, Guid userId) { using var db = Connection; await db.ExecuteAsync( "sp_Student_Insert", new { p_id_student = newStudentGuid.ToString(), p_id_program = programId, p_id_user = userId.ToString(), p_identifier = dto.Identifier, p_program_year = dto.ProgramYear, p_enrollment_date = dto.EnrollmentDate, p_next_iep_date = dto.NextIepDate }, commandType: CommandType.StoredProcedure); return await GetByIdAsync(newStudentGuid); } public async Task UpdateAsync(Guid idStudent, UpdateStudentDto dto) { using var db = Connection; var rowsAffected = await db.ExecuteScalarAsync( "sp_Student_Update", new { p_id_student = idStudent.ToString(), p_identifier = dto.Identifier, p_program_year = dto.ProgramYear, p_enrollment_date = dto.EnrollmentDate, p_next_iep_date = dto.NextIepDate }, commandType: CommandType.StoredProcedure); return rowsAffected > 0; } public async Task DeleteAsync(Guid idStudent) { using var db = Connection; var rowsAffected = await db.ExecuteScalarAsync( "sp_Student_Delete", new { p_id_student = idStudent.ToString() }, commandType: CommandType.StoredProcedure); return rowsAffected > 0; } public async Task AddProgressEventAsync(Guid userId, AddProgressEventDto dto) { using var db = Connection; var row = await db.QuerySingleOrDefaultAsync( "sp_ProgressEvent_Insert", new { p_id_progress_event = Guid.NewGuid().ToString(), p_id_goal = dto.GoalId.ToString(), p_id_user_created = userId.ToString(), p_content = dto.Content, p_is_sensitive = dto.IsSensitive ? 1 : 0 }, commandType: CommandType.StoredProcedure); return row is not null; } public async Task GetStudentIdForGoalAsync(Guid idGoal) { using var db = Connection; var row = await db.QuerySingleOrDefaultAsync( "sp_Goal_GetById", new { p_id_goal = idGoal.ToString() }, commandType: CommandType.StoredProcedure); return row?.StudentId; } public async Task> GetProgressEventsForGoalAsync(Guid idGoal) { using var db = Connection; var rows = await db.QueryAsync( "sp_ProgressEvent_GetByGoalId", new { p_id_goal = idGoal.ToString() }, commandType: CommandType.StoredProcedure); return rows.Select(r => new ProgressEventResponse { ProgressEventId = r.ProgressEventId, Content = r.Content, CreatedAt = r.CreatedAt, CreatedByName = r.CreatedByName }); } public async Task InsertGoalAsync(Guid idStudent, Guid userId, CreateGoalDto dto) { var newGoalId = Guid.NewGuid(); using var db = Connection; var row = await db.QuerySingleOrDefaultAsync( "sp_Goal_Insert", new { p_id_goal = newGoalId.ToString(), p_id_goal_parent = dto.GoalParentId?.ToString(), p_id_student = idStudent.ToString(), p_id_user_created = userId.ToString(), p_title = dto.Title, p_description = dto.Description, p_category = dto.Category }, commandType: CommandType.StoredProcedure); if (row is null) return null; return new StudentGoalItem { GoalId = newGoalId, GoalParentId = dto.GoalParentId, Title = dto.Title, Description = dto.Description, Category = dto.Category, ProgressEventCount = 0 }; } public async Task GetGoalSummaryAsync(Guid idStudent) { using var db = Connection; var rows = await db.QueryAsync( "sp_Goal_GetByStudentId", new { p_id_student = idStudent.ToString() }, commandType: CommandType.StoredProcedure); var list = rows.ToList(); if (list.Count == 0) { var student = await GetByIdAsync(idStudent); if (student is null) return null; return new StudentGoalSummary { StudentIdentifier = student.Identifier, Goals = [] }; } return new StudentGoalSummary { StudentIdentifier = list[0].StudentIdentifier, Goals = list.Select(r => new StudentGoalItem { GoalId = r.GoalId, GoalParentId = r.GoalParentId, Title = r.Title, Description = r.Description, Category = r.Category, ProgressEventCount = r.ProgressEventCount, BenchmarkCount = r.BenchmarkCount }).ToList() }; } // ***************************************************************** // Updates a goal's title, description, and category. // ***************************************************************** public async Task UpdateGoalAsync(Guid goalId, UpdateGoalDto dto) { using var db = Connection; var rowsAffected = await db.ExecuteScalarAsync( "sp_Goal_Update", new { p_id_goal = goalId.ToString(), p_id_goal_parent = (string?)null, p_id_student = (string?)null, p_id_user_created = (string?)null, p_title = dto.Title, p_description = dto.Description, p_category = dto.Category }, commandType: CommandType.StoredProcedure); return rowsAffected > 0; } // ***************************************************************** // Returns all benchmarks for a student, grouped under a summary // with the student identifier. Returns null if student not found. // ***************************************************************** public async Task GetBenchmarkSummaryAsync(Guid idStudent) { using var db = Connection; var rows = await db.QueryAsync( "sp_Benchmark_GetByStudentId", new { p_id_student = idStudent.ToString() }, commandType: CommandType.StoredProcedure); var list = rows.ToList(); if (list.Count == 0) { var student = await GetByIdAsync(idStudent); if (student is null) return null; return new StudentBenchmarkSummary { StudentIdentifier = student.Identifier, Benchmarks = [] }; } return new StudentBenchmarkSummary { StudentIdentifier = list[0].StudentIdentifier, Benchmarks = list.Select(r => new StudentBenchmarkItem { BenchmarkId = r.BenchmarkId, GoalId = r.GoalId, GoalTitle = r.GoalTitle, Benchmark = r.Benchmark, CreatedByName = r.CreatedByName, CreatedAt = r.CreatedAt, UpdatedAt = r.UpdatedAt }).ToList() }; } // ***************************************************************** // Inserts a new benchmark and returns the created benchmark item. // ***************************************************************** public async Task InsertBenchmarkAsync(Guid goalId, Guid userId, CreateBenchmarkDto dto) { var newId = Guid.NewGuid(); using var db = Connection; var row = await db.QuerySingleOrDefaultAsync( "sp_Benchmark_Insert", new { p_id_benchmark = newId.ToString(), p_id_goal = goalId.ToString(), p_id_user_created = userId.ToString(), p_benchmark = dto.Benchmark }, commandType: CommandType.StoredProcedure); if (row is null) return null; return new StudentBenchmarkItem { BenchmarkId = newId, GoalId = goalId, Benchmark = dto.Benchmark, CreatedAt = DateTime.UtcNow }; } // ***************************************************************** // Updates a benchmark's text and returns whether rows were affected. // ***************************************************************** public async Task UpdateBenchmarkAsync(Guid benchmarkId, string benchmarkText) { using var db = Connection; var rowsAffected = await db.ExecuteScalarAsync( "sp_Benchmark_Update", new { p_id_benchmark = benchmarkId.ToString(), p_benchmark = benchmarkText }, commandType: CommandType.StoredProcedure); return rowsAffected > 0; } }