Skip to content

Commit

Permalink
General: Add deletion of multiple exercises (#7213)
Browse files Browse the repository at this point in the history
  • Loading branch information
milljoniaer authored Sep 22, 2023
1 parent 5ba2676 commit 92fbaf3
Show file tree
Hide file tree
Showing 15 changed files with 243 additions and 55 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
<table class="table table-striped">
<thead>
<tr jhiSort [(predicate)]="predicate" [(ascending)]="reverse" (sortChange)="sortRows()">
<th *ngIf="course.isAtLeastInstructor" class="d-md-table-cell">
<input class="form-check-input" type="checkbox" (change)="toggleMultipleExercises(fileUploadExercises)" />
</th>
<th jhiSortBy="id"><span jhiTranslate="global.field.id">ID</span>&nbsp;<fa-icon [icon]="faSort"></fa-icon></th>
<th jhiSortBy="title"><span jhiTranslate="artemisApp.exercise.title">Title</span>&nbsp;<fa-icon [icon]="faSort"></fa-icon></th>
<th jhiSortBy="releaseDate"><span jhiTranslate="artemisApp.exercise.release">Release</span>&nbsp;<fa-icon [icon]="faSort"></fa-icon></th>
Expand All @@ -20,6 +23,9 @@
</thead>
<tbody class="markdown-preview">
<tr *ngFor="let fileUploadExercise of filteredFileUploadExercises; trackBy: trackId" id="exercise-card-{{ fileUploadExercise.id }}">
<td *ngIf="course.isAtLeastInstructor" class="d-md-table-cell">
<input class="form-check-input" type="checkbox" (change)="toggleExercise(fileUploadExercise)" [ngModel]="isExerciseSelected(fileUploadExercise)" />
</td>
<td>
<a *ngIf="fileUploadExercise.isAtLeastEditor; else showId" [routerLink]="['/course-management', courseId, 'file-upload-exercises', fileUploadExercise.id]">
{{ fileUploadExercise.id }}
Expand Down Expand Up @@ -92,4 +98,18 @@
</tr>
</tbody>
</table>
<div *ngIf="selectedExercises.length > 0">
<button
*ngIf="course.isAtLeastInstructor"
jhiDeleteButton
[entityTitle]="'Delete All File Upload Exercises'"
deleteQuestion="artemisApp.exerciseActions.deleteMultipleExercisesQuestion"
(delete)="deleteMultipleExercises(selectedExercises, fileUploadExerciseService)"
[dialogError]="dialogError$"
id="delete-all-quiz"
class="mb-1"
>
<fa-icon [icon]="faTimes"></fa-icon>
</button>
</div>
</div>
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export class FileUploadExerciseComponent extends ExerciseComponent {

constructor(
public exerciseService: ExerciseService,
private fileUploadExerciseService: FileUploadExerciseService,
public fileUploadExerciseService: FileUploadExerciseService,
private courseExerciseService: CourseExerciseService,
private alertService: AlertService,
private accountService: AccountService,
Expand All @@ -64,6 +64,7 @@ export class FileUploadExerciseComponent extends ExerciseComponent {
this.fileUploadExercises.forEach((exercise) => {
exercise.course = this.course;
this.accountService.setAccessRightsForExercise(exercise);
this.selectedExercises = [];
});
this.emitExerciseCount(this.fileUploadExercises.length);
this.applyFilter();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
<table class="table table-striped">
<thead>
<tr jhiSort [(predicate)]="predicate" [(ascending)]="reverse" (sortChange)="sortRows()">
<th *ngIf="course.isAtLeastInstructor" class="d-md-table-cell">
<input class="form-check-input" type="checkbox" (change)="toggleMultipleExercises(modelingExercises)" />
</th>
<th jhiSortBy="id"><span jhiTranslate="global.field.id">ID</span>&nbsp;<fa-icon [icon]="faSort"></fa-icon></th>
<th jhiSortBy="title"><span jhiTranslate="artemisApp.exercise.title">Title</span>&nbsp;<fa-icon [icon]="faSort"></fa-icon></th>
<th jhiSortBy="releaseDate"><span jhiTranslate="artemisApp.exercise.release">Release</span>&nbsp;<fa-icon [icon]="faSort"></fa-icon></th>
Expand All @@ -20,6 +23,9 @@
</thead>
<tbody>
<tr *ngFor="let modelingExercise of filteredModelingExercises; trackBy: trackId" id="{{ 'exercise-card-' + modelingExercise.id }}">
<td *ngIf="course.isAtLeastInstructor" class="d-md-table-cell">
<input class="form-check-input" type="checkbox" (change)="toggleExercise(modelingExercise)" [ngModel]="isExerciseSelected(modelingExercise)" />
</td>
<td>
<a
*ngIf="modelingExercise.isAtLeastEditor; else showId"
Expand Down Expand Up @@ -112,4 +118,18 @@
</tr>
</tbody>
</table>
<div *ngIf="selectedExercises.length > 0">
<button
*ngIf="course.isAtLeastInstructor"
jhiDeleteButton
[entityTitle]="'Delete All Modeling Exercises'"
deleteQuestion="artemisApp.exerciseActions.deleteMultipleExercisesQuestion"
(delete)="deleteMultipleExercises(selectedExercises, modelingExerciseService)"
[dialogError]="dialogError$"
id="delete-all-quiz"
class="mb-1"
>
<fa-icon [icon]="faTimes"></fa-icon>
</button>
</div>
</div>
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export class ModelingExerciseComponent extends ExerciseComponent {

constructor(
public exerciseService: ExerciseService,
private modelingExerciseService: ModelingExerciseService,
public modelingExerciseService: ModelingExerciseService,
private courseExerciseService: CourseExerciseService,
private alertService: AlertService,
private accountService: AccountService,
Expand All @@ -60,6 +60,7 @@ export class ModelingExerciseComponent extends ExerciseComponent {
this.modelingExercises.forEach((exercise) => {
exercise.course = this.course;
this.accountService.setAccessRightsForExercise(exercise);
this.selectedExercises = [];
});
this.applyFilter();
this.emitExerciseCount(this.modelingExercises.length);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
<thead>
<tr jhiSort [(predicate)]="predicate" [(ascending)]="reverse" (sortChange)="sortRows()">
<th *ngIf="course.isAtLeastEditor" class="d-md-table-cell">
<input class="form-check-input" type="checkbox" (change)="toggleAllProgrammingExercises()" />
<input class="form-check-input" type="checkbox" (change)="toggleMultipleExercises(programmingExercises)" />
</th>
<th class="d-md-table-cell" jhiSortBy="id"><span jhiTranslate="global.field.id">ID</span>&nbsp;<fa-icon [icon]="faSort"></fa-icon></th>
<th jhiSortBy="title"><span jhiTranslate="artemisApp.exercise.title">Title</span>&nbsp;<fa-icon [icon]="faSort"></fa-icon></th>
Expand All @@ -29,7 +29,7 @@
<tbody>
<tr *ngFor="let programmingExercise of filteredProgrammingExercises; trackBy: trackId" id="exercise-card-{{ programmingExercise.id }}">
<td *ngIf="programmingExercise.isAtLeastEditor" class="d-md-table-cell">
<input class="form-check-input" type="checkbox" (change)="toggleProgrammingExercise(programmingExercise)" [ngModel]="isExerciseSelected(programmingExercise)" />
<input class="form-check-input" type="checkbox" (change)="toggleExercise(programmingExercise)" [ngModel]="isExerciseSelected(programmingExercise)" />
</td>
<td class="d-md-table-cell">
<a *ngIf="programmingExercise.isAtLeastEditor; else showId" [routerLink]="['/course-management', courseId, 'programming-exercises', programmingExercise.id]">
Expand Down Expand Up @@ -251,20 +251,34 @@
</tr>
</tbody>
</table>
<div *ngIf="selectedProgrammingExercises.length > 0">
<div *ngIf="selectedExercises.length > 0">
<button type="submit" (click)="openEditSelectedModal()" class="btn btn-warning btn-sm me-1">
<fa-icon [icon]="faWrench"></fa-icon>
<span class="d-none d-md-inline" jhiTranslate="entity.action.editSelected">Edit selected</span>
</button>
<jhi-programming-assessment-repo-export
*ngIf="course.isAtLeastInstructor"
[programmingExercises]="selectedProgrammingExercises"
class="me-1"
></jhi-programming-assessment-repo-export>
<jhi-exercise-scores-export-button *ngIf="course.isAtLeastInstructor" [exercises]="selectedProgrammingExercises"></jhi-exercise-scores-export-button>
<jhi-programming-assessment-repo-export *ngIf="course.isAtLeastInstructor" [programmingExercises]="selectedExercises" class="me-1"></jhi-programming-assessment-repo-export>
<jhi-exercise-scores-export-button *ngIf="course.isAtLeastInstructor" [exercises]="selectedExercises"></jhi-exercise-scores-export-button>
<button *ngIf="course.isAtLeastInstructor" (click)="checkConsistencies()" class="btn btn-outline-primary btn-sm me-1">
<fa-icon [icon]="faCheckDouble"></fa-icon>
<span jhiTranslate="artemisApp.consistencyCheck.button">Check consistency</span>
</button>
<button
*ngIf="course.isAtLeastInstructor"
[jhiFeatureToggle]="FeatureToggle.ProgrammingExercises"
jhiDeleteButton
[entityTitle]="'Multiple Programming Exercises'"
deleteQuestion="artemisApp.exerciseActions.deleteMultipleExercisesQuestion"
(delete)="deleteMultipleProgrammingExercises(selectedExercises, $event)"
class="me-1"
id="delete-exercises"
[dialogError]="dialogError$"
[requireConfirmationOnlyForAdditionalChecks]="true"
[additionalChecks]="{
deleteStudentReposBuildPlans: 'artemisApp.programmingExercise.delete.studentReposBuildPlans',
deleteBaseReposBuildPlans: 'artemisApp.programmingExercise.delete.baseReposBuildPlans'
}"
>
<fa-icon [icon]="faTimes"></fa-icon>
</button>
</div>
</div>
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Component, ContentChild, Input, OnDestroy, OnInit, TemplateRef } from '@angular/core';
import { HttpErrorResponse, HttpResponse } from '@angular/common/http';
import { merge } from 'rxjs';
import { ProgrammingExercise } from 'app/entities/programming-exercise.model';
import { ProgrammingExerciseInstructorRepositoryType, ProgrammingExerciseService } from './services/programming-exercise.service';
import { ActivatedRoute, Router } from '@angular/router';
Expand Down Expand Up @@ -46,12 +47,10 @@ import { PROFILE_LOCALVC } from 'app/app.constants';
export class ProgrammingExerciseComponent extends ExerciseComponent implements OnInit, OnDestroy {
@Input() programmingExercises: ProgrammingExercise[];
filteredProgrammingExercises: ProgrammingExercise[];
selectedProgrammingExercises: ProgrammingExercise[];
readonly ActionType = ActionType;
FeatureToggle = FeatureToggle;
solutionParticipationType = ProgrammingExerciseParticipationType.SOLUTION;
templateParticipationType = ProgrammingExerciseParticipationType.TEMPLATE;
allChecked = false;
// Used to make the repository links download the repositories instead of linking to Bitbucket/GitLab.
localVCEnabled = false;

Expand Down Expand Up @@ -92,7 +91,6 @@ export class ProgrammingExerciseComponent extends ExerciseComponent implements O
) {
super(courseService, translateService, route, eventManager);
this.programmingExercises = [];
this.selectedProgrammingExercises = [];
}

ngOnInit(): void {
Expand Down Expand Up @@ -127,7 +125,7 @@ export class ProgrammingExerciseComponent extends ExerciseComponent implements O
);
}
}
this.selectedProgrammingExercises = [];
this.selectedExercises = [];
});
this.applyFilter();
this.emitExerciseCount(this.programmingExercises.length);
Expand Down Expand Up @@ -163,6 +161,27 @@ export class ProgrammingExerciseComponent extends ExerciseComponent implements O
});
}

/**
* Deletes all the given programming exercises
* @param exercisesToDelete the exercise objects which are to be deleted
* @param event contains additional checks which are performed for all these exercises
*/
deleteMultipleProgrammingExercises(exercisesToDelete: ProgrammingExercise[], event: { [key: string]: boolean }) {
const deletionObservables = exercisesToDelete.map((exercise) =>
this.programmingExerciseService.delete(exercise.id!, event.deleteStudentReposBuildPlans, event.deleteBaseReposBuildPlans),
);
return merge(...deletionObservables).subscribe({
next: () => {
this.eventManager.broadcast({
name: 'programmingExerciseListModification',
content: 'Deleted selected programmingExercises',
});
this.dialogErrorSource.next('');
},
error: (error: HttpErrorResponse) => this.dialogErrorSource.next(error.message),
});
}

protected getChangeEventName(): string {
return 'programmingExerciseListModification';
}
Expand All @@ -172,33 +191,12 @@ export class ProgrammingExerciseComponent extends ExerciseComponent implements O
this.applyFilter();
}

toggleProgrammingExercise(programmingExercise: ProgrammingExercise) {
const programmingExerciseIndex = this.selectedProgrammingExercises.indexOf(programmingExercise);
if (programmingExerciseIndex !== -1) {
this.selectedProgrammingExercises.splice(programmingExerciseIndex, 1);
} else {
this.selectedProgrammingExercises.push(programmingExercise);
}
}

toggleAllProgrammingExercises() {
this.selectedProgrammingExercises = [];
if (!this.allChecked) {
this.selectedProgrammingExercises = this.selectedProgrammingExercises.concat(this.programmingExercises);
}
this.allChecked = !this.allChecked;
}

isExerciseSelected(programmingExercise: ProgrammingExercise) {
return this.selectedProgrammingExercises.includes(programmingExercise);
}

openEditSelectedModal() {
const modalRef = this.modalService.open(ProgrammingExerciseEditSelectedComponent, {
size: 'xl',
backdrop: 'static',
});
modalRef.componentInstance.selectedProgrammingExercises = this.selectedProgrammingExercises;
modalRef.componentInstance.selectedProgrammingExercises = this.selectedExercises;
modalRef.closed.subscribe(() => {
location.reload();
});
Expand All @@ -209,7 +207,7 @@ export class ProgrammingExerciseComponent extends ExerciseComponent implements O
*/
checkConsistencies() {
const modalRef = this.modalService.open(ConsistencyCheckComponent, { keyboard: true, size: 'lg' });
modalRef.componentInstance.exercisesToCheck = this.selectedProgrammingExercises;
modalRef.componentInstance.exercisesToCheck = this.selectedExercises;
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
<table class="table table-striped">
<thead>
<tr jhiSort [(predicate)]="predicate" [(ascending)]="reverse" (sortChange)="sortRows()">
<th *ngIf="course.isAtLeastInstructor" class="d-md-table-cell">
<input class="form-check-input" type="checkbox" (change)="toggleMultipleExercises(quizExercises)" />
</th>
<th jhiSortBy="id"><span jhiTranslate="global.field.id">ID</span>&nbsp;<fa-icon [icon]="faSort"></fa-icon></th>
<th jhiSortBy="title"><span jhiTranslate="artemisApp.exercise.title">Title</span>&nbsp;<fa-icon [icon]="faSort"></fa-icon></th>
<th jhiSortBy="status"><span jhiTranslate="artemisApp.quizExercise.status">Status</span>&nbsp;<fa-icon [icon]="faSort"></fa-icon></th>
Expand All @@ -20,6 +23,9 @@
</thead>
<tbody>
<tr *ngFor="let quizExercise of filteredQuizExercises; trackBy: trackId" id="exercise-card-{{ quizExercise.id }}">
<td *ngIf="quizExercise.isAtLeastInstructor" class="d-md-table-cell">
<input class="form-check-input" type="checkbox" (change)="toggleExercise(quizExercise)" [ngModel]="isExerciseSelected(quizExercise)" />
</td>
<td *ngIf="!quizExercise.quizEnded || !quizExercise.isAtLeastEditor">
<a *ngIf="quizExercise.isEditable; else readOnlyId" [routerLink]="['/course-management', quizExercise.course?.id, 'quiz-exercises', quizExercise.id, 'edit']"
>{{ quizExercise.id }}
Expand Down Expand Up @@ -239,4 +245,18 @@
</tr>
</tbody>
</table>
<div *ngIf="selectedExercises.length > 0">
<button
*ngIf="course.isAtLeastInstructor"
jhiDeleteButton
[entityTitle]="'Delete All Quiz Exercises'"
deleteQuestion="artemisApp.exerciseActions.deleteMultipleExercisesQuestion"
(delete)="deleteMultipleExercises(selectedExercises, quizExerciseService)"
[dialogError]="dialogError$"
id="delete-all-quiz"
class="mb-1"
>
<fa-icon [icon]="faTimes"></fa-icon>
</button>
</div>
</div>
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export class QuizExerciseComponent extends ExerciseComponent {
faStopCircle = faStopCircle;

constructor(
private quizExerciseService: QuizExerciseService,
public quizExerciseService: QuizExerciseService,
private accountService: AccountService,
private alertService: AlertService,
private modalService: NgbModal,
Expand All @@ -68,6 +68,7 @@ export class QuizExerciseComponent extends ExerciseComponent {
exercise.isAtLeastInstructor = this.accountService.isAtLeastInstructorInCourse(exercise.course);
exercise.quizBatches = exercise.quizBatches?.sort((a, b) => (a.id ?? 0) - (b.id ?? 0));
exercise.isEditable = isQuizEditable(exercise);
this.selectedExercises = [];
});
this.setQuizExercisesStatus();
this.emitExerciseCount(this.quizExercises.length);
Expand Down
Loading

0 comments on commit 92fbaf3

Please sign in to comment.