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

Plagiarism checks: Enhance navigation to plagiarism cases from detection page #10078

Open
wants to merge 14 commits into
base: develop
Choose a base branch
from
Open
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ <h4 jhiTranslate="artemisApp.plagiarism.cases.pageSubtitle"></h4>
</div>
</div>
@for (exercise of exercisesWithPlagiarismCases; track exercise; let i = $index) {
<div class="card mb-2">
<div #plagExerciseElement class="card mb-2" [id]="'exercise-with-plagiarism-case-' + exercise.id">
<div class="card-header">
<div class="row">
<div class="col-3">
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { HttpResponse } from '@angular/common/http';
import { Component, OnInit } from '@angular/core';
import { Component, ElementRef, OnInit, effect, viewChildren } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { PlagiarismCasesService } from 'app/course/plagiarism-cases/shared/plagiarism-cases.service';
import { PlagiarismCase } from 'app/exercises/shared/plagiarism/types/PlagiarismCase';
Expand All @@ -15,22 +15,32 @@ import { AlertService } from 'app/core/util/alert.service';
export class PlagiarismCasesInstructorViewComponent implements OnInit {
courseId: number;
examId?: number;
exerciseId?: number;
plagiarismCases: PlagiarismCase[] = [];
groupedPlagiarismCases: any; // maybe? { [key: number]: PlagiarismCase[] }
exercisesWithPlagiarismCases: Exercise[] = [];

exerciseWithPlagCasesElements = viewChildren<ElementRef>('plagExerciseElement');

readonly getIcon = getIcon;
readonly documentationType: DocumentationType = 'PlagiarismChecks';

constructor(
private plagiarismCasesService: PlagiarismCasesService,
private route: ActivatedRoute,
private alertService: AlertService,
) {}
) {
effect(() => {
if (this.exerciseId) {
this.scrollToExercise();
}
});
}

ngOnInit(): void {
this.courseId = Number(this.route.snapshot.paramMap.get('courseId'));
this.examId = Number(this.route.snapshot.paramMap.get('examId'));
this.exerciseId = Number(this.route.snapshot.queryParamMap?.get('exerciseId'));

const plagiarismCasesForInstructor$ = this.examId
? this.plagiarismCasesService.getExamPlagiarismCasesForInstructor(this.courseId, this.examId)
Expand Down Expand Up @@ -68,6 +78,17 @@ export class PlagiarismCasesInstructorViewComponent implements OnInit {
});
}

scrollToExercise() {
const element = this.exerciseWithPlagCasesElements().find((elem) => elem.nativeElement.id === 'exercise-with-plagiarism-case-' + this.exerciseId);
if (element) {
element.nativeElement.scrollIntoView({
behavior: 'smooth',
block: 'start',
inline: 'nearest',
});
}
}

/**
* calculate the total number of plagiarism cases
* @param plagiarismCases plagiarismCases in the course or exam
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,17 @@ <h5 class="fw-medium">
</div>

<div class="plagiarism-header-right">
<button
[disabled]="comparison.status === plagiarismStatus.CONFIRMED || isLoading || exercise.teamMode"
class="btn btn-success btn-sm"
(click)="confirmPlagiarism()"
data-qa="confirm-plagiarism-button"
jhiTranslate="artemisApp.plagiarism.confirm"
></button>
@if (comparison.status !== plagiarismStatus.CONFIRMED && !isLoading && !exercise.teamMode) {
AjayvirS marked this conversation as resolved.
Show resolved Hide resolved
<button class="btn btn-success btn-sm" (click)="confirmPlagiarism()" data-qa="confirm-plagiarism-button" jhiTranslate="artemisApp.plagiarism.confirm"></button>
} @else {
<button
class="btn btn-primary btn-sm"
data-qa="view-plagiarism-cases-button"
jhiTranslate="artemisApp.plagiarism.viewCases"
[routerLink]="['/course-management', exercise.course?.id, 'plagiarism-cases']"
[queryParams]="{ exerciseId: exercise.id }"
></button>
}

<button
[disabled]="comparison.status === plagiarismStatus.DENIED || isLoading || exercise.teamMode"
Expand Down
1 change: 1 addition & 0 deletions src/main/webapp/i18n/de/plagiarism.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"plagiarism": {
"plagiarismDetection": "Plagiatskontrolle",
"confirm": "Bestätigen",
"viewCases": "Fälle ansehen",
"deny": "Ablehnen",
"denyAfterConfirmModalTitle": "Wechsel von Bestätigen zu Ablehnen",
"denyAfterConfirmModalText": "Bist du dir sicher, dass du die Entscheidung von \"Bestätigung des Plagiats\" in \"Ablehnung\" ändern möchtest? Dadurch wird der entsprechende Plagiatsfall einschließlich der Kommunikation mit dem/der Studierenden und des Urteils gelöscht und kann nicht rückgängig gemacht werden.",
Expand Down
1 change: 1 addition & 0 deletions src/main/webapp/i18n/en/plagiarism.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"plagiarism": {
"plagiarismDetection": "Plagiarism Detection",
"confirm": "Confirm",
"viewCases": "View Case(s)",
"deny": "Deny",
"denyAfterConfirmModalTitle": "Change from confirm to deny",
"denyAfterConfirmModalText": "Are you sure that you want to change the decision from confirming the plagiarism to denying it? This will delete the corresponding plagiarism case incl. the communication with the student and the verdict and cannot be undone.",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { DocumentationButtonComponent } from 'app/shared/components/documentatio
import { MockComponent } from 'ng-mocks';
import { NotificationService } from 'app/shared/notification/notification.service';
import { MockNotificationService } from '../../helpers/mocks/service/mock-notification.service';
import { ElementRef, signal } from '@angular/core';

jest.mock('app/shared/util/download.util', () => ({
downloadFile: jest.fn(),
Expand Down Expand Up @@ -198,4 +199,25 @@ describe('Plagiarism Cases Instructor View Component', () => {
expect(downloadSpy).toHaveBeenCalledOnce();
expect(downloadSpy).toHaveBeenCalledWith(new Blob(expectedBlob, { type: 'text/csv' }), 'plagiarism-cases.csv');
});

it('should scroll to the correct exercise element when scrollToExercise is called', () => {
component.exerciseId = 1;

const nativeElement1 = { id: 'exercise-with-plagiarism-case-1', scrollIntoView: jest.fn() };
const nativeElement2 = { id: 'exercise-with-plagiarism-case-2', scrollIntoView: jest.fn() };

const elementRef1 = new ElementRef(nativeElement1);
const elementRef2 = new ElementRef(nativeElement2);

component.exerciseWithPlagCasesElements = signal([elementRef1, elementRef2]);

component.scrollToExercise();

expect(nativeElement1.scrollIntoView).toHaveBeenCalledWith({
behavior: 'smooth',
block: 'start',
inline: 'nearest',
});
expect(nativeElement2.scrollIntoView).not.toHaveBeenCalled();
});
});
Loading