Skip to content

[WIP] Detailed Course Statistics #140

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

Open
wants to merge 8 commits into
base: master
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
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,18 @@ public async Task<IActionResult> GetCourseStatistics(long courseId)
var result = await _solutionClient.GetCourseStatistics(courseId, userId);
return result == null
? Forbid()
: Ok(result) as IActionResult;
: Ok(result);
}

[HttpGet("getDetailedStat/{courseId}")]
[ProducesResponseType(typeof(DetailedCourseStatsModel[]), (int)HttpStatusCode.OK)]
public async Task<IActionResult> GetDetailedCourseStatistics(long courseId)
{
var userId = Request.GetUserId();
var result = await _solutionClient.GetDetailedCourseStatistics(courseId, userId);
return result == null
? Forbid()
: Ok(result);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
namespace HwProj.Models.StatisticsService
{
public class DetailedCourseStatsModel
{
public long TaskId { get; set; }

public long NumberSolutionsRatePosted { get; set; }

public long NumberSolutionsRateFinal { get; set; }

public double AverageTimeHandIn { get; set; }

public double MinimumTimeHandIn { get; set; }

public double AverageScoreOnFirstAttempt { get; set; }

public double AverageFinalGrade { get; set; }

public double AverageNumberOfCorrections { get; set; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,9 @@ public class SolutionsController : Controller
private readonly ISolutionsService _solutionsService;
private readonly IMapper _mapper;
private readonly ICoursesServiceClient _coursesClient;

public SolutionsController(ISolutionsService solutionsService, IMapper mapper, ICoursesServiceClient coursesClient, IAuthServiceClient authClient)

public SolutionsController(ISolutionsService solutionsService, IMapper mapper,
ICoursesServiceClient coursesClient, IAuthServiceClient authClient)
{
_solutionsService = solutionsService;
_coursesClient = coursesClient;
Expand Down Expand Up @@ -63,18 +64,22 @@ public async Task<IActionResult> PostSolution(long taskId, [FromBody] SolutionVi
var homework = await _coursesClient.GetHomework(task.HomeworkId);
var course = await _coursesClient.GetCourseById(homework.CourseId, solutionViewModel.StudentId);

if (course.CourseMates.Exists(courseMate => courseMate.StudentId == solutionViewModel.StudentId && courseMate.IsAccepted) && task.CanSendSolution)
if (course.CourseMates.Exists(courseMate =>
courseMate.StudentId == solutionViewModel.StudentId && courseMate.IsAccepted) &&
task.CanSendSolution)
{
var solution = _mapper.Map<Solution>(solutionViewModel);
solution.TaskId = taskId;
var solutionId = await _solutionsService.PostOrUpdateAsync(taskId, solution);
return Ok(solutionId);
}

return Forbid();
}

[HttpPost("rateSolution/{solutionId}")]
public async Task<IActionResult> RateSolution(long solutionId, [FromQuery] int newRating, [FromQuery] string lecturerComment, [FromQuery] string lecturerId)
public async Task<IActionResult> RateSolution(long solutionId, [FromQuery] int newRating,
[FromQuery] string lecturerComment, [FromQuery] string lecturerId)
{
var solution = await _solutionsService.GetSolutionAsync(solutionId);
var task = await _coursesClient.GetTask(solution.TaskId);
Expand All @@ -89,7 +94,7 @@ public async Task<IActionResult> RateSolution(long solutionId, [FromQuery] int n

return Forbid();
}

[HttpPost("markSolutionFinal/{solutionId}")]
public async Task MarkSolutionFinal(long solutionId)
{
Expand All @@ -101,7 +106,7 @@ public async Task DeleteSolution(long solutionId)
{
await _solutionsService.DeleteSolutionAsync(solutionId);
}

[HttpPost("{groupId}/{taskId}")]
public async Task<long> PostSolution(long groupId, long taskId, [FromBody] SolutionViewModel solutionViewModel)
{
Expand All @@ -124,16 +129,16 @@ public async Task<IActionResult> GetCourseStat(long courseId, [FromQuery] string
var course = await _coursesClient.GetCourseById(courseId, userId);
course.CourseMates.RemoveAll(cm => !cm.IsAccepted);

var solutions = (await _solutionsService.GetAllSolutionsAsync())
var solutions = (await _solutionsService.GetAllSolutionsAsync())
.Where(s => course.Homeworks
.Any(hw => hw.Tasks
.Any(t => t.Id == s.TaskId)))
.ToList();

var courseMatesData = new Dictionary<string, AccountDataDto>();

//course.CourseMates.ForEach(async cm => courseMatesData.Add(cm.StudentId, await _authClient.GetAccountData(cm.StudentId)));

foreach (var cm in course.CourseMates)
{
courseMatesData.Add(cm.StudentId, await _authClient.GetAccountData(cm.StudentId));
Expand All @@ -155,5 +160,13 @@ public async Task<IActionResult> GetCourseStat(long courseId, [FromQuery] string

return Ok(result);
}

[HttpGet("getDetailedCourseStat/{courseId}")]
[ProducesResponseType(typeof(DetailedCourseStatsModel[]), (int)HttpStatusCode.OK)]
public async Task<IActionResult> GetDetailedCourseStat(long courseId, [FromQuery] string userId)
{
var result = await _solutionsService.GetDetailedCourseStat(courseId, userId);
return Ok(result);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System.Threading.Tasks;
using HwProj.Models.SolutionsService;
using HwProj.Models.StatisticsService;
using HwProj.SolutionsService.API.Models;

namespace HwProj.SolutionsService.API.Services
Expand All @@ -13,13 +14,15 @@ public interface ISolutionsService
Task<Solution[]> GetTaskSolutionsFromStudentAsync(long taskId, string studentId);

Task<Solution[]> GetTaskSolutionsFromGroupAsync(long taskId, long groupId);

Task<long> PostOrUpdateAsync(long taskId, Solution solution);

Task RateSolutionAsync(long solutionId, int newRating, string lecturerComment);

Task DeleteSolutionAsync(long solutionId);

Task MarkSolutionFinal(long solutionId);

Task<DetailedCourseStatsModel[]> GetDetailedCourseStat(long courseId, string userId);
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using AutoMapper;
Expand All @@ -8,6 +9,7 @@
using HwProj.Models.AuthService.DTO;
using HwProj.Models.CoursesService.ViewModels;
using HwProj.Models.SolutionsService;
using HwProj.Models.StatisticsService;
using HwProj.SolutionsService.API.Events;
using HwProj.SolutionsService.API.Repositories;
using Microsoft.EntityFrameworkCore;
Expand All @@ -22,7 +24,9 @@ public class SolutionsService : ISolutionsService
private readonly IMapper _mapper;
private readonly ICoursesServiceClient _coursesServiceClient;
private readonly IAuthServiceClient _authServiceClient;
public SolutionsService(ISolutionsRepository solutionsRepository, IEventBus eventBus, IMapper mapper, ICoursesServiceClient coursesServiceClient, IAuthServiceClient authServiceClient)

public SolutionsService(ISolutionsRepository solutionsRepository, IEventBus eventBus, IMapper mapper,
ICoursesServiceClient coursesServiceClient, IAuthServiceClient authServiceClient)
{
_solutionsRepository = solutionsRepository;
_eventBus = eventBus;
Expand All @@ -47,12 +51,12 @@ public async Task<Solution[]> GetTaskSolutionsFromStudentAsync(long taskId, stri
.FindAll(solution => solution.TaskId == taskId && solution.StudentId == studentId)
.ToArrayAsync();
}


public async Task<long> PostOrUpdateAsync(long taskId, Solution solution)
{
solution.PublicationDate = DateTime.UtcNow;
var allSolutionsForTask= await GetTaskSolutionsFromStudentAsync(taskId, solution.StudentId);
var allSolutionsForTask = await GetTaskSolutionsFromStudentAsync(taskId, solution.StudentId);
var currentSolution = allSolutionsForTask.FirstOrDefault(s => s.Id == solution.Id);
var solutionModel = _mapper.Map<SolutionViewModel>(solution);
var task = await _coursesServiceClient.GetTask(solution.TaskId);
Expand All @@ -69,7 +73,7 @@ public async Task<long> PostOrUpdateAsync(long taskId, Solution solution)
var id = await _solutionsRepository.AddAsync(solution);
return id;
}

await _solutionsRepository.UpdateAsync(currentSolution.Id, s => new Solution()
{
State = SolutionState.Rated,
Expand Down Expand Up @@ -110,5 +114,91 @@ public Task<Solution[]> GetTaskSolutionsFromGroupAsync(long taskId, long groupId
{
return _solutionsRepository.FindAll(cm => cm.GroupId == groupId).ToArrayAsync();
}

public async Task<DetailedCourseStatsModel[]> GetDetailedCourseStat(long courseId, string userId)
{
var course = await _coursesServiceClient.GetCourseById(courseId, userId);

var solutions = (await GetAllSolutionsAsync())
.Where(s => course.Homeworks
.Any(hw => hw.Tasks
.Any(t => t.Id == s.TaskId)))
.ToArray();

var result = course.Homeworks.SelectMany(hw =>
hw.Tasks.Select(t => new DetailedCourseStatsModel()
{
TaskId = t.Id,
NumberSolutionsRatePosted = GetNumberSolutionsRate(solutions, t.Id, SolutionState.Posted),
NumberSolutionsRateFinal = GetNumberSolutionsRate(solutions, t.Id, SolutionState.Final),
AverageTimeHandIn = GetAverageTimeHandIn(solutions, t.Id),
MinimumTimeHandIn = GetMinimumTimeHandIn(solutions, t.Id),
AverageScoreOnFirstAttempt = GetAverageScoreOnFirstAttempt(solutions, t.Id),
AverageFinalGrade = GetAverageFinalGrade(solutions, t.Id),
AverageNumberOfCorrections = GetAverageNumberOfCorrections(solutions, t.Id)
})).ToArray();

return result;
}

private long GetNumberSolutionsRate(Solution[] solutions, long taskId, SolutionState state)
{
var result = solutions.Where(s => s.TaskId == taskId)
.Select(s => s.State == state)
.Count();

return result;
}

private double GetAverageTimeHandIn(Solution[] solutions, long taskId)
{
var sol = solutions
.Where(s => s.TaskId == taskId && s.State == SolutionState.Final)
.ToArray();

var result = sol.Count() == 0
? 0
: sol.Average(s => (DateTime.Now - s.PublicationDate).TotalDays);

return result;
}

private double GetMinimumTimeHandIn(Solution[] solutions, long taskId)
{
var sol = solutions.Where(s => s.TaskId == taskId && s.State == SolutionState.Final).ToArray();
var result = sol.Count() == 0
? 0
: sol.Min(s => (DateTime.Now - s.PublicationDate).TotalDays);

return result;
}

private double GetAverageScoreOnFirstAttempt(Solution[] solutions, long taskId)
{
var result = solutions.Where(s => s.TaskId == taskId)
.GroupBy(s => new { s.StudentId, s.PublicationDate })
.Average(s => s.First().Rating);

return result;
}

private double GetAverageFinalGrade(Solution[] solutions, long taskId)
{
var sol = solutions.Where(s => s.TaskId == taskId && s.State == SolutionState.Final).ToArray();
var result = sol.Count() == 0
? 0
: sol.Average(s => s.Rating);

return result;
}

private double GetAverageNumberOfCorrections(Solution[] solutions, long taskId)
{
var result = solutions.Where(s => s.TaskId == taskId)
.GroupBy(s => new { s.StudentId, s.PublicationDate })
.Average(g => g.Count());

return result;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,6 @@ public interface ISolutionsServiceClient
Task<long> PostGroupSolution(SolutionViewModel model, long taskId, long groupId);
Task<Solution[]> GetTaskSolutions(long groupId, long taskId);
Task<StatisticsCourseMatesModel[]> GetCourseStatistics(long courseId, string userId);
Task<DetailedCourseStatsModel[]> GetDetailedCourseStatistics(long courseId, string userId);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -140,5 +140,15 @@ public async Task<StatisticsCourseMatesModel[]> GetCourseStatistics(long courseI
var response = await _httpClient.SendAsync(httpRequest);
return await response.DeserializeAsync<StatisticsCourseMatesModel[]>();
}

public async Task<DetailedCourseStatsModel[]> GetDetailedCourseStatistics(long courseId, string userId)
{
using var httpRequest = new HttpRequestMessage(
HttpMethod.Get,
_solutionServiceUri + $"api/Solutions/getDetailedCourseStat/{courseId}?userId={userId}");

var response = await _httpClient.SendAsync(httpRequest);
return await response.DeserializeAsync<DetailedCourseStatsModel[]>();
}
}
}
Loading