use of de.tum.in.www1.artemis.domain.participation.StudentParticipation in project ArTEMiS by ls1intum.
the class ParticipationTeamWebsocketService method updateSubmission.
/**
* Updates a modeling or text submission
*
* @param participationId id of participation
* @param submission updated modeling text submission
* @param principal principal of user who wants to update the submission
* @param topicPath path of websocket destination topic where to send the new submission
*/
private void updateSubmission(@DestinationVariable Long participationId, @Payload Submission submission, Principal principal, String topicPath) {
// Without this, custom jpa repository methods don't work in websocket channel.
SecurityUtils.setAuthorizationObject();
final StudentParticipation participation = studentParticipationRepository.findByIdElseThrow(participationId);
// user must belong to the team who owns the participation in order to update a submission
if (!participation.isOwnedBy(principal.getName())) {
return;
}
final User user = userRepository.getUserWithGroupsAndAuthorities(principal.getName());
final Exercise exercise = exerciseRepository.findByIdElseThrow(participation.getExercise().getId());
if (submission instanceof ModelingSubmission && exercise instanceof ModelingExercise) {
submission = modelingSubmissionService.save((ModelingSubmission) submission, (ModelingExercise) exercise, principal.getName());
modelingSubmissionService.hideDetails(submission, user);
} else if (submission instanceof TextSubmission && exercise instanceof TextExercise) {
submission = textSubmissionService.handleTextSubmission((TextSubmission) submission, (TextExercise) exercise, principal);
textSubmissionService.hideDetails(submission, user);
} else {
throw new IllegalArgumentException("Submission type '" + submission.getType() + "' not allowed.");
}
// update the last action date for the user and send out list of team members
updateValue(lastActionTracker, participationId, principal.getName());
sendOnlineTeamStudents(participationId);
SubmissionSyncPayload payload = new SubmissionSyncPayload(submission, user);
messagingTemplate.convertAndSend(getDestination(participationId, topicPath), payload);
}
use of de.tum.in.www1.artemis.domain.participation.StudentParticipation in project ArTEMiS by ls1intum.
the class TextSubmissionService method save.
/**
* Saves the given submission. Is used for creating and updating text submissions.
*
* @param textSubmission the submission that should be saved
* @param participation the participation the submission belongs to
* @param textExercise the exercise the submission belongs to
* @param principal the principal of the user
* @return the textSubmission entity that was saved to the database
*/
public TextSubmission save(TextSubmission textSubmission, StudentParticipation participation, TextExercise textExercise, Principal principal) {
// update submission properties
textSubmission.setSubmissionDate(ZonedDateTime.now());
textSubmission.setType(SubmissionType.MANUAL);
textSubmission.setParticipation(participation);
// remove result from submission (in the unlikely case it is passed here), so that students cannot inject a result
textSubmission.setResults(new ArrayList<>());
textSubmission = textSubmissionRepository.save(textSubmission);
// versioning of submission
try {
if (textExercise.isTeamMode()) {
submissionVersionService.saveVersionForTeam(textSubmission, principal.getName());
} else if (textExercise.isExamExercise()) {
submissionVersionService.saveVersionForIndividual(textSubmission, principal.getName());
}
} catch (Exception ex) {
log.error("Text submission version could not be saved", ex);
}
participation.addSubmission(textSubmission);
participation.setInitializationState(InitializationState.FINISHED);
StudentParticipation savedParticipation = studentParticipationRepository.save(participation);
if (textSubmission.getId() == null) {
Optional<Submission> optionalTextSubmission = savedParticipation.findLatestSubmission();
if (optionalTextSubmission.isPresent()) {
textSubmission = (TextSubmission) optionalTextSubmission.get();
}
}
return textSubmission;
}
use of de.tum.in.www1.artemis.domain.participation.StudentParticipation in project ArTEMiS by ls1intum.
the class ExamQuizService method evaluateSubmissions.
/**
* // @formatter:off
* Evaluate the given quiz exercise by performing the following actions for each participation:
* 1. Get the submission for each participation (there should be only one as in exam mode, the submission gets created upfront and will be updated)
* - If no submission is found, print a warning and continue as we cannot evaluate that submission
* - If more than one submission is found, select one of them
* 2. mark submission and participation as evaluated
* 3. Create a new result for the selected submission and calculate scores
* 4. Save the updated submission & participation and the newly created result
*
* After processing all participations, the created results will be returned for further processing
* Note: We ignore test run participations
* // @formatter:on
* @param quizExercise the id of the QuizExercise that should be evaluated
* @return the newly generated results
*/
private Set<Result> evaluateSubmissions(@NotNull QuizExercise quizExercise) {
Set<Result> createdResults = new HashSet<>();
List<StudentParticipation> studentParticipations = studentParticipationRepository.findAllWithEagerLegalSubmissionsAndEagerResultsByExerciseId(quizExercise.getId());
for (var participation : studentParticipations) {
if (!participation.isTestRun()) {
try {
// reconnect so that the quiz questions are available later on (otherwise there will be a org.hibernate.LazyInitializationException)
participation.setExercise(quizExercise);
Set<Submission> submissions = participation.getSubmissions();
QuizSubmission quizSubmission;
if (submissions.isEmpty()) {
log.warn("Found no submissions for participation {} (Participant {}) in quiz {}", participation.getId(), participation.getParticipant().getName(), quizExercise.getId());
continue;
} else if (submissions.size() > 1) {
log.warn("Found multiple ({}) submissions for participation {} (Participant {}) in quiz {}, taking the one with highest id", submissions.size(), participation.getId(), participation.getParticipant().getName(), quizExercise.getId());
List<Submission> submissionsList = new ArrayList<>(submissions);
// Load submission with highest id
submissionsList.sort(Comparator.comparing(Submission::getId).reversed());
quizSubmission = (QuizSubmission) submissionsList.get(0);
} else {
quizSubmission = (QuizSubmission) submissions.iterator().next();
}
participation.setInitializationState(InitializationState.FINISHED);
boolean resultExisting = false;
// create new result if none is existing
Result result;
if (participation.getResults().isEmpty()) {
result = new Result().participation(participation);
} else {
resultExisting = true;
result = participation.getResults().iterator().next();
}
// Only create Results once after the first evaluation
if (!resultExisting) {
// delete result from quizSubmission, to be able to set a new one
if (quizSubmission.getLatestResult() != null) {
resultService.deleteResultWithComplaint(quizSubmission.getLatestResult().getId());
}
result.setRated(true);
result.setAssessmentType(AssessmentType.AUTOMATIC);
result.setCompletionDate(ZonedDateTime.now());
// set submission to calculate scores
result.setSubmission(quizSubmission);
// calculate scores and update result and submission accordingly
quizSubmission.calculateAndUpdateScores(quizExercise);
result.evaluateQuizSubmission();
// remove submission to follow save order for ordered collections
result.setSubmission(null);
// NOTE: we save participation, submission and result here individually so that one exception (e.g. duplicated key) cannot destroy multiple student answers
submissionRepository.save(quizSubmission);
result = resultRepository.save(result);
// add result to participation
participation.addResult(result);
studentParticipationRepository.save(participation);
// add result to submission
result.setSubmission(quizSubmission);
quizSubmission.addResult(result);
submissionRepository.save(quizSubmission);
// Add result so that it can be returned (and processed later)
createdResults.add(result);
}
} catch (Exception e) {
log.error("Exception in evaluateExamQuizExercise() for user {} in quiz {}: {}", participation.getParticipantIdentifier(), quizExercise.getId(), e.getMessage(), e);
}
}
}
return createdResults;
}
use of de.tum.in.www1.artemis.domain.participation.StudentParticipation in project ArTEMiS by ls1intum.
the class ExamSubmissionService method preventMultipleSubmissions.
/**
* We want to prevent multiple submissions for text, modeling, file upload and quiz exercises. Therefore we check if
* a submission for this exercise+student already exists.
* - If a submission exists, we will always overwrite this submission, even if the id of the received submission
* deviates from the one we've got from the database.
* - If no submission exists (on creation) we allow adding one (implicitly via repository.save()).
*
* TODO: we might want to move this to the SubmissionService
*
* @param exercise the exercise for which the submission should be saved
* @param submission the submission
* @param user the current user
* @return the submission. If a submission already exists for the exercise we will set the id
*/
public Submission preventMultipleSubmissions(Exercise exercise, Submission submission, User user) {
// Return immediately if it is not an exam submissions or if it is a programming exercise
if (!isExamSubmission(exercise) || exercise instanceof ProgrammingExercise) {
return submission;
}
List<StudentParticipation> participations = participationService.findByExerciseAndStudentIdWithEagerSubmissions(exercise, user.getId());
if (!participations.isEmpty()) {
Set<Submission> submissions = participations.get(0).getSubmissions();
if (!submissions.isEmpty()) {
Submission existingSubmission = submissions.iterator().next();
// Instead of creating a new submission, we want to overwrite the already existing submission. Therefore
// we set the id of the received submission to the id of the existing submission. When repository.save()
// is invoked the existing submission will be updated.
submission.setId(existingSubmission.getId());
}
}
return submission;
}
use of de.tum.in.www1.artemis.domain.participation.StudentParticipation in project ArTEMiS by ls1intum.
the class ResultResource method getResultsForExerciseWithPointsPerCriterion.
/**
* GET /exercises/:exerciseId/results-with-points-per-criterion : get the successful results for an exercise, ordered ascending by build completion date.
* Also contains for each result the points the student achieved with manual feedback. Those points are grouped as sum for each grading criterion.
*
* @param exerciseId of the exercise for which to retrieve the results.
* @param withSubmissions defines if submissions are loaded from the database for the results.
* @return the ResponseEntity with status 200 (OK) and the list of results with points in body.
*/
@GetMapping("exercises/{exerciseId}/results-with-points-per-criterion")
@PreAuthorize("hasRole('INSTRUCTOR')")
public ResponseEntity<List<ResultWithPointsPerGradingCriterionDTO>> getResultsForExerciseWithPointsPerCriterion(@PathVariable Long exerciseId, @RequestParam(defaultValue = "true") boolean withSubmissions) {
final Exercise exercise = exerciseRepository.findByIdElseThrow(exerciseId);
authCheckService.checkHasAtLeastRoleForExerciseElseThrow(Role.INSTRUCTOR, exercise, null);
final List<StudentParticipation> participations;
if (exercise.isExamExercise()) {
participations = studentParticipationRepository.findByExerciseIdWithEagerSubmissionsResultAssessorFeedbacksIgnoreTestRuns(exerciseId);
} else {
participations = studentParticipationRepository.findByExerciseIdWithEagerSubmissionsResultAssessorFeedbacks(exerciseId);
}
final Course course = exercise.getCourseViaExerciseGroupOrCourseMember();
final List<Result> results = resultsForExercise(exercise, participations, withSubmissions);
final List<ResultWithPointsPerGradingCriterionDTO> resultsWithPoints = results.stream().map(result -> resultRepository.calculatePointsPerGradingCriterion(result, course)).toList();
return ResponseEntity.ok().body(resultsWithPoints);
}
Aggregations