Skip to content

Commit

Permalink
Quiz exercises: Show all participations with filter options
Browse files Browse the repository at this point in the history
  • Loading branch information
krusche committed Nov 25, 2024
1 parent 4dde77f commit 7cad252
Show file tree
Hide file tree
Showing 6 changed files with 137 additions and 119 deletions.

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,6 @@
import de.tum.cit.aet.artemis.core.service.messaging.InstanceMessageSendService;
import de.tum.cit.aet.artemis.core.util.HeaderUtil;
import de.tum.cit.aet.artemis.exercise.domain.Exercise;
import de.tum.cit.aet.artemis.exercise.domain.ExerciseType;
import de.tum.cit.aet.artemis.exercise.domain.InitializationState;
import de.tum.cit.aet.artemis.exercise.domain.Submission;
import de.tum.cit.aet.artemis.exercise.domain.SubmissionType;
Expand Down Expand Up @@ -595,9 +594,7 @@ public ResponseEntity<List<StudentParticipation>> updateParticipationDueDates(@P
}

private Set<StudentParticipation> findParticipationWithLatestResults(Exercise exercise) {
if (exercise.getExerciseType() == ExerciseType.QUIZ) {
return studentParticipationRepository.findByExerciseIdWithLatestAndManualRatedResultsAndAssessmentNote(exercise.getId());
}
// TODO: we should reduce the amount of data fetched here and sent to the client, because submissions and results are not used at all
if (exercise.isTeamMode()) {
// For team exercises the students need to be eagerly fetched
return studentParticipationRepository.findByExerciseIdWithLatestAndManualResultsWithTeamInformation(exercise.getId());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@ default ProgrammingSubmission findFirstByParticipationIdAndCommitHashOrderByIdDe
@Query(value = """
SELECT new de.tum.cit.aet.artemis.programming.dto.ProgrammingSubmissionIdAndSubmissionDateDTO(ps.id, ps.submissionDate)
FROM ProgrammingSubmission ps
WHERE ps.participation.id = :participationId ORDER BY ps.submissionDate DESC
WHERE ps.participation.id = :participationId
ORDER BY ps.submissionDate DESC
""")
List<ProgrammingSubmissionIdAndSubmissionDateDTO> findFirstIdByParticipationIdOrderBySubmissionDateDesc(@Param("participationId") long participationId, Pageable pageable);

Expand All @@ -72,8 +73,8 @@ default Optional<ProgrammingSubmission> findFirstByParticipationIdWithResultsOrd
if (result.isEmpty()) {
return Optional.empty();
}
long id = result.getFirst().programmingSubmissionId();
return findProgrammingSubmissionWithResultsById(id);
long submissionId = result.getFirst().programmingSubmissionId();
return findProgrammingSubmissionWithResultsById(submissionId);
}

@Query("""
Expand Down Expand Up @@ -104,8 +105,7 @@ default Optional<ProgrammingSubmission> findFirstByParticipationIdWithResultsOrd
* @return ProgrammingSubmission list (can be empty!)
*/
default List<ProgrammingSubmission> findGradedByParticipationIdWithResultsOrderBySubmissionDateDesc(long participationId, Pageable pageable) {
List<Long> ids = findSubmissionIdsAndDatesByParticipationId(participationId, pageable).stream().map(ProgrammingSubmissionIdAndSubmissionDateDTO::programmingSubmissionId)
.toList();
var ids = findSubmissionIdsAndDatesByParticipationId(participationId, pageable).stream().map(ProgrammingSubmissionIdAndSubmissionDateDTO::programmingSubmissionId).toList();

if (ids.isEmpty()) {
return Collections.emptyList();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -231,12 +231,12 @@ public ResponseEntity<ProgrammingSubmission> getLatestPendingSubmission(@PathVar
@GetMapping("programming-exercises/{exerciseId}/latest-pending-submissions")
@EnforceAtLeastTutor
public ResponseEntity<Map<Long, Optional<ProgrammingSubmission>>> getLatestPendingSubmissionsByExerciseId(@PathVariable Long exerciseId) {
ProgrammingExercise programmingExercise;
programmingExercise = programmingExerciseRepository.findByIdWithTemplateAndSolutionParticipationElseThrow(exerciseId);
ProgrammingExercise programmingExercise = programmingExerciseRepository.findByIdWithTemplateAndSolutionParticipationElseThrow(exerciseId);

if (!authCheckService.isAtLeastTeachingAssistantForExercise(programmingExercise)) {
throw new AccessForbiddenException("exercise", exerciseId);
}
// TODO: this REST call is quite slow for > 100 participations. We should consider a more efficient way to get the latest pending submissions.
Map<Long, Optional<ProgrammingSubmission>> pendingSubmissions = submissionService.getLatestPendingSubmissionsForProgrammingExercise(exerciseId);
// Remove unnecessary data to make response smaller (exercise, student of participation).
pendingSubmissions = pendingSubmissions.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, entry -> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,38 +4,36 @@
<h2>
<span>{{ exercise?.title }} - </span>{{ filteredParticipationsSize }} <span jhiTranslate="artemisApp.participation.home.title"></span>
</h2>
@if (exercise?.type === ExerciseType.PROGRAMMING) {
<div class="d-flex align-items-center mt-2">
<label class="radio-inline mb-0 d-flex align-items-center">
<input type="radio" [ngModel]="participationCriteria.filterProp" (click)="updateParticipationFilter(FilterProp.ALL)" [value]="FilterProp.ALL" />
<span class="ms-1" jhiTranslate="artemisApp.exercise.showAll"></span>
</label>
<label class="radio-inline ms-2 mb-0 d-flex align-items-center">
<input type="radio" [ngModel]="participationCriteria.filterProp" (click)="updateParticipationFilter(FilterProp.FAILED)" [value]="FilterProp.FAILED" />
<span class="ms-1" jhiTranslate="artemisApp.exercise.showFailed"></span>
</label>
<div class="d-flex align-items-center mt-2">
<label class="radio-inline mb-0 d-flex align-items-center">
<input type="radio" [ngModel]="participationCriteria.filterProp" (click)="updateParticipationFilter(FilterProp.ALL)" [value]="FilterProp.ALL" />
<span class="ms-1" jhiTranslate="artemisApp.exercise.showAll"></span>
</label>
<label class="radio-inline ms-2 mb-0 d-flex align-items-center">
<input type="radio" [ngModel]="participationCriteria.filterProp" (click)="updateParticipationFilter(FilterProp.FAILED)" [value]="FilterProp.FAILED" />
<span class="ms-1" jhiTranslate="artemisApp.exercise.showFailed"></span>
</label>
<label class="radio-inline ms-2 mb-0 d-flex align-items-center">
<input
type="radio"
[ngModel]="participationCriteria.filterProp"
(click)="updateParticipationFilter(FilterProp.NO_SUBMISSIONS)"
[value]="FilterProp.NO_SUBMISSIONS"
/>
<span class="ms-1" jhiTranslate="artemisApp.exercise.showNoSubmissions"></span>
</label>
@if (exercise.type === ExerciseType.PROGRAMMING && afterDueDate) {
<label class="radio-inline ms-2 mb-0 d-flex align-items-center">
<input
type="radio"
[ngModel]="participationCriteria.filterProp"
(click)="updateParticipationFilter(FilterProp.NO_SUBMISSIONS)"
[value]="FilterProp.NO_SUBMISSIONS"
(click)="updateParticipationFilter(FilterProp.NO_PRACTICE)"
[value]="FilterProp.NO_PRACTICE"
/>
<span class="ms-1" jhiTranslate="artemisApp.exercise.showNoSubmissions"></span>
<span class="ms-1" jhiTranslate="artemisApp.exercise.showNoPracticeMode"></span>
</label>
@if (exercise.type === ExerciseType.PROGRAMMING && afterDueDate) {
<label class="radio-inline ms-2 mb-0 d-flex align-items-center">
<input
type="radio"
[ngModel]="participationCriteria.filterProp"
(click)="updateParticipationFilter(FilterProp.NO_PRACTICE)"
[value]="FilterProp.NO_PRACTICE"
/>
<span class="ms-1" jhiTranslate="artemisApp.exercise.showNoPracticeMode"></span>
</label>
}
</div>
}
}
</div>
</div>
@if (exercise?.type !== ExerciseType.QUIZ && exercise?.isAtLeastInstructor) {
<div class="p-2">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -916,7 +916,8 @@ void getAllParticipationsForExercise_withLatestResults_forQuizExercise() throws
courseRepository.save(course);
exerciseRepository.save(quizExercise);

var participation = participationUtilService.createAndSaveParticipationForExercise(quizExercise, TEST_PREFIX + "student1");
final var login = TEST_PREFIX + "student1";
var participation = participationUtilService.createAndSaveParticipationForExercise(quizExercise, login);
var result1 = participationUtilService.createSubmissionAndResult(participation, 42, true);
var notGradedResult = participationUtilService.addResultToParticipation(participation, result1.getSubmission());
notGradedResult.setRated(false);
Expand All @@ -926,11 +927,10 @@ void getAllParticipationsForExercise_withLatestResults_forQuizExercise() throws
params.add("withLatestResults", "true");
var participations = request.getList("/api/exercises/" + quizExercise.getId() + "/participations", HttpStatus.OK, StudentParticipation.class, params);

var receivedParticipationWithResult = participations.stream().filter(p -> ((User) p.getParticipant()).getLogin().equals(TEST_PREFIX + "student1")).findFirst()
.orElseThrow();
assertThat(receivedParticipationWithResult.getResults()).containsOnly(result1);
assertThat(receivedParticipationWithResult.getSubmissions()).isEmpty();
assertThat(receivedParticipationWithResult.getSubmissionCount()).isEqualTo(1);
var receivedParticipation = participations.stream().filter(p -> p.getParticipantIdentifier().equals(login)).findFirst().orElseThrow();
assertThat(receivedParticipation.getResults()).containsOnly(notGradedResult);
assertThat(receivedParticipation.getSubmissions()).isEmpty();
assertThat(receivedParticipation.getSubmissionCount()).isEqualTo(1);
}

@Test
Expand Down

0 comments on commit 7cad252

Please sign in to comment.