Skip to content

Commit

Permalink
Merge branch 'develop' into feature/programming-exercises/editor-reme…
Browse files Browse the repository at this point in the history
…mber-scroll-position
  • Loading branch information
chrisknedl authored Dec 25, 2024
2 parents 574e980 + d8d2239 commit 8b07263
Show file tree
Hide file tree
Showing 19 changed files with 151 additions and 64 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ on:
# Keep in sync with codeql-analysis.yml and test.yml and analysis-of-endpoint-connections.yml
env:
CI: true
node: 20
node: 22
java: 21
RAW_URL: https://raw.githubusercontent.com/${{ github.repository }}/${{ github.sha }}

Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/codeql-analysis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ on:
# Keep in sync with build.yml and test.yml and analysis-of-endpoint-connections.yml
env:
CI: true
node: 20
node: 22
java: 21


Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ concurrency:
# Keep in sync with codeql-analysis.yml and build.yml
env:
CI: true
node: 20
node: 22
java: 21

jobs:
Expand Down
2 changes: 2 additions & 0 deletions docker/playwright-E2E-tests-multi-node.yml
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,8 @@ services:
condition: service_healthy
environment:
PLAYWRIGHT_DB_TYPE: 'MySQL'
networks:
- artemis

networks:
artemis:
Expand Down
1 change: 0 additions & 1 deletion docker/playwright-E2E-tests-mysql-localci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,6 @@ services:
environment:
PLAYWRIGHT_DB_TYPE: 'MySQL'
network_mode: service:artemis-app
networks: !reset []

networks:
artemis:
Expand Down
7 changes: 0 additions & 7 deletions docker/playwright.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,13 +35,6 @@ services:
'
volumes:
- ..:/app/artemis
networks:
- artemis
stdin_open: true
tty: true
ipc: host

networks:
artemis:
driver: 'bridge'
name: artemis
3 changes: 2 additions & 1 deletion docs/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
alabaster==1.0.0
docutils==0.21.2
Jinja2==3.1.5
requests==2.32.3
Sphinx==8.1.3
sphinx-rtd-theme==3.0.2
sphinx-autobuild==2024.10.3
sphinxcontrib-bibtex==2.6.3
urllib3==2.2.3
urllib3==2.3.0
zipp==3.21.0
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ <h4 jhiTranslate="artemisApp.plagiarism.cases.pageSubtitle"></h4>
<button class="btn btn-primary" (click)="exportPlagiarismCases()" jhiTranslate="artemisApp.plagiarism.cases.exportCsv"></button>
</div>
</div>
@for (exercise of exercisesWithPlagiarismCases; track exercise; let i = $index) {
@for (exercise of exercisesWithPlagiarismCases; track exercise.id; let exerciseIndex = $index) {
<div class="card mb-2">
<div class="card-header">
<div class="row">
Expand All @@ -20,6 +20,14 @@ <h5 class="mb-0">
<fa-icon [icon]="getIcon(exercise.type)" />
}
{{ exercise.title }}
<small>
<a
id="plagiarism-detection-link-{{ exercise.id }}"
[routerLink]="['/course-management', courseId, getExerciseUrlSegment(exercise.type), exercise.id, 'plagiarism']"
jhiTranslate="artemisApp.plagiarism.plagiarismCases.viewComparisons"
>
</a>
</small>
</h5>
</div>
<div class="col-3">
Expand Down Expand Up @@ -49,20 +57,25 @@ <h5 class="mb-0">
</div>
</div>
<div class="card-body">
@for (plagiarismCase of groupedPlagiarismCases[exercise!.id!]; track plagiarismCase) {
@for (plagiarismCase of groupedPlagiarismCases[exercise!.id!]; track plagiarismCase; let plagiarismCaseIndex = $index) {
<div class="row mb-3">
<div class="col-1 text-center">
<a [routerLink]="['/course-management', courseId, 'plagiarism-cases', plagiarismCase.id]">
{{ plagiarismCase.student.name }} ({{ plagiarismCase.student.login }})
</a>
</div>
@if (plagiarismCase.student) {
<div class="col-1 text-center">
<a [routerLink]="['/course-management', courseId, 'plagiarism-cases', plagiarismCase.id]">
{{ plagiarismCase.student.name }} ({{ plagiarismCase.student.login }})
</a>
</div>
}
@if (plagiarismCase.plagiarismSubmissions) {
<div
class="col-2 text-center"
jhiTranslate="artemisApp.plagiarism.plagiarismCases.appearsInComparisons"
[translateValues]="{ count: plagiarismCase.plagiarismSubmissions.length }"
></div>
<div class="col-2 text-center">
<span>
{{
'artemisApp.plagiarism.plagiarismCases.appearsInComparisons' | artemisTranslate: { count: plagiarismCase.plagiarismSubmissions?.length }
}}
</span>
</div>
}

@if (plagiarismCase.post) {
<div class="col-3 text-center">
{{ 'artemisApp.plagiarism.plagiarismCases.notifiedAt' | artemisTranslate }} {{ plagiarismCase.post.creationDate | artemisDate }}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,28 @@ import { Component, OnInit } 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';
import { Exercise, getIcon } from 'app/entities/exercise.model';
import { Exercise, getExerciseUrlSegment, getIcon } from 'app/entities/exercise.model';
import { downloadFile } from 'app/shared/util/download.util';
import { DocumentationType } from 'app/shared/components/documentation-button/documentation-button.component';
import { GroupedPlagiarismCases } from 'app/exercises/shared/plagiarism/types/GroupedPlagiarismCase';
import { AlertService } from 'app/core/util/alert.service';

@Component({
selector: 'jhi-plagiarism-cases-instructor-view',
templateUrl: './plagiarism-cases-instructor-view.component.html',
styleUrls: ['./plagiarism-cases-instructor-view.component.scss'],
})
export class PlagiarismCasesInstructorViewComponent implements OnInit {
courseId: number;
examId?: number;
plagiarismCases: PlagiarismCase[] = [];
groupedPlagiarismCases: any; // maybe? { [key: number]: PlagiarismCase[] }
groupedPlagiarismCases: GroupedPlagiarismCases;
exercisesWithPlagiarismCases: Exercise[] = [];

// method called as html template variable, angular only recognises reference variables in html if they are a property
// of the corresponding component class
getExerciseUrlSegment = getExerciseUrlSegment;

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

Expand All @@ -39,31 +45,7 @@ export class PlagiarismCasesInstructorViewComponent implements OnInit {
plagiarismCasesForInstructor$.subscribe({
next: (res: HttpResponse<PlagiarismCase[]>) => {
this.plagiarismCases = res.body!;
this.groupedPlagiarismCases = this.plagiarismCases.reduce(
(
acc: {
[exerciseId: number]: PlagiarismCase[];
},
plagiarismCase,
) => {
const caseExerciseId = plagiarismCase.exercise?.id;
if (caseExerciseId === undefined) {
return acc;
}

// Group initialization
if (!acc[caseExerciseId]) {
acc[caseExerciseId] = [];
this.exercisesWithPlagiarismCases.push(plagiarismCase.exercise!);
}

// Grouping
acc[caseExerciseId].push(plagiarismCase);

return acc;
},
{},
);
this.groupedPlagiarismCases = this.getGroupedPlagiarismCasesByExercise(this.plagiarismCases);
},
});
}
Expand Down Expand Up @@ -185,4 +167,29 @@ export class PlagiarismCasesInstructorViewComponent implements OnInit {
this.alertService.error('artemisApp.plagiarism.plagiarismCases.export.error');
}
}

/**
* groups plagiarism cases by exercise for view
* @param cases to be grouped by exerises
* @private return object containing grouped cases
*/
private getGroupedPlagiarismCasesByExercise(cases: PlagiarismCase[]): GroupedPlagiarismCases {
return cases.reduce((acc: { [exerciseId: number]: PlagiarismCase[] }, plagiarismCase: PlagiarismCase) => {
const caseExerciseId = plagiarismCase.exercise?.id;
if (caseExerciseId === undefined) {
return acc;
}

// Group initialization
if (!acc[caseExerciseId]) {
acc[caseExerciseId] = [];
this.exercisesWithPlagiarismCases.push(plagiarismCase.exercise!);
}

// Grouping
acc[caseExerciseId].push(plagiarismCase);

return acc;
}, {});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { PlagiarismCase } from 'app/exercises/shared/plagiarism/types/PlagiarismCase';

export interface GroupedPlagiarismCases {
[exerciseId: number]: PlagiarismCase[];
}
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,9 @@
<div class="col-12 text-end">
<button type="submit" id="submitButton" [disabled]="!isSubmitPossible" class="btn btn-primary">
<span jhiTranslate="artemisApp.dialogs.addUsers.addUsersForm.addUsersButton"></span>
@if (isLoading()) {
<fa-icon [icon]="faSpinner" animation="spin" class="ms-2" />
}
</button>
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import { Component, EventEmitter, Input, OnChanges, OnInit, Output } from '@angular/core';
import { Component, EventEmitter, Input, OnChanges, OnInit, Output, input } from '@angular/core';
import { UserPublicInfoDTO } from 'app/core/user/user.model';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { ConversationDTO } from 'app/entities/metis/conversation/conversation.model';
import { getAsChannelDTO } from 'app/entities/metis/conversation/channel.model';
import { faSpinner } from '@fortawesome/free-solid-svg-icons';

export interface AddUsersFormData {
selectedUsers?: UserPublicInfoDTO[];
Expand All @@ -24,8 +25,13 @@ export class ConversationAddUsersFormComponent implements OnInit, OnChanges {
@Input()
activeConversation: ConversationDTO;

protected readonly isLoading = input<boolean>(false);

form: FormGroup;

// Icons
protected readonly faSpinner = faSpinner;

getAsChannel = getAsChannelDTO;

mode: 'individual' | 'group' = 'individual';
Expand All @@ -37,8 +43,9 @@ export class ConversationAddUsersFormComponent implements OnInit, OnChanges {

get isSubmitPossible() {
return (
(this.mode === 'individual' && !this.form.invalid) ||
(this.mode === 'group' && (this.form.value?.addAllStudents || this.form.value?.addAllTutors || this.form.value?.addAllInstructors))
!this.isLoading() &&
((this.mode === 'individual' && !this.form.invalid) ||
(this.mode === 'group' && (this.form.value?.addAllStudents || this.form.value?.addAllTutors || this.form.value?.addAllInstructors)))
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ <h4 class="modal-title">
</div>
<div class="modal-body">
<jhi-conversation-add-users-form
[isLoading]="isLoading"
[maxSelectable]="maxSelectable"
[courseId]="course.id!"
(formSubmitted)="onFormSubmitted($event)"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export class ConversationAddUsersDialogComponent extends AbstractDialogComponent

isInitialized = false;
maxSelectable: number | undefined;
protected isLoading: boolean = false;

initialize() {
super.initialize(['course', 'activeConversation']);
Expand Down Expand Up @@ -65,6 +66,8 @@ export class ConversationAddUsersDialogComponent extends AbstractDialogComponent
private addUsers(usersToAdd: UserPublicInfoDTO[], addAllStudents: boolean, addAllTutors: boolean, addAllInstructors: boolean) {
const userLogins = usersToAdd.map((user) => user.login!);

this.isLoading = true;

if (isChannelDTO(this.activeConversation)) {
this.channelService
.registerUsersToChannel(this.course.id!, this.activeConversation.id!, addAllStudents, addAllTutors, addAllInstructors, userLogins)
Expand All @@ -77,6 +80,9 @@ export class ConversationAddUsersDialogComponent extends AbstractDialogComponent
error: (errorResponse: HttpErrorResponse) => {
onError(this.alertService, errorResponse);
},
complete: () => {
this.isLoading = false;
},
});
} else if (isGroupChatDTO(this.activeConversation)) {
this.groupChatService
Expand All @@ -90,6 +96,9 @@ export class ConversationAddUsersDialogComponent extends AbstractDialogComponent
error: (errorResponse: HttpErrorResponse) => {
onError(this.alertService, errorResponse);
},
complete: () => {
this.isLoading = false;
},
});
} else {
throw new Error('Conversation type not supported');
Expand Down
3 changes: 2 additions & 1 deletion src/main/webapp/i18n/de/plagiarism.json
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,8 @@
"noCourseCases": "Keine Plagiatsfälle in diesem Kurs",
"noExamCases": "Keine Plagiatsfälle in dieser Prüfung",
"notifyStudent": "Studierende:n benachrichtigen",
"studentNotified": "Studierende:r wurde benachrichtigt."
"studentNotified": "Studierende:r wurde benachrichtigt.",
"viewComparisons": "Vergleiche ansehen"
},
"cases": {
"pageTitle": "Plagiatsfälle",
Expand Down
3 changes: 2 additions & 1 deletion src/main/webapp/i18n/en/plagiarism.json
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,8 @@
"noCourseCases": "No plagiarism cases in this course",
"noExamCases": "No plagiarism cases in this exam",
"notifyStudent": "Notify student",
"studentNotified": "Student has been notified."
"studentNotified": "Student has been notified.",
"viewComparisons": "View comparisons"
},
"cases": {
"pageTitle": "Plagiarism Cases",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { GroupChatService } from 'app/shared/metis/conversations/group-chat.serv
import { ConversationDTO } from 'app/entities/metis/conversation/conversation.model';
import { Course } from 'app/entities/course.model';
import { generateExampleChannelDTO, generateExampleGroupChatDTO } from '../../helpers/conversationExampleModels';
import { Component, EventEmitter, Input, Output } from '@angular/core';
import { Component, EventEmitter, Input, Output, input } from '@angular/core';
import { AddUsersFormData } from 'app/overview/course-conversations/dialogs/conversation-add-users-dialog/add-users-form/conversation-add-users-form.component';
import { initializeDialog } from '../dialog-test-helpers';
import { ArtemisTranslatePipe } from 'app/shared/pipes/artemis-translate.pipe';
Expand All @@ -29,6 +29,8 @@ class ConversationAddUsersFormStubComponent {
@Input() courseId: number;
@Input() maxSelectable?: number = undefined;

protected readonly isLoading = input<boolean>(false);

@Input()
activeConversation: ConversationDTO;
}
Expand Down
Loading

0 comments on commit 8b07263

Please sign in to comment.