Skip to content
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

Exam mode: Enable students to participate in the test exam multiple times #8609

Open
wants to merge 120 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 98 commits
Commits
Show all changes
120 commits
Select commit Hold shift + click to select a range
7392a09
refactor StudentExamService
coolchock May 13, 2024
89ef7fb
refactor ParticipationService^
coolchock May 13, 2024
61cba39
add student_exam_participation table
coolchock May 13, 2024
8da735c
assign generated participations to the student exam
coolchock May 14, 2024
07c541e
refactor start exercise method
coolchock May 14, 2024
c6ced67
remove index to allow multiple participations with the same student_i…
coolchock May 14, 2024
fff4bfa
refactor exam access service
coolchock May 14, 2024
6dc3c42
add participation_id column to participation table
coolchock May 16, 2024
4b90546
add participation_id column to the index
coolchock May 16, 2024
b882350
add a query to fetch last text exam participation
coolchock May 16, 2024
ffb9eab
add changelogs to master.xml
coolchock May 16, 2024
6912659
change navigation in the attempt-review-component
coolchock May 16, 2024
261c3be
refactor ExamSubmissionService
coolchock May 16, 2024
43b0ef1
refactor ExamDateService
coolchock May 16, 2024
c055ef1
add isFinished method in the StudentExam class
coolchock May 16, 2024
9e2e08c
change column name from participation_id to number_of_attempts
coolchock May 16, 2024
dd4e8a7
add query to check if an exam is a test exam
coolchock May 16, 2024
e552578
add column to Participation entity
coolchock May 16, 2024
1ab81f1
set number of attempts
coolchock May 16, 2024
bf79e1b
check for the testExam in the live-events
coolchock May 16, 2024
7f3da78
change method in the exam summary method
coolchock May 16, 2024
f632053
filter out participations in the ExamService
coolchock May 16, 2024
b9cd7db
set number of attempts to 255
coolchock May 16, 2024
3a022b5
add comment
coolchock May 16, 2024
0023448
adjust comments
coolchock May 16, 2024
0fb3cfd
remove commented method
coolchock May 16, 2024
c58dd87
Merge branch 'refs/heads/develop' into feature/exam-mode/participate-…
coolchock May 27, 2024
7d4a49c
fetch latest student exam in ResultService
coolchock May 28, 2024
0fbb9c3
add repository method to fetch latest StudentParticipation
coolchock May 28, 2024
1a8c1a5
distinguish between test exam and other exam types in ProgrammingExer…
coolchock May 28, 2024
7fc8758
change ParticipationServiceTest
coolchock May 28, 2024
aaaa4b2
fetch latest participation for the test exam exercise
coolchock May 29, 2024
87b9321
implement findLatest methods
coolchock May 29, 2024
38e08bf
remove attemptNumber from repository slug
coolchock Jun 3, 2024
cb24abf
Merge branch 'refs/heads/develop' into feature/exam-mode/participate-…
coolchock Jun 3, 2024
b1c1b9c
Merge branch 'refs/heads/develop' into feature/exam-mode/participate-…
coolchock Jun 5, 2024
dc95707
remove redundant call to set attempt number
coolchock Jun 5, 2024
564bde9
convert list to a set to improve performance
coolchock Jun 5, 2024
ca54780
add attempt number to repository name
coolchock Jun 5, 2024
da998a0
rename method and remove submission policy
coolchock Jun 7, 2024
044fadc
change router link
coolchock Jun 7, 2024
7e7b868
change logic of openStudentExam
coolchock Jun 7, 2024
ae91e10
make attempt component non-clickable if the attempt was not submitted…
coolchock Jun 7, 2024
5f34573
change repositoryLink for test-exams
coolchock Jun 7, 2024
49f5d11
remove @Transactional
coolchock Jun 7, 2024
1e1c3b5
add @Param annotations
coolchock Jun 7, 2024
ed10000
optimize query by using limit and order instead of select max
coolchock Jun 7, 2024
b604bf0
use toList() instead of Collectors
coolchock Jun 7, 2024
52f1e5a
change repository method
coolchock Jun 7, 2024
74599b9
fix the issue, that submissions of first attempt are not saved
coolchock Jun 9, 2024
1c0c7af
Merge branch 'refs/heads/develop' into feature/exam-mode/participate-…
coolchock Jun 10, 2024
e4ddb76
distinguish between test exams in repository method
coolchock Jun 10, 2024
cd7dc52
move filtering of the student exam participations to the repository m…
coolchock Jun 10, 2024
631dd2f
Merge branch 'refs/heads/develop' into feature/exam-mode/participate-…
coolchock Jun 12, 2024
8476dcd
fix tests
coolchock Jun 12, 2024
d00108f
adjust architecture test
coolchock Jun 12, 2024
71171c0
avoid unnecessary DB call
coolchock Jun 12, 2024
6c085c3
adjust repository methods
coolchock Jun 12, 2024
8c9f604
rename method^
coolchock Jun 12, 2024
6500a9e
fix test
coolchock Jun 12, 2024
3b9a656
remove unnecessary test
coolchock Jun 12, 2024
8fad453
adjust translations
coolchock Jun 14, 2024
ac396fd
refactor getExamInCourseElseThrow method
coolchock Jun 14, 2024
2e4ad8c
Merge branch 'refs/heads/develop' into feature/exam-mode/participate-…
coolchock Jun 14, 2024
0a2519c
translations
coolchock Jun 15, 2024
8ed669d
Merge branch 'refs/heads/develop' into feature/exam-mode/participate-…
coolchock Jun 15, 2024
6b4856e
resolve rabbit comments
coolchock Jun 16, 2024
c6d851e
adjust tests
coolchock Jun 16, 2024
ea1f359
Merge branch 'refs/heads/develop' into feature/exam-mode/participate-…
coolchock Jun 16, 2024
b186bae
adjust filtering of sensitive feedbacks in exam exercises
coolchock Jun 17, 2024
afeed60
implement isTestExamExercise method
coolchock Jun 17, 2024
7b8ec72
isTestExam method
coolchock Jun 17, 2024
ee30325
add numberOfAttempts @param docu
coolchock Jun 17, 2024
bfd9323
Merge branch 'refs/heads/develop' into feature/exam-mode/participate-…
coolchock Jun 21, 2024
bac9202
Merge branch 'refs/heads/develop' into feature/exam-mode/participate-…
coolchock Jun 23, 2024
b40c3ab
Merge branch 'refs/heads/develop' into feature/exam-mode/participate-…
coolchock Jun 24, 2024
c85c599
fix instructors not being able to participate
coolchock Jun 24, 2024
7644eb6
prettier
coolchock Jun 24, 2024
54853d5
remove LIMIT from JPQL queries
coolchock Jun 24, 2024
edc9c18
add ElseThrow to method signature^
coolchock Jun 24, 2024
f913762
Remove unused files in new exam mode ui
edkaya Jul 10, 2024
ebcdd54
Resolve merge conflict
edkaya Jul 10, 2024
2025853
Implement new attempt design and integrate to new exam mode ui
edkaya Jul 10, 2024
8870714
Fix one client test
edkaya Jul 10, 2024
1b6c16e
Add javadoc comments
edkaya Jul 10, 2024
588e161
Merge branch 'refs/heads/develop' into feature/exam-mode/participate-…
coolchock Aug 20, 2024
dc78eec
solve problems after merging develop
coolchock Aug 21, 2024
613fd3a
Merge branch 'refs/heads/develop' into feature/exam-mode/participate-…
coolchock Aug 27, 2024
32e4e75
add test exam to condition
coolchock Aug 27, 2024
6e00298
modify repository method to find first participation with submissions
coolchock Aug 27, 2024
183d28c
merge changes in sidebar-card-item.component.html
coolchock Aug 28, 2024
1398574
reload attempts after finishing one
coolchock Aug 28, 2024
35d6501
rename number_of_attempts to attempts in migrations and class
coolchock Aug 28, 2024
3fa1a04
remove entity graph
coolchock Aug 28, 2024
8ec09d1
fix architecture test
coolchock Sep 1, 2024
72f8906
Merge branch 'refs/heads/develop' into feature/exam-mode/participate-…
coolchock Sep 1, 2024
f0730d6
add tests
coolchock Sep 1, 2024
42258ed
resolve TODOs
coolchock Sep 1, 2024
73acd0b
Merge branch 'refs/heads/develop' into feature/exam-mode/participate-…
coolchock Sep 3, 2024
622ac46
don't show percentDiff for test exams
coolchock Sep 3, 2024
53c2219
show start and end dates for test exams
coolchock Sep 3, 2024
11abf89
Merge branch 'refs/heads/develop' into feature/exam-mode/participate-…
coolchock Sep 15, 2024
f8c24cb
resolve merge conflicts
coolchock Sep 15, 2024
7956b02
incorporate feedback
coolchock Sep 17, 2024
690d249
add javadoc
coolchock Sep 17, 2024
d75d88f
improve coverage
coolchock Sep 17, 2024
def8e4e
fix tests
coolchock Sep 17, 2024
a51b76e
improve coverage
coolchock Sep 17, 2024
5d7067d
fix typo
coolchock Sep 17, 2024
0bed9b0
use findFirst to simplify the code
coolchock Sep 21, 2024
2bd0b8e
use template literals to improve readability
coolchock Sep 21, 2024
1d11448
rename method
coolchock Sep 21, 2024
abea29b
Merge branch 'refs/heads/develop' into feature/exam-mode/participate-…
coolchock Sep 21, 2024
6c526d8
Merge branch 'refs/heads/develop' into feature/exam-mode/participate-…
coolchock Oct 1, 2024
917afe7
Merge branch 'refs/heads/develop' into feature/exam-mode/participate-…
coolchock Oct 15, 2024
dcc5903
resolve merge conflicts
coolchock Oct 15, 2024
0658e9a
resolve merge conflicts
coolchock Oct 15, 2024
0400798
resolve merge conflicts
coolchock Oct 16, 2024
e83d11e
Merge branch 'refs/heads/develop' into feature/exam-mode/participate-…
coolchock Oct 20, 2024
6fd8939
Merge branch 'refs/heads/develop' into feature/exam-mode/participate-…
coolchock Nov 10, 2024
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
5 changes: 5 additions & 0 deletions src/main/java/de/tum/in/www1/artemis/domain/Exercise.java
Original file line number Diff line number Diff line change
Expand Up @@ -369,6 +369,11 @@ public boolean isExamExercise() {
return this.exerciseGroup != null;
}

@JsonIgnore
public boolean isTestExamExercise() {
return isExamExercise() && this.getExam().isTestExam();
}

/**
* Utility method to get the course. Get the course over the exerciseGroup, if one was set, otherwise return
* the course class member
Expand Down
25 changes: 25 additions & 0 deletions src/main/java/de/tum/in/www1/artemis/domain/exam/StudentExam.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import de.tum.in.www1.artemis.domain.AbstractAuditingEntity;
import de.tum.in.www1.artemis.domain.Exercise;
import de.tum.in.www1.artemis.domain.User;
import de.tum.in.www1.artemis.domain.participation.StudentParticipation;
import de.tum.in.www1.artemis.domain.quiz.QuizQuestion;

@Entity
Expand Down Expand Up @@ -84,6 +85,12 @@ public class StudentExam extends AbstractAuditingEntity {
@JoinTable(name = "student_exam_quiz_question", joinColumns = @JoinColumn(name = "student_exam_id", referencedColumnName = "id"), inverseJoinColumns = @JoinColumn(name = "quiz_question_id", referencedColumnName = "id"))
private List<QuizQuestion> quizQuestions = new ArrayList<>();

@ManyToMany
coolchock marked this conversation as resolved.
Show resolved Hide resolved
@JoinTable(name = "student_exam_participation", joinColumns = @JoinColumn(name = "student_exam_id", referencedColumnName = "id"), inverseJoinColumns = @JoinColumn(name = "participation_id", referencedColumnName = "id"))
@OrderColumn(name = "participation_order")
@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
private List<StudentParticipation> studentParticipations = new ArrayList<>();
coolchock marked this conversation as resolved.
Show resolved Hide resolved

public Boolean isSubmitted() {
return submitted;
}
Expand Down Expand Up @@ -199,6 +206,14 @@ public void setQuizQuestions(List<QuizQuestion> quizQuestions) {
this.quizQuestions = quizQuestions;
}

public List<StudentParticipation> getStudentParticipations() {
return studentParticipations;
}

public void setStudentParticipations(List<StudentParticipation> studentParticipations) {
this.studentParticipations = studentParticipations;
}

/**
* Adds the given exam session to the student exam
*
Expand Down Expand Up @@ -231,6 +246,16 @@ public Boolean isEnded() {
return ZonedDateTime.now().isAfter(getIndividualEndDate());
}

/**
* Check if the individual student exam is finished
* A student exam is finished if it's started and either submitted or the time has passed
*
* @return true if the exam is finished, otherwise false
*/
public boolean isFinished() {
coolchock marked this conversation as resolved.
Show resolved Hide resolved
return Boolean.TRUE.equals(this.isStarted()) && (Boolean.TRUE.equals(this.isEnded()) || Boolean.TRUE.equals(this.isSubmitted()));
coolchock marked this conversation as resolved.
Show resolved Hide resolved
}

/**
* Returns the individual exam end date taking the working time of this student exam into account.
* For test exams, the startedDate needs to be defined as this is not equal to exam.startDate
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,17 @@ public abstract class Participation extends DomainObject implements Participatio
@Cache(usage = CacheConcurrencyStrategy.NONSTRICT_READ_WRITE)
private Set<Submission> submissions = new HashSet<>();

/**
* Graded course exercises, practice mode course exercises and real exam exercises always have only one parcitipation per exercise
* In case of a test exam, there are multiple participations possible for one exercise
* This field is necessary to preserve the constraint of one partipation per exercise, while allowing multiple particpiations per exercise for test exams
* The value is 0 for graded course exercises and exercises in the real exams
* The value is 1 for practice mode course exercises
* The value is 0-255 for test exam exercises. For each subsequent participation the number is increased by one
*/
@Column(name = "attempt")
private int attempt = 0;

/**
* This property stores the total number of submissions in this participation. Not stored in the database, computed dynamically and used in showing statistics to the user in
* the exercise view.
Expand Down Expand Up @@ -242,6 +253,14 @@ public void setSubmissions(Set<Submission> submissions) {
this.submissions = submissions;
}

public int getAttempt() {
return attempt;
}

public void setAttempt(int index) {
this.attempt = index;
}

// jhipster-needle-entity-add-getters-setters - JHipster will add getters and setters here, do not remove

/**
Expand Down Expand Up @@ -353,4 +372,5 @@ public String toString() {

@JsonIgnore
public abstract String getType();

}
Original file line number Diff line number Diff line change
Expand Up @@ -508,4 +508,11 @@ private static Map<Long, Integer> convertListOfCountsIntoMap(List<long[]> examId
AND registeredUsers.user.id = :userId
""")
Set<Exam> findActiveExams(@Param("courseIds") Set<Long> courseIds, @Param("userId") long userId, @Param("visible") ZonedDateTime visible, @Param("end") ZonedDateTime end);

@Query("""
SELECT e.testExam
FROM Exam e
WHERE e.id = :examId
""")
boolean isTestExam(@Param("examId") long examId);
coolchock marked this conversation as resolved.
Show resolved Hide resolved
}
Original file line number Diff line number Diff line change
Expand Up @@ -74,21 +74,44 @@ Optional<ProgrammingExerciseStudentParticipation> findByIdWithAllResultsAndRelat

Optional<ProgrammingExerciseStudentParticipation> findByExerciseIdAndStudentLogin(long exerciseId, String username);

Optional<ProgrammingExerciseStudentParticipation> findFirstByExerciseIdAndStudentLoginOrderByIdDesc(long exerciseId, String username);

List<ProgrammingExerciseStudentParticipation> findAllByExerciseIdAndStudentLogin(long exerciseId, String username);

default ProgrammingExerciseStudentParticipation findByExerciseIdAndStudentLoginOrThrow(long exerciseId, String username) {
return getValueElseThrow(findByExerciseIdAndStudentLogin(exerciseId, username));
}

default ProgrammingExerciseStudentParticipation findFirstByExerciseIdAndStudentLoginOrThrow(long exerciseId, String username) {
return getValueElseThrow(findFirstByExerciseIdAndStudentLoginOrderByIdDesc(exerciseId, username));
}

@EntityGraph(type = LOAD, attributePaths = { "submissions" })
Optional<ProgrammingExerciseStudentParticipation> findWithSubmissionsByExerciseIdAndStudentLogin(long exerciseId, String username);

@Query("""
SELECT participation
FROM ProgrammingExerciseStudentParticipation participation
LEFT JOIN FETCH participation.submissions s
WHERE participation.exercise.id = :exerciseId
AND participation.student.login = :username
ORDER BY participation.id DESC
""")
List<ProgrammingExerciseStudentParticipation> findFirstWithSubmissionsByExerciseIdAndStudentLoginOrderByIdDesc(@Param("exerciseId") long exerciseId,
@Param("username") String username);

default ProgrammingExerciseStudentParticipation findWithSubmissionsByExerciseIdAndStudentLoginOrThrow(long exerciseId, String username) {
return getValueElseThrow(findWithSubmissionsByExerciseIdAndStudentLogin(exerciseId, username));
}

default ProgrammingExerciseStudentParticipation findFirstWithSubmissionsByExerciseIdAndStudentLoginOrThrow(long exerciseId, String username) {
return getValueElseThrow(findFirstWithSubmissionsByExerciseIdAndStudentLoginOrderByIdDesc(exerciseId, username).stream().findFirst());
}

Optional<ProgrammingExerciseStudentParticipation> findByExerciseIdAndStudentLoginAndTestRun(long exerciseId, String username, boolean testRun);

Optional<ProgrammingExerciseStudentParticipation> findFirstByExerciseIdAndStudentLoginAndTestRunOrderByIdDesc(long exerciseId, String username, boolean testRun);

@EntityGraph(type = LOAD, attributePaths = { "team.students" })
Optional<ProgrammingExerciseStudentParticipation> findByExerciseIdAndTeamId(long exerciseId, long teamId);

Expand Down Expand Up @@ -164,6 +187,22 @@ List<ProgrammingExerciseStudentParticipation> findWithSubmissionsByExerciseIdAnd
Optional<ProgrammingExerciseStudentParticipation> findWithSubmissionsByExerciseIdAndStudentLoginAndTestRun(@Param("exerciseId") long exerciseId,
@Param("username") String username, @Param("testRun") boolean testRun);

@Query("""
SELECT participation
FROM ProgrammingExerciseStudentParticipation participation
LEFT JOIN FETCH participation.submissions s
WHERE participation.exercise.id = :exerciseId
AND participation.student.login = :username
AND participation.testRun = :testRun
ORDER BY participation.id DESC
""")
List<ProgrammingExerciseStudentParticipation> findFirstWithSubmissionsByExerciseIdAndStudentLoginAndTestRunOrderByIdDesc(@Param("exerciseId") long exerciseId,
@Param("username") String username, @Param("testRun") boolean testRun);

default Optional<ProgrammingExerciseStudentParticipation> findFirstWithSubmissionsByExerciseIdAndStudentLoginAndTestRun(long exerciseId, String username, boolean testRun) {
return findFirstWithSubmissionsByExerciseIdAndStudentLoginAndTestRunOrderByIdDesc(exerciseId, username, testRun).stream().findFirst();
}

@Query("""
SELECT participation
FROM ProgrammingExerciseStudentParticipation participation
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,15 +42,18 @@ public interface StudentExamRepository extends ArtemisJpaRepository<StudentExam,
@EntityGraph(type = LOAD, attributePaths = { "exercises" })
Optional<StudentExam> findWithExercisesById(Long studentExamId);

@EntityGraph(type = LOAD, attributePaths = { "exercises", "studentParticipations" })
Optional<StudentExam> findWithExercisesAndStudentParticipationsById(Long studentExamId);
coolchock marked this conversation as resolved.
Show resolved Hide resolved

coolchock marked this conversation as resolved.
Show resolved Hide resolved
@Query("""
SELECT se
FROM StudentExam se
LEFT JOIN FETCH se.exercises e
LEFT JOIN FETCH e.submissionPolicy
LEFT JOIN FETCH se.examSessions
LEFT JOIN FETCH se.studentParticipations
WHERE se.id = :studentExamId
""")
Optional<StudentExam> findWithExercisesSubmissionPolicyAndSessionsById(@Param("studentExamId") long studentExamId);
Optional<StudentExam> findWithExercisesAndSessionsById(@Param("studentExamId") long studentExamId);
coolchock marked this conversation as resolved.
Show resolved Hide resolved

@Query("""
SELECT DISTINCT se
Expand Down Expand Up @@ -215,6 +218,38 @@ SELECT COUNT(se)
""")
Optional<StudentExam> findByExamIdAndUserId(@Param("examId") long examId, @Param("userId") long userId);

Optional<StudentExam> findFirstByExamIdAndUserIdOrderByIdDesc(long examId, long userId);

@Query("""
SELECT se
FROM StudentExam se
JOIN se.studentParticipations p
WHERE se.exam.id = :examId
AND p.id = :participationId
""")
Optional<StudentExam> findByExamIdAndParticipationId(@Param("examId") long examId, @Param("participationId") long participationId);

coolchock marked this conversation as resolved.
Show resolved Hide resolved
/**
* Return the StudentExam for the given examId and userId, if possible. For test exams, the latest Student Exam is returned.
*
* @param examId id of the exam
* @param userId id of the user
* @param testExam boolean indicating if the exam is a test exam
* @return the student exam
* @throws EntityNotFoundException if no student exams could be found
*/
default StudentExam findOneByExamIdAndUserIdElseThrow(long examId, long userId, boolean testExam) {
Optional<StudentExam> studentExam;
if (testExam) {
studentExam = this.findFirstByExamIdAndUserIdOrderByIdDesc(examId, userId);
}
else {
studentExam = this.findByExamIdAndUserId(examId, userId);
}

return getValueElseThrow(studentExam);
}

/**
* Checks if any StudentExam exists for the given user (student) id in the given course.
*
Expand Down Expand Up @@ -280,7 +315,17 @@ SELECT MAX(se.workingTime)
AND se.exam.testExam = TRUE
AND se.testRun = FALSE
""")
List<StudentExam> findStudentExamForTestExamsByUserIdAndCourseId(@Param("userId") Long userId, @Param("courseId") Long courseId);
List<StudentExam> findStudentExamsForTestExamsByUserIdAndCourseId(@Param("userId") Long userId, @Param("courseId") Long courseId);

@Query("""
SELECT DISTINCT se
FROM StudentExam se
WHERE se.user.id = :userId
AND se.exam.id = :examId
AND se.exam.testExam = TRUE
AND se.testRun = FALSE
""")
List<StudentExam> findStudentExamsForTestExamsByUserIdAndExamId(@Param("userId") Long userId, @Param("examId") Long examId);
coolchock marked this conversation as resolved.
Show resolved Hide resolved

@Query("""
SELECT DISTINCT se
Expand Down Expand Up @@ -340,15 +385,20 @@ default StudentExam findByIdWithExercisesElseThrow(Long studentExamId) {
return getValueElseThrow(findWithExercisesById(studentExamId), studentExamId);
}

@NotNull
default StudentExam findByIdWithExercisesAndStudentParticipationsElseThrow(Long studentExamId) {
return getValueElseThrow(findWithExercisesAndStudentParticipationsById(studentExamId));
}

coolchock marked this conversation as resolved.
Show resolved Hide resolved
/**
* Get one student exam by id with exercises, programming exercise submission policy and sessions
* Get one student exam by id with exercises and sessions
*
* @param studentExamId the id of the student exam
* @return the student exam with exercises
*/
@NotNull
default StudentExam findByIdWithExercisesSubmissionPolicyAndSessionsElseThrow(Long studentExamId) {
return getValueElseThrow(findWithExercisesSubmissionPolicyAndSessionsById(studentExamId), studentExamId);
default StudentExam findByIdWithExercisesAndSessionsElseThrow(Long studentExamId) {
return getValueElseThrow(findWithExercisesAndSessionsById(studentExamId), studentExamId);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,8 @@ SELECT COUNT(p.id) > 0
""")
Optional<StudentParticipation> findByExerciseIdAndStudentLogin(@Param("exerciseId") long exerciseId, @Param("username") String username);

Optional<StudentParticipation> findFirstByExerciseIdAndStudentLoginOrderByIdDesc(long exerciseId, String username);

@Query("""
SELECT DISTINCT p
FROM StudentParticipation p
Expand All @@ -126,6 +128,21 @@ SELECT COUNT(p.id) > 0
""")
Optional<StudentParticipation> findWithEagerLegalSubmissionsByExerciseIdAndStudentLogin(@Param("exerciseId") long exerciseId, @Param("username") String username);

@Query("""
SELECT DISTINCT p
FROM StudentParticipation p
LEFT JOIN FETCH p.submissions s
WHERE p.id = (
SELECT MAX(p2.id)
FROM StudentParticipation p2
LEFT JOIN p2.submissions s2
WHERE p2.exercise.id = :exerciseId
AND p2.student.login = :username
AND (s2.type <> de.tum.in.www1.artemis.domain.enumeration.SubmissionType.ILLEGAL OR s2.type IS NULL)
)
""")
Optional<StudentParticipation> findLatestWithEagerLegalSubmissionsByExerciseIdAndStudentLogin(@Param("exerciseId") long exerciseId, @Param("username") String username);

@Query("""
SELECT DISTINCT p
FROM StudentParticipation p
Expand Down Expand Up @@ -426,7 +443,7 @@ default List<StudentParticipation> findByExerciseIdWithManualResultAndFeedbacksA
LEFT JOIN FETCH p.submissions
WHERE p.exercise.id = :exerciseId
AND p.student.id = :studentId
""")
""")
List<StudentParticipation> findByExerciseIdAndStudentIdWithEagerResultsAndSubmissions(@Param("exerciseId") long exerciseId, @Param("studentId") long studentId);

@Query("""
Expand Down Expand Up @@ -766,6 +783,30 @@ List<StudentParticipation> findByStudentIdAndIndividualExercisesWithEagerSubmiss
List<StudentParticipation> findByStudentIdAndIndividualExercisesWithEagerSubmissionsResultAndAssessorIgnoreTestRuns(@Param("studentId") long studentId,
@Param("exercises") List<Exercise> exercises);

@Query("""
SELECT DISTINCT p
FROM StudentExam se
JOIN se.studentParticipations p
LEFT JOIN FETCH p.submissions s
LEFT JOIN FETCH s.results r
LEFT JOIN FETCH r.assessor
WHERE p.testRun = FALSE
AND se.id IN :studentExamId
""")
List<StudentParticipation> findTestExamParticipationsByStudentIdAndIndividualExercisesWithEagerSubmissionsResultAndAssessorIgnoreTestRuns(
@Param("studentExamId") long studentExamId);

@Query("""
SELECT DISTINCT p
FROM StudentExam se
JOIN se.studentParticipations p
LEFT JOIN FETCH p.submissions s
LEFT JOIN FETCH s.results r
WHERE p.testRun = FALSE
AND se.id IN :studentExamId
""")
List<StudentParticipation> findTestExamParticipationsByStudentIdAndIndividualExercisesWithEagerSubmissionsResultIgnoreTestRuns(@Param("studentExamId") long studentExamId);
coolchock marked this conversation as resolved.
Show resolved Hide resolved
coolchock marked this conversation as resolved.
Show resolved Hide resolved
coolchock marked this conversation as resolved.
Show resolved Hide resolved

@Query("""
SELECT DISTINCT p
FROM StudentParticipation p
Expand Down Expand Up @@ -976,7 +1017,7 @@ private List<StudentParticipation> filterParticipationsWithRelevantResults(List<

/**
* Get all participations for the given studentExam and exercises combined with their submissions with a result.
* Distinguishes between student exams and test runs and only loads the respective participations
* Distinguishes between real exams, test exams and test runs and only loads the respective participations
*
* @param studentExam studentExam with exercises loaded
* @param withAssessor (only for non-test runs) if assessor should be loaded with the result
Expand All @@ -986,6 +1027,15 @@ default List<StudentParticipation> findByStudentExamWithEagerSubmissionsResult(S
if (studentExam.isTestRun()) {
return findTestRunParticipationsByStudentIdAndIndividualExercisesWithEagerSubmissionsResult(studentExam.getUser().getId(), studentExam.getExercises());
}

if (studentExam.isTestExam()) {
if (withAssessor) {
return findTestExamParticipationsByStudentIdAndIndividualExercisesWithEagerSubmissionsResultAndAssessorIgnoreTestRuns(studentExam.getId());
}
else {
return findTestExamParticipationsByStudentIdAndIndividualExercisesWithEagerSubmissionsResultIgnoreTestRuns(studentExam.getId());
}
}
else {
if (withAssessor) {
return findByStudentIdAndIndividualExercisesWithEagerSubmissionsResultAndAssessorIgnoreTestRuns(studentExam.getUser().getId(), studentExam.getExercises());
Expand Down
Loading
Loading