Skip to content

Tutor exam dashboard for the assessment of exams #1727

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

Merged
merged 79 commits into from
Jul 4, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
79 commits
Select commit Hold shift + click to select a range
046e031
Adjust CourseResource and CourseService to handle exam-dashboard-requ…
JonasPetry Jun 27, 2020
e6fbb11
Adjust server to initialize exam-dashboard for one specific exam
JonasPetry Jun 27, 2020
6a89be4
Link tutor-course-dashboard.component.ts for exam in the client
JonasPetry Jun 27, 2020
606f68c
Adjust back link in tutor-course-dashboard.component.ts depending on …
JonasPetry Jun 27, 2020
47e3b8c
Add missing javadoc to CourseService and CourseResource
JonasPetry Jun 27, 2020
fdfea88
Only get interesting exercises for the dashboard if the end date of t…
JonasPetry Jun 29, 2020
2b28c6c
Merge branch 'develop' into exam-mode/variants/tutor-dashboard
JonasPetry Jun 29, 2020
2c34ac6
Adjust page title of tutor exam dashboard in exam-management.route.ts
JonasPetry Jun 29, 2020
bcfaa32
Add exam case distinction to text exercise assessment
JonasPetry Jun 29, 2020
221ce29
Adjust CourseResource and CourseService to handle exam-dashboard-requ…
JonasPetry Jun 27, 2020
1399c91
Adjust server to initialize exam-dashboard for one specific exam
JonasPetry Jun 27, 2020
d20ab0d
Link tutor-course-dashboard.component.ts for exam in the client
JonasPetry Jun 27, 2020
6d883b8
Adjust back link in tutor-course-dashboard.component.ts depending on …
JonasPetry Jun 27, 2020
08eb362
Add missing javadoc to CourseService and CourseResource
JonasPetry Jun 27, 2020
cf0ab35
Only get interesting exercises for the dashboard if the end date of t…
JonasPetry Jun 29, 2020
85a7367
Adjust page title of tutor exam dashboard in exam-management.route.ts
JonasPetry Jun 29, 2020
eaa72eb
Add exam case distinction to text exercise assessment
JonasPetry Jun 29, 2020
3b355a4
Merge remote-tracking branch 'origin/exam-mode/variants/tutor-dashboa…
JonasPetry Jun 29, 2020
f7450c5
Merge branch 'develop' into exam-mode/variants/tutor-dashboard
JonasPetry Jun 29, 2020
6f041a8
Update debug message in CourseResource
JonasPetry Jun 29, 2020
aa93b63
Remove unnecessary debug message
JonasPetry Jun 29, 2020
a9da60a
Add createCourseWithExamAndExerciseGroupAndExercises() to DatabaseUti…
JonasPetry Jun 29, 2020
4a84244
Add createCourseWithExamAndExerciseGroupAndExercises() to DatabaseUti…
JonasPetry Jun 29, 2020
1afd7df
Add first integration test for course exam dashboard
JonasPetry Jun 29, 2020
2f11085
Merge branch 'develop' into exam-mode/variants/tutor-dashboard
JonasPetry Jun 29, 2020
c898301
Adjust links in text exercise assessment
JonasPetry Jun 29, 2020
bd95961
Merge branch 'develop' into exam-mode/variants/tutor-dashboard
JonasPetry Jun 30, 2020
2f207c6
Merge branch 'develop' into exam-mode/variants/tutor-dashboard
JonasPetry Jun 30, 2020
7e3e88d
Adjust links in text exercise assessment
JonasPetry Jun 30, 2020
e802faa
Fix buttons in tutor-exercise-dashboard.component.ts
JonasPetry Jun 30, 2020
ffd9f93
Fix back button in text exercise assessment
JonasPetry Jun 30, 2020
8a7a5fd
Merge branch 'develop' into exam-mode/variants/tutor-dashboard
JonasPetry Jun 30, 2020
d0eff6a
Merge branch 'develop' into exam-mode/variants/tutor-dashboard
JonasPetry Jun 30, 2020
4b542de
Merge branch 'develop' into exam-mode/variants/tutor-dashboard
JonasPetry Jun 30, 2020
ebb385f
Merge branch 'develop' into exam-mode/variants/tutor-dashboard
JonasPetry Jul 1, 2020
d539075
Remove unused parameter in tutor-exercise-dashboard.component.ts
JonasPetry Jul 1, 2020
73660aa
Update documentation
JonasPetry Jul 1, 2020
7892991
Restore integration test that got lost during merge
JonasPetry Jul 1, 2020
fb70cdc
Add todo for correct status calculation
JonasPetry Jul 1, 2020
634f3ca
Add more test cases for CourseIntegrationTest
JonasPetry Jul 1, 2020
3e3fd73
Add more integration tests and remove todo
JonasPetry Jul 1, 2020
105270e
Merge branch 'develop' into exam-mode/variants/tutor-dashboard
JonasPetry Jul 1, 2020
0dd87fc
Implement feedback on pull request
JonasPetry Jul 1, 2020
4768cbb
Extract method to remove duplicated code
JonasPetry Jul 1, 2020
2dba396
Merge branch 'develop' into exam-mode/variants/tutor-dashboard
JonasPetry Jul 1, 2020
9e69e00
Fix typescript error
JonasPetry Jul 1, 2020
ad10812
Try to fix failing test case
JonasPetry Jul 1, 2020
dfcbaef
Update unclear method name and documentation in CourseService
JonasPetry Jul 2, 2020
b53b2ce
small improvements
Jul 2, 2020
40ee79f
some adaptions regarding the population of exercises
Jul 2, 2020
963ad89
Merge branch 'develop' into exam-mode/variants/tutor-dashboard
JonasPetry Jul 2, 2020
5aee45a
Load exercises through exam and not through course
JonasPetry Jul 2, 2020
07ffdff
Rename test cases
JonasPetry Jul 2, 2020
06b4d8b
Make tutor-course-dashboard work
JonasPetry Jul 2, 2020
774c736
Add ngIf to unused heading
JonasPetry Jul 2, 2020
a6fb2ee
Merge develop and resolve conflicts
kloessst Jul 3, 2020
e082e6d
Check route consistency and only prepare interesting exercises for as…
kloessst Jul 3, 2020
e69254e
Convert date from server
kloessst Jul 3, 2020
ce35ba8
Improve loop with reduce
kloessst Jul 3, 2020
e672b2e
Simplify conditional
kloessst Jul 3, 2020
145ac47
Revert reduce loop
kloessst Jul 3, 2020
9fac49b
Small improvement
kloessst Jul 3, 2020
2e5c067
Move REST call to the right service
kloessst Jul 3, 2020
d28faa3
Improve doc
kloessst Jul 3, 2020
a4764d1
TS style
kloessst Jul 3, 2020
ad9d6de
Remove unnecessary wrapper and try to fix serialization
kloessst Jul 3, 2020
a0bb6fd
Disable eslint no-unused-expression
kloessst Jul 3, 2020
8c7e9ae
refactored duplicated code
Jul 3, 2020
2033472
add default constructor
Jul 3, 2020
d850bd3
Merge branch 'exam-mode/variants/tutor-dashboard' of https://github.c…
kloessst Jul 3, 2020
f2e7743
allowSetters is the key to the universe
Jul 3, 2020
9559827
Merge branch 'exam-mode/variants/tutor-dashboard' of https://github.c…
Jul 3, 2020
46a2244
Deactivate test. Thanks Jackson!
kloessst Jul 3, 2020
40f722e
Add doc
kloessst Jul 3, 2020
4a3dc89
Merge branch 'develop' into exam-mode/variants/tutor-dashboard
Jul 4, 2020
32a2635
remove unused imports
Jul 4, 2020
dc086cd
Merge branch 'develop' into exam-mode/variants/tutor-dashboard
Jul 4, 2020
8852d62
fix disabled test with correct annotation @JsonIgnoreProperties(value…
Jul 4, 2020
22f43d3
avoid duplicated code and improve code understanding
Jul 4, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 0 additions & 9 deletions src/main/java/de/tum/in/www1/artemis/domain/Course.java
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
package de.tum.in.www1.artemis.domain;

import static de.tum.in.www1.artemis.config.Constants.ARTEMIS_GROUP_DEFAULT_PREFIX;
import static de.tum.in.www1.artemis.domain.enumeration.AssessmentType.AUTOMATIC;

import java.io.Serializable;
import java.time.ZonedDateTime;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

import javax.persistence.CascadeType;
import javax.persistence.Column;
Expand All @@ -34,7 +32,6 @@

import de.tum.in.www1.artemis.config.Constants;
import de.tum.in.www1.artemis.domain.exam.Exam;
import de.tum.in.www1.artemis.domain.modeling.ModelingExercise;
import de.tum.in.www1.artemis.domain.view.QuizView;
import de.tum.in.www1.artemis.service.FilePathService;
import de.tum.in.www1.artemis.service.FileService;
Expand Down Expand Up @@ -580,12 +577,6 @@ public String toString() {
+ ", presentationScore='" + getPresentationScore() + "}";
}

@JsonIgnore
public Set<Exercise> getInterestingExercisesForAssessmentDashboards() {
return getExercises().stream().filter(exercise -> exercise instanceof TextExercise || exercise instanceof ModelingExercise || exercise instanceof FileUploadExercise
|| (exercise instanceof ProgrammingExercise && exercise.getAssessmentType() != AUTOMATIC)).collect(Collectors.toSet());
}

public void setNumberOfInstructors(Long numberOfInstructors) {
this.numberOfInstructorsTransient = numberOfInstructors;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,6 @@ public abstract class Exercise implements Serializable {

@ManyToOne
@JsonView(QuizView.Before.class)
@JsonIgnoreProperties(value = "exercises")
private ExerciseGroup exerciseGroup;

@OneToMany(mappedBy = "exercise", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY)
Expand Down
6 changes: 3 additions & 3 deletions src/main/java/de/tum/in/www1/artemis/domain/exam/Exam.java
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ public class Exam implements Serializable {
@OneToMany(mappedBy = "exam", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY)
@OrderColumn(name = "exercise_group_order")
@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
@JsonIgnoreProperties("exam")
@JsonIgnoreProperties(value = "exam", allowSetters = true)
private List<ExerciseGroup> exerciseGroups = new ArrayList<>();

// TODO: add a big fat warning in case instructor delete exams where student exams already exist
Expand Down Expand Up @@ -253,12 +253,12 @@ public void setRegisteredUsers(Set<User> registeredUsers) {
this.registeredUsers = registeredUsers;
}

public Exam addUser(User user) {
public Exam addRegisteredUser(User user) {
this.registeredUsers.add(user);
return this;
}

public Exam removeUser(User user) {
public Exam removeRegisteredUser(User user) {
this.registeredUsers.remove(user);
return this;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ public class ExerciseGroup implements Serializable {

@OneToMany(mappedBy = "exerciseGroup", fetch = FetchType.LAZY)
@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
@JsonIgnoreProperties("exerciseGroup")
@JsonIgnoreProperties(value = "exerciseGroup", allowSetters = true)
private Set<Exercise> exercises = new HashSet<>();

public Long getId() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,4 +49,7 @@ public interface CourseRepository extends JpaRepository<Course, Long> {
List<Course> findAllCurrentlyActiveAndNotOnlineAndEnabled(@Param("now") ZonedDateTime now);

List<Course> findAllByShortName(String shortName);

Optional<Course> findById(long courseId);

}
18 changes: 15 additions & 3 deletions src/main/java/de/tum/in/www1/artemis/service/CourseService.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
package de.tum.in.www1.artemis.service;

import static de.tum.in.www1.artemis.domain.enumeration.AssessmentType.AUTOMATIC;

import java.time.ZonedDateTime;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

import javax.validation.constraints.NotNull;
Expand All @@ -11,10 +14,9 @@
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import de.tum.in.www1.artemis.domain.Course;
import de.tum.in.www1.artemis.domain.Exercise;
import de.tum.in.www1.artemis.domain.User;
import de.tum.in.www1.artemis.domain.*;
import de.tum.in.www1.artemis.domain.exam.ExerciseGroup;
import de.tum.in.www1.artemis.domain.modeling.ModelingExercise;
import de.tum.in.www1.artemis.repository.CourseRepository;
import de.tum.in.www1.artemis.repository.UserRepository;
import de.tum.in.www1.artemis.web.rest.errors.EntityNotFoundException;
Expand Down Expand Up @@ -225,4 +227,14 @@ public Course retrieveCourseOverExerciseGroupOrCourseId(Exercise exercise) {
return findOne(exercise.getCourseViaExerciseGroupOrCourseMember().getId());
}
}

/**
* filters the passed exercises for the relevant ones that need to be manually assessed. This excludes quizzes and automatic programming exercises
* @param exercises all exercises (e.g. of a course or exercise group) that should be filtered
* @return the filtered and relevant exercises for manual assessment
*/
public Set<Exercise> getInterestingExercisesForAssessmentDashboards(Set<Exercise> exercises) {
return exercises.stream().filter(exercise -> exercise instanceof TextExercise || exercise instanceof ModelingExercise || exercise instanceof FileUploadExercise
|| (exercise instanceof ProgrammingExercise && exercise.getAssessmentType() != AUTOMATIC)).collect(Collectors.toSet());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -358,7 +358,7 @@ public List<StudentDTO> registerStudentsForExam(@PathVariable Long courseId, @Pa
if (!student.getGroups().contains(course.getStudentGroupName())) {
userService.addUserToGroup(student, course.getStudentGroupName());
}
exam.addUser(student);
exam.addRegisteredUser(student);
continue;
}
// 2) if we cannot find the student, we use the registration number and try to find the student in the (TUM) LDAP, create it in the Artemis DB and in a potential
Expand All @@ -368,7 +368,7 @@ public List<StudentDTO> registerStudentsForExam(@PathVariable Long courseId, @Pa
var student = optionalStudent.get();
// the newly created student needs to get the rights to access the course, otherwise the student cannot access the exam (within the course)
userService.addUserToGroup(student, course.getStudentGroupName());
exam.addUser(student);
exam.addRegisteredUser(student);
continue;
}
// 3) if we cannot find the user in the (TUM) LDAP, we report this to the client
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package de.tum.in.www1.artemis.service;

import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import de.tum.in.www1.artemis.domain.ExampleSubmission;
import de.tum.in.www1.artemis.domain.Exercise;
import de.tum.in.www1.artemis.domain.ProgrammingExercise;
import de.tum.in.www1.artemis.domain.enumeration.TutorParticipationStatus;
import de.tum.in.www1.artemis.domain.participation.TutorParticipation;
import de.tum.in.www1.artemis.repository.ExampleSubmissionRepository;
import de.tum.in.www1.artemis.web.rest.dto.DueDateStat;

/**
* Service Implementation for managing Tutor-Assessment-Dashboard.
*/
@Service
public class TutorDashboardService {

private final Logger log = LoggerFactory.getLogger(ExamService.class);

private final ExerciseService exerciseService;

private final ProgrammingExerciseService programmingExerciseService;

private final SubmissionService submissionService;

private final ResultService resultService;

private final ExampleSubmissionRepository exampleSubmissionRepository;

public TutorDashboardService(ExerciseService exerciseService, ProgrammingExerciseService programmingExerciseService, SubmissionService submissionService,
ResultService resultService, ExampleSubmissionRepository exampleSubmissionRepository) {
this.exerciseService = exerciseService;
this.programmingExerciseService = programmingExerciseService;
this.submissionService = submissionService;
this.resultService = resultService;
this.exampleSubmissionRepository = exampleSubmissionRepository;
}

/**
* Prepares the exercises for the tutor dashboard by setting the tutor participations and statistics
*
* @param exercises exercises to be prepared for the tutor dashboard
* @param tutorParticipations participations of the tutors
*/
public void prepareExercisesForTutorDashboard(Set<Exercise> exercises, List<TutorParticipation> tutorParticipations) {
for (Exercise exercise : exercises) {

DueDateStat numberOfSubmissions;
DueDateStat numberOfAssessments;

if (exercise instanceof ProgrammingExercise) {
numberOfSubmissions = new DueDateStat(programmingExerciseService.countSubmissionsByExerciseIdSubmitted(exercise.getId()), 0L);
numberOfAssessments = new DueDateStat(programmingExerciseService.countAssessmentsByExerciseIdSubmitted(exercise.getId()), 0L);
}
else {
numberOfSubmissions = submissionService.countSubmissionsForExercise(exercise.getId());
numberOfAssessments = resultService.countNumberOfFinishedAssessmentsForExercise(exercise.getId());
}

exercise.setNumberOfSubmissions(numberOfSubmissions);
exercise.setNumberOfAssessments(numberOfAssessments);

exerciseService.calculateNrOfOpenComplaints(exercise);

List<ExampleSubmission> exampleSubmissions = this.exampleSubmissionRepository.findAllByExerciseId(exercise.getId());
// Do not provide example submissions without any assessment
exampleSubmissions.removeIf(exampleSubmission -> exampleSubmission.getSubmission() == null || exampleSubmission.getSubmission().getResult() == null);
exercise.setExampleSubmissions(new HashSet<>(exampleSubmissions));

TutorParticipation tutorParticipation = tutorParticipations.stream().filter(participation -> participation.getAssessedExercise().getId().equals(exercise.getId()))
.findFirst().orElseGet(() -> {
TutorParticipation emptyTutorParticipation = new TutorParticipation();
emptyTutorParticipation.setStatus(TutorParticipationStatus.NOT_PARTICIPATED);
return emptyTutorParticipation;
});
exercise.setTutorParticipations(Collections.singleton(tutorParticipation));
}
}
}
Loading