Skip to content

Commit

Permalink
Development: Migrate suspicious behavior module to new client coding …
Browse files Browse the repository at this point in the history
…guidelines (#9887)
  • Loading branch information
coolchock authored Dec 20, 2024
1 parent 98c0bcf commit 9436717
Show file tree
Hide file tree
Showing 11 changed files with 75 additions and 81 deletions.
8 changes: 0 additions & 8 deletions src/main/webapp/app/exam/manage/exam-management.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,6 @@ import { ArtemisModePickerModule } from 'app/exercises/shared/mode-picker/mode-p
import { StudentExamTimelineComponent } from './student-exams/student-exam-timeline/student-exam-timeline.component';
import { TitleChannelNameModule } from 'app/shared/form/title-channel-name/title-channel-name.module';
import { ExamEditWorkingTimeDialogComponent } from 'app/exam/manage/exams/exam-checklist-component/exam-edit-workingtime-dialog/exam-edit-working-time-dialog.component';
import { SuspiciousBehaviorComponent } from './suspicious-behavior/suspicious-behavior.component';
import { SuspiciousSessionsOverviewComponent } from './suspicious-behavior/suspicious-sessions-overview/suspicious-sessions-overview.component';
import { PlagiarismCasesOverviewComponent } from './suspicious-behavior/plagiarism-cases-overview/plagiarism-cases-overview.component';
import { SuspiciousSessionsComponent } from './suspicious-behavior/suspicious-sessions/suspicious-sessions.component';
import { ExamEditWorkingTimeComponent } from 'app/exam/manage/exams/exam-checklist-component/exam-edit-workingtime-dialog/exam-edit-working-time.component';
import { ExamLiveAnnouncementCreateModalComponent } from 'app/exam/manage/exams/exam-checklist-component/exam-announcement-dialog/exam-live-announcement-create-modal.component';
import { ExamLiveAnnouncementCreateButtonComponent } from 'app/exam/manage/exams/exam-checklist-component/exam-announcement-dialog/exam-live-announcement-create-button.component';
Expand Down Expand Up @@ -145,10 +141,6 @@ const ENTITY_STATES = [...examManagementState];
ExamEditWorkingTimeDialogComponent,
ExamLiveAnnouncementCreateModalComponent,
ExamLiveAnnouncementCreateButtonComponent,
SuspiciousBehaviorComponent,
SuspiciousSessionsOverviewComponent,
PlagiarismCasesOverviewComponent,
SuspiciousSessionsComponent,
StudentExamTimelineComponent,
ProgrammingExerciseExamDiffComponent,
],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,12 @@ <h5 jhiTranslate="artemisApp.examManagement.plagiarismCasesOverview.title"></h5>
</tr>
</thead>
<tbody>
@for (exercise of exercises; track exercise; let i = $index) {
@for (exercise of exercises(); track exercise; let i = $index) {
<tr>
<th scope="row">{{ i + 1 }}</th>
<td>{{ exercise.title }}</td>
<td>{{ plagiarismResultsPerExercise.get(exercise) }}</td>
<td>{{ plagiarismCasesPerExercise.get(exercise) }}</td>
<td>{{ plagiarismResultsPerExercise().get(exercise) }}</td>
<td>{{ plagiarismCasesPerExercise().get(exercise) }}</td>
<td>
<button
id="view-plagiarism-results-btn-{{ i }}"
Expand All @@ -28,7 +28,7 @@ <h5 jhiTranslate="artemisApp.examManagement.plagiarismCasesOverview.title"></h5>
}
</tbody>
</table>
@if (anyPlagiarismCases) {
@if (anyPlagiarismCases()) {
<button
class="btn btn-primary"
id="view-plagiarism-cases-btn"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,28 +1,32 @@
import { Component, Input } from '@angular/core';
import { Component, inject, input } from '@angular/core';
import { Exercise, getExerciseUrlSegment } from 'app/entities/exercise.model';
import { Router } from '@angular/router';
import { TranslateDirective } from 'app/shared/language/translate.directive';

@Component({
selector: 'jhi-plagiarism-cases-overview',
templateUrl: './plagiarism-cases-overview.component.html',
standalone: true,
imports: [TranslateDirective],
})
export class PlagiarismCasesOverviewComponent {
@Input() exercises: Exercise[];
@Input() plagiarismCasesPerExercise: Map<Exercise, number>;
@Input() plagiarismResultsPerExercise: Map<Exercise, number> = new Map<Exercise, number>();
@Input() anyPlagiarismCases = false;
@Input() courseId: number;
@Input() examId: number;
constructor(private router: Router) {}
private router = inject(Router);

exercises = input.required<Exercise[]>();
plagiarismCasesPerExercise = input.required<Map<Exercise, number>>();
plagiarismResultsPerExercise = input.required<Map<Exercise, number>>();
anyPlagiarismCases = input(false);
courseId = input.required<number>();
examId = input.required<number>();

goToPlagiarismDetection(exercise: Exercise) {
const exerciseGroupId = exercise.exerciseGroup?.id;
const exerciseType = exercise.type;
this.router.navigate([
'/course-management',
this.courseId,
this.courseId(),
'exams',
this.examId,
this.examId(),
'exercise-groups',
exerciseGroupId,
getExerciseUrlSegment(exerciseType),
Expand All @@ -31,6 +35,6 @@ export class PlagiarismCasesOverviewComponent {
]);
}
goToPlagiarismCases() {
this.router.navigate(['/course-management', this.courseId, 'exams', this.examId, 'plagiarism-cases']);
this.router.navigate(['/course-management', this.courseId(), 'exams', this.examId(), 'plagiarism-cases']);
}
}
Original file line number Diff line number Diff line change
@@ -1,20 +1,31 @@
import { Component, OnInit, ViewChild } from '@angular/core';
import { Component, OnInit, inject } from '@angular/core';
import { Exercise } from 'app/entities/exercise.model';
import { SuspiciousExamSessions, SuspiciousSessionsAnalysisOptions } from 'app/entities/exam/exam-session.model';
import { SuspiciousSessionsService } from 'app/exam/manage/suspicious-behavior/suspicious-sessions.service';
import { ActivatedRoute, Router } from '@angular/router';
import { PlagiarismCasesService } from 'app/course/plagiarism-cases/shared/plagiarism-cases.service';
import { ExamManagementService } from 'app/exam/manage/exam-management.service';
import { PlagiarismResultsService } from 'app/course/plagiarism-cases/shared/plagiarism-results.service';
import { NgForm } from '@angular/forms';
import { DocumentationType } from 'app/shared/components/documentation-button/documentation-button.component';
import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe';
import { ArtemisSharedComponentModule } from 'app/shared/components/shared-component.module';
import { TranslateDirective } from 'app/shared/language/translate.directive';
import { PlagiarismCasesOverviewComponent } from 'app/exam/manage/suspicious-behavior/plagiarism-cases-overview/plagiarism-cases-overview.component';
import { FormsModule } from '@angular/forms';

@Component({
selector: 'jhi-suspicious-behavior',
templateUrl: './suspicious-behavior.component.html',
standalone: true,
imports: [FormsModule, TranslateDirective, ArtemisTranslatePipe, ArtemisSharedComponentModule, PlagiarismCasesOverviewComponent],
})
export class SuspiciousBehaviorComponent implements OnInit {
@ViewChild('analysis', { static: false }) analysisForm: NgForm;
private suspiciousSessionsService = inject(SuspiciousSessionsService);
private activatedRoute = inject(ActivatedRoute);
private plagiarismCasesService = inject(PlagiarismCasesService);
private examService = inject(ExamManagementService);
private plagiarismResultsService = inject(PlagiarismResultsService);
private router = inject(Router);

exercises: Exercise[] = [];
plagiarismCasesPerExercise: Map<Exercise, number> = new Map<Exercise, number>();
Expand All @@ -40,15 +51,6 @@ export class SuspiciousBehaviorComponent implements OnInit {

readonly documentationType: DocumentationType = 'SuspiciousBehavior';

constructor(
private suspiciousSessionsService: SuspiciousSessionsService,
private activatedRoute: ActivatedRoute,
private plagiarismCasesService: PlagiarismCasesService,
private examService: ExamManagementService,
private plagiarismResultsService: PlagiarismResultsService,
private router: Router,
) {}

ngOnInit(): void {
this.examId = Number(this.activatedRoute.snapshot.paramMap.get('examId'));
this.courseId = Number(this.activatedRoute.snapshot.paramMap.get('courseId'));
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
import { Component, OnInit } from '@angular/core';
import { SuspiciousExamSessions, SuspiciousSessionReason } from 'app/entities/exam/exam-session.model';
import { cloneDeep } from 'lodash-es';
import { SuspiciousSessionsComponent } from 'app/exam/manage/suspicious-behavior/suspicious-sessions/suspicious-sessions.component';
import { TranslateDirective } from 'app/shared/language/translate.directive';
import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe';

@Component({
selector: 'jhi-suspicious-sessions-overview',
templateUrl: './suspicious-sessions-overview.component.html',
styleUrls: ['./suspicious-sessions-overview.component.scss'],
standalone: true,
imports: [SuspiciousSessionsComponent, TranslateDirective, ArtemisTranslatePipe],
})
export class SuspiciousSessionsOverviewComponent implements OnInit {
suspiciousSessions: SuspiciousExamSessions[] = [];
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { HttpClient, HttpParams } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { Injectable, inject } from '@angular/core';
import { SuspiciousExamSessions, SuspiciousSessionsAnalysisOptions } from 'app/entities/exam/exam-session.model';
import { Observable } from 'rxjs';

@Injectable({
providedIn: 'root',
})
export class SuspiciousSessionsService {
constructor(private http: HttpClient) {}
private http = inject(HttpClient);

getSuspiciousSessions(courseId: number, examId: number, options: SuspiciousSessionsAnalysisOptions): Observable<SuspiciousExamSessions[]> {
let params = new HttpParams()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
@for (session of suspiciousSessions.examSessions; track session; let i = $index) {
@for (session of suspiciousSessions().examSessions; track session; let i = $index) {
<tr>
<th scope="row">{{ session.id }}</th>
<td [ngClass]="suspiciousFingerprint ? 'suspicious' : ''">{{ session.browserFingerprintHash }}</td>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,19 @@
import { Component, Input, OnInit } from '@angular/core';
import { Component, OnInit, input } from '@angular/core';
import { SuspiciousExamSessions, SuspiciousSessionReason } from 'app/entities/exam/exam-session.model';
import { StudentExam } from 'app/entities/student-exam.model';
import { ArtemisSharedModule } from 'app/shared/shared.module';

@Component({
// this is intended and an attribute selector because otherwise the rendered table breaks
// eslint-disable-next-line @angular-eslint/component-selector
selector: '[jhi-suspicious-sessions]',
templateUrl: './suspicious-sessions.component.html',
styleUrls: ['./suspicious-sessions.component.scss'],
standalone: true,
imports: [ArtemisSharedModule],
})
export class SuspiciousSessionsComponent implements OnInit {
@Input() suspiciousSessions: SuspiciousExamSessions;
suspiciousSessions = input.required<SuspiciousExamSessions>();
suspiciousFingerprint = false;
suspiciousIpAddress = false;
ngOnInit(): void {
Expand All @@ -31,6 +34,6 @@ export class SuspiciousSessionsComponent implements OnInit {
}

private isSuspiciousFor(reason: SuspiciousSessionReason) {
return this.suspiciousSessions.examSessions.some((session) => session.suspiciousReasons.includes(reason));
return this.suspiciousSessions().examSessions.some((session) => session.suspiciousReasons.includes(reason));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,10 @@ import { ComponentFixture, TestBed } from '@angular/core/testing';
import { Router } from '@angular/router';

import { PlagiarismCasesOverviewComponent } from 'app/exam/manage/suspicious-behavior/plagiarism-cases-overview/plagiarism-cases-overview.component';
import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe';
import { MockPipe } from 'ng-mocks';
import { MockRouter } from '../../../../helpers/mocks/mock-router';
import { Exercise } from 'app/entities/exercise.model';
import { ArtemisTestModule } from '../../../../test.module';

describe('PlagiarismCasesOverviewComponent', () => {
let component: PlagiarismCasesOverviewComponent;
let fixture: ComponentFixture<PlagiarismCasesOverviewComponent>;
let router: Router;
const exercise1 = {
Expand Down Expand Up @@ -40,41 +37,41 @@ describe('PlagiarismCasesOverviewComponent', () => {

beforeEach(() => {
TestBed.configureTestingModule({
declarations: [PlagiarismCasesOverviewComponent, MockPipe(ArtemisTranslatePipe)],
providers: [
{
provide: Router,
useClass: MockRouter,
},
],
imports: [ArtemisTestModule],
});
fixture = TestBed.createComponent(PlagiarismCasesOverviewComponent);
component = fixture.componentInstance;
router = TestBed.inject(Router);
component.courseId = 1;
component.examId = 2;
component.exercises = [exercise1, exercise2];
component.plagiarismCasesPerExercise = new Map([
[exercise1, 0],
[exercise2, 1],
]);
component.plagiarismResultsPerExercise = new Map([
[exercise1, 2],
[exercise2, 4],
]);
fixture.componentRef.setInput('courseId', 1);
fixture.componentRef.setInput('examId', 2);
fixture.componentRef.setInput('exercises', [exercise1, exercise2]);
fixture.componentRef.setInput(
'plagiarismCasesPerExercise',
new Map([
[exercise1, 0],
[exercise2, 1],
]),
);
fixture.componentRef.setInput(
'plagiarismResultsPerExercise',
new Map([
[exercise1, 2],
[exercise2, 4],
]),
);

fixture.detectChanges();
});

it('should navigate to plagiarism cases on view plagiarism cases click', () => {
component.anyPlagiarismCases = true;
fixture.componentRef.setInput('anyPlagiarismCases', true);
fixture.detectChanges();
const viewCasesButton = fixture.debugElement.nativeElement.querySelector('#view-plagiarism-cases-btn');
viewCasesButton.click();
expect(router.navigate).toHaveBeenCalledWith(['/course-management', 1, 'exams', 2, 'plagiarism-cases']);
});

it('should not show view cases button if no cases exist', () => {
component.anyPlagiarismCases = false;
fixture.componentRef.setInput('anyPlagiarismCases', false);
fixture.detectChanges();
const viewCasesButton = fixture.debugElement.nativeElement.querySelector('#view-plagiarism-cases-btn');
expect(viewCasesButton).toBeNull();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,12 @@ import { ArtemisTestModule } from '../../../../test.module';
import { MockComponent, MockModule, MockPipe } from 'ng-mocks';
import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe';
import { PlagiarismCasesOverviewComponent } from 'app/exam/manage/suspicious-behavior/plagiarism-cases-overview/plagiarism-cases-overview.component';
import { ButtonComponent } from 'app/shared/components/button.component';
import { MockRouterLinkDirective } from '../../../../helpers/mocks/directive/mock-router-link.directive';
import { ExamManagementService } from 'app/exam/manage/exam-management.service';
import { Exercise } from 'app/entities/exercise.model';
import { SuspiciousExamSessions, SuspiciousSessionReason } from 'app/entities/exam/exam-session.model';
import { MockRouter } from '../../../../helpers/mocks/mock-router';
import { FormsModule } from '@angular/forms';
import { DocumentationButtonComponent } from 'app/shared/components/documentation-button/documentation-button.component';

describe('SuspiciousBehaviorComponent', () => {
let component: SuspiciousBehaviorComponent;
Expand Down Expand Up @@ -73,13 +71,7 @@ describe('SuspiciousBehaviorComponent', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [ArtemisTestModule, MockRouterLinkDirective, MockModule(FormsModule)],
declarations: [
SuspiciousBehaviorComponent,
MockPipe(ArtemisTranslatePipe),
MockComponent(PlagiarismCasesOverviewComponent),
MockComponent(ButtonComponent),
MockComponent(DocumentationButtonComponent),
],
declarations: [SuspiciousBehaviorComponent, MockPipe(ArtemisTranslatePipe), MockComponent(PlagiarismCasesOverviewComponent)],
providers: [
{ provide: ActivatedRoute, useValue: route },
{ provide: Router, useClass: MockRouter },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe';
import { MockPipe } from 'ng-mocks';
import { StudentExam } from 'app/entities/student-exam.model';
import { SuspiciousExamSessions, SuspiciousSessionReason } from 'app/entities/exam/exam-session.model';
import { ArtemisDatePipe } from 'app/shared/pipes/artemis-date.pipe';
import { ArtemisTestModule } from '../../../../test.module';

describe('SuspiciousSessionsComponent', () => {
Expand Down Expand Up @@ -39,28 +38,28 @@ describe('SuspiciousSessionsComponent', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [ArtemisTestModule],
declarations: [SuspiciousSessionsComponent, MockPipe(ArtemisTranslatePipe), MockPipe(ArtemisDatePipe)],
declarations: [SuspiciousSessionsComponent, MockPipe(ArtemisTranslatePipe)],
});
fixture = TestBed.createComponent(SuspiciousSessionsComponent);
component = fixture.componentInstance;
component.suspiciousSessions = suspiciousSessions1;
fixture.componentRef.setInput('suspiciousSessions', suspiciousSessions1);
});

it('should contain correct link to student exam in table cell', () => {
expect(component.getStudentExamLink(studentExam)).toBe('/course-management/1/exams/1/student-exams/1');
});

it('should correctly determine suspicious reasons', () => {
component.suspiciousSessions = suspiciousSessions1;
fixture.componentRef.setInput('suspiciousSessions', suspiciousSessions1);
component.ngOnInit();
expect(component.suspiciousFingerprint).toBeTrue();
expect(component.suspiciousIpAddress).toBeTrue();

component.suspiciousSessions = suspiciousSessions2;
fixture.componentRef.setInput('suspiciousSessions', suspiciousSessions2);
component.ngOnInit();
expect(component.suspiciousFingerprint).toBeFalse();
expect(component.suspiciousIpAddress).toBeFalse();
component.suspiciousSessions = suspiciousSessions3;
fixture.componentRef.setInput('suspiciousSessions', suspiciousSessions3);
component.ngOnInit();
expect(component.suspiciousFingerprint).toBeFalse();
expect(component.suspiciousIpAddress).toBeTrue();
Expand Down

0 comments on commit 9436717

Please sign in to comment.