Skip to content

Commit 962e345

Browse files
committed
StudentSolutionsPage: просмотр решений студентов другого преподавателя
1 parent 47a4709 commit 962e345

File tree

4 files changed

+108
-28
lines changed

4 files changed

+108
-28
lines changed

HwProj.APIGateway/HwProj.APIGateway.API/Controllers/SolutionsController.cs

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -147,13 +147,26 @@ public async Task<IActionResult> GetStudentSolution(long taskId, string studentI
147147
[Authorize]
148148
[HttpGet("tasks/{taskId}")]
149149
[ProducesResponseType(typeof(TaskSolutionStatisticsPageData), (int)HttpStatusCode.OK)]
150-
public async Task<IActionResult> GetTaskSolutionsPageData(long taskId)
150+
public async Task<IActionResult> GetTaskSolutionsPageData(long taskId, string? secondMentorId = null)
151151
{
152152
var course = await _coursesServiceClient.GetCourseByTask(taskId);
153153
//TODO: CourseMentorOnlyAttribute
154154
if (course == null || !course.MentorIds.Contains(UserId)) return Forbid();
155155

156156
var students = course.AcceptedStudents.ToDictionary(x => x.StudentId);
157+
var secondMentorStudentIds = new HashSet<string>();
158+
if (secondMentorId != null && course.MentorIds.Contains(secondMentorId))
159+
{
160+
var secondMentorCourseResult =
161+
await _coursesServiceClient.GetCourseByIdForMentor(course.Id, secondMentorId);
162+
if (!secondMentorCourseResult.Succeeded) return BadRequest(secondMentorCourseResult.Errors);
163+
164+
foreach (var student in secondMentorCourseResult.Value.AcceptedStudents)
165+
{
166+
secondMentorStudentIds.Add(student.StudentId);
167+
students.TryAdd(student.StudentId, student);
168+
}
169+
}
157170
var studentIds = students.Keys.ToArray();
158171

159172
var currentDateTime = DateTime.UtcNow;
@@ -225,9 +238,11 @@ public async Task<IActionResult> GetTaskSolutionsPageData(long taskId)
225238
Student = new StudentDataDto(usersData[studentId])
226239
{
227240
Characteristics = students[studentId].Characteristics,
228-
}
241+
},
242+
HasDifferentReviewer = secondMentorStudentIds.Contains(studentId)
229243
})
230-
.OrderBy(t => t.Student.Surname)
244+
.OrderBy(x => !x.HasDifferentReviewer)
245+
.ThenBy(t => t.Student.Surname)
231246
.ThenBy(t => t.Student.Name)
232247
.ToArray(),
233248
};
@@ -253,7 +268,9 @@ public async Task<IActionResult> GetTaskSolutionsPageData(long taskId)
253268
}).ToArray()
254269
}).ToArray()
255270
};
256-
}).ToArray()
271+
}).ToArray(),
272+
273+
CourseMentors = course.MentorIds.Select(t => usersData[t]).ToArray()
257274
};
258275

259276
return Ok(result);

HwProj.APIGateway/HwProj.APIGateway.API/Models/Solutions/UserTaskSolutions.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
using HwProj.Models.AuthService.DTO;
22
using HwProj.Models.CoursesService.DTO;
3-
using HwProj.Models.CoursesService.ViewModels;
43
using HwProj.Models.SolutionsService;
54
using HwProj.Models.StatisticsService;
65

@@ -10,6 +9,7 @@ public class UserTaskSolutions
109
{
1110
public GetSolutionModel[] Solutions { get; set; }
1211
public StudentDataDto Student { get; set; }
12+
public bool HasDifferentReviewer { get; set; }
1313
}
1414

1515
public class UserTaskSolutions2
@@ -23,6 +23,7 @@ public class UserTaskSolutions2
2323

2424
public class TaskSolutionStatisticsPageData
2525
{
26+
public AccountDataDto[] CourseMentors { get; set; }
2627
public TaskSolutions[] TaskSolutions { get; set; }
2728
public long CourseId { get; set; }
2829
public HomeworksGroupSolutionStats[] StatsForTasks { get; set; }

hwproj.front/src/api/api.ts

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2376,6 +2376,12 @@ export interface TaskDeadlineView {
23762376
* @interface TaskSolutionStatisticsPageData
23772377
*/
23782378
export interface TaskSolutionStatisticsPageData {
2379+
/**
2380+
*
2381+
* @type {Array<AccountDataDto>}
2382+
* @memberof TaskSolutionStatisticsPageData
2383+
*/
2384+
courseMentors?: Array<AccountDataDto>;
23792385
/**
23802386
*
23812387
* @type {Array<TaskSolutions>}
@@ -2627,6 +2633,12 @@ export interface UserTaskSolutions {
26272633
* @memberof UserTaskSolutions
26282634
*/
26292635
student?: StudentDataDto;
2636+
/**
2637+
*
2638+
* @type {boolean}
2639+
* @memberof UserTaskSolutions
2640+
*/
2641+
hasDifferentReviewer?: boolean;
26302642
}
26312643
/**
26322644
*
@@ -7815,10 +7827,11 @@ export const SolutionsApiFetchParamCreator = function (configuration?: Configura
78157827
/**
78167828
*
78177829
* @param {number} taskId
7830+
* @param {string} [secondMentorId]
78187831
* @param {*} [options] Override http request option.
78197832
* @throws {RequiredError}
78207833
*/
7821-
solutionsGetTaskSolutionsPageData(taskId: number, options: any = {}): FetchArgs {
7834+
solutionsGetTaskSolutionsPageData(taskId: number, secondMentorId?: string, options: any = {}): FetchArgs {
78227835
// verify required parameter 'taskId' is not null or undefined
78237836
if (taskId === null || taskId === undefined) {
78247837
throw new RequiredError('taskId','Required parameter taskId was null or undefined when calling solutionsGetTaskSolutionsPageData.');
@@ -7838,6 +7851,10 @@ export const SolutionsApiFetchParamCreator = function (configuration?: Configura
78387851
localVarHeaderParameter["Authorization"] = localVarApiKeyValue;
78397852
}
78407853

7854+
if (secondMentorId !== undefined) {
7855+
localVarQueryParameter['secondMentorId'] = secondMentorId;
7856+
}
7857+
78417858
localVarUrlObj.query = Object.assign({}, localVarUrlObj.query, localVarQueryParameter, options.query);
78427859
// fix override query string Detail: https://stackoverflow.com/a/7517673/1077943
78437860
localVarUrlObj.search = null;
@@ -8182,11 +8199,12 @@ export const SolutionsApiFp = function(configuration?: Configuration) {
81828199
/**
81838200
*
81848201
* @param {number} taskId
8202+
* @param {string} [secondMentorId]
81858203
* @param {*} [options] Override http request option.
81868204
* @throws {RequiredError}
81878205
*/
8188-
solutionsGetTaskSolutionsPageData(taskId: number, options?: any): (fetch?: FetchAPI, basePath?: string) => Promise<TaskSolutionStatisticsPageData> {
8189-
const localVarFetchArgs = SolutionsApiFetchParamCreator(configuration).solutionsGetTaskSolutionsPageData(taskId, options);
8206+
solutionsGetTaskSolutionsPageData(taskId: number, secondMentorId?: string, options?: any): (fetch?: FetchAPI, basePath?: string) => Promise<TaskSolutionStatisticsPageData> {
8207+
const localVarFetchArgs = SolutionsApiFetchParamCreator(configuration).solutionsGetTaskSolutionsPageData(taskId, secondMentorId, options);
81908208
return (fetch: FetchAPI = isomorphicFetch, basePath: string = BASE_PATH) => {
81918209
return fetch(basePath + localVarFetchArgs.url, localVarFetchArgs.options).then((response) => {
81928210
if (response.status >= 200 && response.status < 300) {
@@ -8367,11 +8385,12 @@ export const SolutionsApiFactory = function (configuration?: Configuration, fetc
83678385
/**
83688386
*
83698387
* @param {number} taskId
8388+
* @param {string} [secondMentorId]
83708389
* @param {*} [options] Override http request option.
83718390
* @throws {RequiredError}
83728391
*/
8373-
solutionsGetTaskSolutionsPageData(taskId: number, options?: any) {
8374-
return SolutionsApiFp(configuration).solutionsGetTaskSolutionsPageData(taskId, options)(fetch, basePath);
8392+
solutionsGetTaskSolutionsPageData(taskId: number, secondMentorId?: string, options?: any) {
8393+
return SolutionsApiFp(configuration).solutionsGetTaskSolutionsPageData(taskId, secondMentorId, options)(fetch, basePath);
83758394
},
83768395
/**
83778396
*
@@ -8500,12 +8519,13 @@ export class SolutionsApi extends BaseAPI {
85008519
/**
85018520
*
85028521
* @param {number} taskId
8522+
* @param {string} [secondMentorId]
85038523
* @param {*} [options] Override http request option.
85048524
* @throws {RequiredError}
85058525
* @memberof SolutionsApi
85068526
*/
8507-
public solutionsGetTaskSolutionsPageData(taskId: number, options?: any) {
8508-
return SolutionsApiFp(this.configuration).solutionsGetTaskSolutionsPageData(taskId, options)(this.fetch, this.basePath);
8527+
public solutionsGetTaskSolutionsPageData(taskId: number, secondMentorId?: string, options?: any) {
8528+
return SolutionsApiFp(this.configuration).solutionsGetTaskSolutionsPageData(taskId, secondMentorId, options)(this.fetch, this.basePath);
85098529
}
85108530

85118531
/**

hwproj.front/src/components/Solutions/StudentSolutionsPage.tsx

Lines changed: 58 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import {
55
HomeworkTaskViewModel,
66
Solution,
77
TaskSolutionsStats,
8-
SolutionState, StudentDataDto
8+
SolutionState, StudentDataDto, AccountDataDto
99
} from "@/api";
1010
import Typography from "@material-ui/core/Typography";
1111
import Task from "../Tasks/Task";
@@ -26,7 +26,10 @@ import {
2626
SelectChangeEvent,
2727
Stack,
2828
Tooltip,
29-
Checkbox
29+
Checkbox,
30+
Autocomplete,
31+
AutocompleteRenderInputParams,
32+
TextField
3033
} from "@mui/material";
3134
import StudentStatsUtils from "../../services/StudentStatsUtils";
3235

@@ -37,16 +40,19 @@ import {getTip} from "../Common/HomeworkTags";
3740
import {appBarStateManager} from "../AppBar";
3841
import {DotLottieReact} from "@lottiefiles/dotlottie-react";
3942
import {RemovedFromCourseTag} from "@/components/Common/StudentTags";
43+
import AuthService from "@/services/AuthService";
4044

4145
interface IStudentSolutionsPageState {
4246
currentTaskId: string
4347
task: HomeworkTaskViewModel
4448
isLoaded: boolean
4549
courseId: number,
50+
courseMentors: AccountDataDto[],
4651
homeworkSolutionsStats: HomeworksGroupSolutionStats[],
4752
taskStudentsSolutionsPreview: {
4853
taskId: number,
4954
studentSolutionsPreview: {
55+
hasDifferentReviewer: boolean,
5056
student: StudentDataDto,
5157
solutions: GetSolutionModel[]
5258
lastSolution: GetSolutionModel,
@@ -79,14 +85,18 @@ const StudentSolutionsPage: FC = () => {
7985
const [studentSolutionsState, setStudentSolutionsState] = useState<IStudentSolutionsPageState>({
8086
currentTaskId: "",
8187
task: {},
88+
courseMentors: [],
8289
isLoaded: false,
8390
courseId: -1,
8491
homeworkSolutionsStats: [],
8592
taskStudentsSolutionsPreview: [],
8693
})
87-
const [filterState, setFilterState] = React.useState<Filter[]>(
94+
const [filterState, setFilterState] = useState<Filter[]>(
8895
localStorage.getItem(FilterStorageKey)?.split(", ").filter(x => x !== "").map(x => x as Filter) || []
8996
)
97+
98+
const [secondMentorId, setSecondMentorId] = useState<string | undefined>(undefined)
99+
90100
const handleFilterChange = (event: SelectChangeEvent<typeof filterState>) => {
91101
const filters = filterState.length > 0 ? [] : ["Только непроверенные" as Filter]
92102
localStorage.setItem(FilterStorageKey, filters.join(", "))
@@ -100,9 +110,12 @@ const StudentSolutionsPage: FC = () => {
100110
currentTaskId,
101111
taskStudentsSolutionsPreview,
102112
courseId,
103-
homeworkSolutionsStats
113+
homeworkSolutionsStats,
114+
courseMentors
104115
} = studentSolutionsState
105116

117+
const secondMentor = courseMentors.find(x => x.userId == secondMentorId)
118+
106119
const currentTaskSolutionsPreview = taskStudentsSolutionsPreview.find(x => x.taskId === +currentTaskId)
107120
const currentTaskSolutions = currentTaskSolutionsPreview?.studentSolutionsPreview || []
108121

@@ -157,7 +170,7 @@ const StudentSolutionsPage: FC = () => {
157170
? []
158171
: homeworks.map(h => h.statsForTasks![taskIndexInHomework].taskId!)
159172

160-
const getTaskData = async (taskId: string, fullUpdate: boolean) => {
173+
const getTaskData = async (taskId: string, secondMentorId: string | undefined, fullUpdate: boolean) => {
161174
const task = await ApiSingleton.tasksApi.tasksGetTask(+taskId!)
162175

163176
if (!fullUpdate && versionsOfCurrentTask.includes(+taskId)) {
@@ -173,14 +186,16 @@ const StudentSolutionsPage: FC = () => {
173186
const {
174187
taskSolutions,
175188
courseId,
176-
statsForTasks
177-
} = await ApiSingleton.solutionsApi.solutionsGetTaskSolutionsPageData(+taskId!)
189+
statsForTasks,
190+
courseMentors
191+
} = await ApiSingleton.solutionsApi.solutionsGetTaskSolutionsPageData(+taskId!, secondMentorId)
178192

179193
const studentSolutionsPreview = taskSolutions!.map(ts => ({
180194
taskId: ts.taskId!,
181195
studentSolutionsPreview: ts.studentSolutions!.map(studentSolutions => {
182196
const ratedSolutionInfo = StudentStatsUtils.calculateLastRatedSolutionInfo(studentSolutions.solutions!, task.maxRating!)
183197
return {
198+
hasDifferentReviewer: studentSolutions.hasDifferentReviewer!,
184199
student: studentSolutions.student!, ...ratedSolutionInfo,
185200
solutions: studentSolutions.solutions!
186201
}
@@ -194,6 +209,7 @@ const StudentSolutionsPage: FC = () => {
194209
currentTaskId: taskId,
195210
homeworkSolutionsStats: statsForTasks!,
196211
taskStudentsSolutionsPreview: studentSolutionsPreview,
212+
courseMentors: courseMentors!.filter(x => x.userId !== ApiSingleton.authService.getUserId()),
197213
courseId: courseId!
198214
})
199215
}
@@ -204,7 +220,7 @@ const StudentSolutionsPage: FC = () => {
204220
}, [courseId])
205221

206222
useEffect(() => {
207-
getTaskData(taskId!, false)
223+
getTaskData(taskId!, secondMentorId, false)
208224
}, [taskId])
209225

210226
useEffect(() => {
@@ -221,9 +237,8 @@ const StudentSolutionsPage: FC = () => {
221237
: <TaskAltIcon color={isSelected ? "primary" : "success"}/>
222238
}
223239

224-
const renderStudentListItem = (student: StudentDataDto) => {
225-
if (!student.characteristics || student.characteristics.tags?.length === 0) return student.surname + " " + student.name
226-
const tags = student.characteristics.tags!
240+
const renderStudentListItem = (student: StudentDataDto, hasDifferentReviewer: boolean) => {
241+
const tags = student.characteristics?.tags || []
227242

228243
const hasGoodCharacteristics = tags.some(x => x.startsWith("+"))
229244
const hasBadCharacteristics = tags.some(x => x.startsWith("-"))
@@ -232,13 +247,20 @@ const StudentSolutionsPage: FC = () => {
232247
? <s>{student.surname + " " + student.name}</s>
233248
: student.surname + " " + student.name
234249

235-
if (!hasGoodCharacteristics && !hasBadCharacteristics) return studentFio
236-
237250
return <div>{studentFio}
238251
<sup style={{paddingLeft: 5}}>
239252
{hasGoodCharacteristics && <ThumbUpIcon color={"success"} style={{fontSize: 14}}/>}
240253
{hasBadCharacteristics && <ThumbDownIcon color={"error"} style={{fontSize: 14}}/>}
241254
</sup>
255+
{hasDifferentReviewer && secondMentor && <Typography
256+
style={{
257+
color: "GrayText",
258+
fontSize: "12px",
259+
lineHeight: '1.2'
260+
}}
261+
>
262+
{secondMentor.name} {secondMentor.surname}
263+
</Typography>}
242264
</div>
243265
}
244266

@@ -281,6 +303,23 @@ const StudentSolutionsPage: FC = () => {
281303
</Grid>
282304
<Grid container spacing={3} style={{marginTop: '1px'}} direction={"row"}>
283305
<Grid item xs={12} sm={12} md={4} lg={3}>
306+
{courseMentors.length > 0 && <Autocomplete
307+
fullWidth
308+
freeSolo={false}
309+
size={"medium"}
310+
options={courseMentors}
311+
getOptionLabel={(option) => option.name! + ' ' + option.surname!}
312+
value={secondMentor}
313+
onChange={async (_, newValue) => {
314+
setSecondMentorId(newValue?.userId)
315+
await getTaskData(currentTaskId, newValue?.userId, true)
316+
}}
317+
renderInput={params => <TextField
318+
{...params}
319+
label="Другие решения"
320+
placeholder="Выберите преподавателя"
321+
/>}
322+
/>}
284323
<Stack direction={"row"} alignItems={"center"}>
285324
<Checkbox
286325
onChange={handleFilterChange}
@@ -294,7 +333,8 @@ const StudentSolutionsPage: FC = () => {
294333
color,
295334
solutionsDescription,
296335
lastRatedSolution,
297-
student
336+
student,
337+
hasDifferentReviewer
298338
}, idx) => {
299339
const {userId} = student
300340
const storageKey = {
@@ -332,7 +372,9 @@ const StudentSolutionsPage: FC = () => {
332372
size={"small"}
333373
label={lastRatedSolution == undefined ? "?" : lastRatedSolution.rating}/>
334374
</Tooltip>}
335-
<ListItemText primary={renderStudentListItem(student)}/>
375+
<ListItemText
376+
key={student.userId}
377+
primary={renderStudentListItem(student, hasDifferentReviewer)}/>
336378
</Stack>
337379
</ListItemButton>
338380
</Link>
@@ -375,7 +417,7 @@ const StudentSolutionsPage: FC = () => {
375417
courseStudents={courseStudents}
376418
onSolutionRateClick={async () => {
377419
//const nextStudentIndex = studentSolutionsPreview.findIndex(x => x.student.userId !== currentStudentId && x.lastSolution && x.lastSolution.state === Solution.StateEnum.NUMBER_0)
378-
await getTaskData(currentTaskId, true)
420+
await getTaskData(currentTaskId, secondMentorId, true)
379421
//else navigate(`/task/${currentTaskId}/${studentSolutionsPreview[nextStudentIndex].student.userId}`)
380422
}}
381423
/>

0 commit comments

Comments
 (0)