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

Отображение закрепления преподавателей за студентами #479

Merged
merged 7 commits into from
Mar 20, 2025
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
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
using System.Linq;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using HwProj.APIGateway.API.Models.Statistics;
using HwProj.AuthService.Client;
using HwProj.CoursesService.Client;
using HwProj.Models.AuthService.DTO;
using HwProj.Models.CoursesService.DTO;
using HwProj.Models.CoursesService.ViewModels;
using HwProj.Models.Roles;
using HwProj.SolutionsService.Client;
Expand All @@ -19,7 +23,7 @@ public class StatisticsController : AggregationController
private readonly ISolutionsServiceClient _solutionClient;
private readonly ICoursesServiceClient _coursesClient;

public StatisticsController(ISolutionsServiceClient solutionClient, IAuthServiceClient authServiceClient,
public StatisticsController(ISolutionsServiceClient solutionClient, IAuthServiceClient authServiceClient,
ICoursesServiceClient coursesServiceClient) :
base(authServiceClient)
{
Expand Down Expand Up @@ -56,32 +60,43 @@ public async Task<IActionResult> GetCourseStatistics(long courseId)
if (statistics == null) return Forbid();

var studentIds = statistics.Select(t => t.StudentId).ToArray();
var students = await AuthServiceClient.GetAccountsData(studentIds);
var getStudentsTask = AuthServiceClient.GetAccountsData(studentIds);

// Получаем пары <студент, закрепленные преподаватели (те, которые его явно в фильтре выбрали)>
var mentorsToStudents = await _coursesClient.GetMentorsToAssignedStudents(courseId);
var studentsToMentors = await GetStudentsToMentorsDictionary(mentorsToStudents);

var result = statistics.Zip(students, (stats, student) => new StatisticsCourseMatesModel
{
Id = student.UserId,
Name = student.Name,
Surname = student.Surname,
Homeworks = stats.Homeworks
}).OrderBy(t => t.Surname).ThenBy(t => t.Name);
var result = statistics.Zip(
await getStudentsTask,
(stats, student) =>
{
studentsToMentors.TryGetValue(student.UserId, out var reviewers);
return new StatisticsCourseMatesModel()
{
Id = student.UserId,
Name = student.Name,
Surname = student.Surname,
Reviewers = reviewers ?? Array.Empty<AccountDataDto>(),
Homeworks = stats.Homeworks
};
}).OrderBy(t => t.Surname).ThenBy(t => t.Name);

return Ok(result);
}

[HttpGet("{courseId}/charts")]
[ProducesResponseType(typeof(AdvancedCourseStatisticsViewModel), (int)HttpStatusCode.OK)]
public async Task<IActionResult> GetChartStatistics(long courseId)
{
var course = await _coursesClient.GetCourseById(courseId);
if (course == null)
if (course == null)
return Forbid();

var statistics = await _solutionClient.GetCourseStatistics(courseId, UserId);
var studentIds = statistics.Select(t => t.StudentId).ToArray();
var studentsData = await AuthServiceClient.GetAccountsData(studentIds);
var students = statistics.Zip(studentsData,

var students = statistics.Zip(studentsData,
(stats, student) => new StatisticsCourseMatesModel
{
Id = student.UserId,
Expand All @@ -105,8 +120,37 @@ public async Task<IActionResult> GetChartStatistics(long courseId)
AverageStudentSolutions = statisticsMeasure.AverageStudentSolutions,
BestStudentSolutions = statisticsMeasure.BestStudentSolutions
};

return Ok(result);
}

private async Task<Dictionary<string, AccountDataDto[]>> GetStudentsToMentorsDictionary(
MentorToAssignedStudentsDTO[] mentorsToStudents)
{
var mentorsIds = mentorsToStudents.Select(mts => mts.MentorId).ToArray();
var mentorsAccountData = await AuthServiceClient.GetAccountsData(mentorsIds);
var mentorIdToAccountData = mentorsAccountData
.ToDictionary(
accountData => accountData.UserId,
accountData => accountData
);

return mentorsToStudents
.SelectMany(m =>
m.SelectedStudentsIds.Select(studentId =>
new
{
StudentId = studentId,
Reviewer = mentorIdToAccountData[m.MentorId]
})
)
.GroupBy(sr => sr.StudentId)
.ToDictionary(
groups => groups.Key,
groups => groups.Select(sr => sr.Reviewer)
.Distinct()
.ToArray()
);
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using System;
using System.Collections.Generic;
using HwProj.Models.AuthService.DTO;
using HwProj.Models.StatisticsService;

namespace HwProj.APIGateway.API.Models.Statistics
Expand All @@ -8,6 +10,7 @@ public class StatisticsCourseMatesModel
public string Id { get; set; }
public string Name { get; set; }
public string Surname { get; set; }
public AccountDataDto[] Reviewers { get; set; } = Array.Empty<AccountDataDto>();
public List<StatisticsCourseHomeworksModel> Homeworks { get; set; } = new List<StatisticsCourseHomeworksModel>();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
using System.Collections.Generic;

namespace HwProj.Models.CoursesService.DTO
{
public class MentorToAssignedStudentsDTO
{
public string MentorId { get; set; }

public List<string> SelectedStudentsIds { get; set; } = new List<string>();
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using AutoMapper;
using HwProj.CoursesService.API.Filters;
Expand All @@ -9,6 +10,7 @@
using Microsoft.AspNetCore.Mvc;
using System.Linq;
using System.Net;
using HwProj.AuthService.Client;
using HwProj.CoursesService.API.Repositories;
using HwProj.Models.AuthService.DTO;
using HwProj.Models.CoursesService.DTO;
Expand All @@ -24,16 +26,19 @@ namespace HwProj.CoursesService.API.Controllers
public class CoursesController : Controller
{
private readonly ICoursesService _coursesService;
private readonly ICourseFilterService _courseFilterService;
private readonly IHomeworksRepository _homeworksRepository;
private readonly IMapper _mapper;

public CoursesController(ICoursesService coursesService,
IHomeworksRepository homeworksRepository,
IMapper mapper)
IMapper mapper,
ICourseFilterService courseFilterService)
{
_coursesService = coursesService;
_homeworksRepository = homeworksRepository;
_mapper = mapper;
_courseFilterService = courseFilterService;
}

[HttpGet]
Expand Down Expand Up @@ -238,5 +243,14 @@ public async Task<IActionResult> GetAllTagsForCourse(long courseId)

return Ok(result);
}

[HttpGet("getMentorsToStudents/{courseId}")]
[ProducesResponseType(typeof(MentorToAssignedStudentsDTO), (int)HttpStatusCode.OK)]
public async Task<IActionResult> GetMentorsToAssignedStudents(long courseId)
{
var mentorIds = await _coursesService.GetCourseLecturers(courseId);
var mentorsToAssignedStudents = await _courseFilterService.GetAssignedStudentsIds(courseId, mentorIds);
return Ok(mentorsToAssignedStudents);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,15 @@ public CourseFilterRepository(CourseContext context) : base(context)
return userToCourseFilter?.CourseFilter;
}

public async Task<List<UserToCourseFilter>> GetAsync(string[] userIds, long courseId)
{
return await Context.Set<UserToCourseFilter>()
.AsNoTracking()
.Where(u => userIds.Contains(u.UserId) && u.CourseId == courseId)
.Include(ucf => ucf.CourseFilter)
.ToListAsync();
}

public async Task<List<UserToCourseFilter>> GetAsync(string userId, long[] courseIds)
{
return await Context.Set<UserToCourseFilter>()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ namespace HwProj.CoursesService.API.Repositories
public interface ICourseFilterRepository : ICrudRepository<CourseFilter, long>
{
Task<CourseFilter?> GetAsync(string userId, long courseId);
Task<List<UserToCourseFilter>> GetAsync(string[] userIds, long courseId);
Task<List<UserToCourseFilter>> GetAsync(string userId, long[] courseIds);

Task<long> AddAsync(CourseFilter courseFilter, string userId, long courseId);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using HwProj.CoursesService.API.Models;
using HwProj.CoursesService.API.Repositories;
using HwProj.Models.CoursesService;
using HwProj.Models.CoursesService.DTO;
using HwProj.Models.CoursesService.ViewModels;
using HwProj.Models.Result;

Expand Down Expand Up @@ -76,6 +77,20 @@ public async Task<CourseDTO> ApplyFilter(CourseDTO courseDto, string userId)
return ApplyFilterInternal(courseDto, courseFilter);
}

public async Task<MentorToAssignedStudentsDTO[]> GetAssignedStudentsIds(long courseId, string[] mentorsIds)
{
var usersCourseFilters = await _courseFilterRepository.GetAsync(mentorsIds, courseId);

return usersCourseFilters
.Where(u => u.CourseFilter.Filter.HomeworkIds.Count == 0)
.Select(u => new MentorToAssignedStudentsDTO
{
MentorId = u.UserId,
SelectedStudentsIds = u.CourseFilter.Filter.StudentIds
})
.ToArray();
}

private async Task<long> AddCourseFilter(Filter filter, long courseId, string userId)
{
var courseFilterId =
Expand Down Expand Up @@ -131,4 +146,4 @@ private CourseDTO ApplyFilterInternal(CourseDTO courseDto, CourseFilter? courseF
};
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,6 @@ public interface ICourseFilterService
Task UpdateAsync(long courseFilterId, Filter filter);
Task<CourseDTO[]> ApplyFiltersToCourses(string userId, CourseDTO[] courses);
Task<CourseDTO> ApplyFilter(CourseDTO courseDto, string userId);
Task<MentorToAssignedStudentsDTO[]> GetAssignedStudentsIds(long courseId, string[] mentorsIds);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -585,6 +585,17 @@ public async Task AddAnswerForQuestion(AddAnswerForQuestionDto answer)
await _httpClient.SendAsync(httpRequest);
}

public async Task<MentorToAssignedStudentsDTO[]> GetMentorsToAssignedStudents(long courseId)
{
using var httpRequest = new HttpRequestMessage(
HttpMethod.Get,
_coursesServiceUri + $"api/Courses/getMentorsToStudents/{courseId}");

httpRequest.TryAddUserId(_httpContextAccessor);
var response = await _httpClient.SendAsync(httpRequest);
return await response.DeserializeAsync<MentorToAssignedStudentsDTO[]>();
}

public async Task<bool> Ping()
{
try
Expand All @@ -598,4 +609,4 @@ public async Task<bool> Ping()
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ public interface ICoursesServiceClient
Task AddQuestionForTask(AddTaskQuestionDto question);
Task<GetTaskQuestionDto[]> GetQuestionsForTask(long taskId);
Task AddAnswerForQuestion(AddAnswerForQuestionDto answer);
Task<MentorToAssignedStudentsDTO[]> GetMentorsToAssignedStudents(long courseId);
Task<bool> Ping();
}
}
6 changes: 6 additions & 0 deletions hwproj.front/src/api/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1862,6 +1862,12 @@ export interface StatisticsCourseMatesModel {
* @memberof StatisticsCourseMatesModel
*/
surname?: string;
/**
*
* @type {Array<AccountDataDto>}
* @memberof StatisticsCourseMatesModel
*/
reviewers?: Array<AccountDataDto>;
/**
*
* @type {Array<StatisticsCourseHomeworksModel>}
Expand Down
16 changes: 14 additions & 2 deletions hwproj.front/src/components/Courses/StudentStats.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import React, {useEffect, useState} from "react";
import {CourseViewModel, HomeworkViewModel, StatisticsCourseMatesModel, HomeworkTaskViewModel} from "../../api/";
import {CourseViewModel, HomeworkViewModel, StatisticsCourseMatesModel} from "../../api/";
import {useNavigate, useParams} from 'react-router-dom';
import {Table, TableBody, TableCell, TableContainer, TableHead, TableRow} from "@material-ui/core";
import StudentStatsCell from "../Tasks/StudentStatsCell";
import {Alert, Button, Chip} from "@mui/material";
import {Alert, Button, Chip, Typography} from "@mui/material";
import {grey} from "@material-ui/core/colors";
import StudentStatsUtils from "../../services/StudentStatsUtils";
import ShowChartIcon from "@mui/icons-material/ShowChart";
Expand Down Expand Up @@ -226,6 +226,18 @@ const StudentStats: React.FC<IStudentStatsProps> = (props) => {
variant={"head"}
>
{cm.surname} {cm.name}
<Typography
style={{
color: "GrayText",
fontSize: "12px",
lineHeight: '1.2'
}}
>
{cm.reviewers && cm.reviewers
.filter(r => r.userId !== props.userId)
.map(r => `${r.name} ${r.surname}`)
.join(', ')}
</Typography>
</TableCell>
{hasHomeworks && <TableCell
align="center"
Expand Down