Search in sources :

Example 36 with ExerciseGroup

use of de.tum.in.www1.artemis.domain.exam.ExerciseGroup in project Artemis by ls1intum.

the class ExamService method validateForStudentExamGeneration.

/**
 * Validates exercise settings.
 *
 * @param exam exam which is validated
 * @throws BadRequestAlertException an exception if the exam is not configured correctly
 */
public void validateForStudentExamGeneration(Exam exam) throws BadRequestAlertException {
    List<ExerciseGroup> exerciseGroups = exam.getExerciseGroups();
    long numberOfExercises = exam.getNumberOfExercisesInExam() != null ? exam.getNumberOfExercisesInExam() : 0;
    long numberOfOptionalExercises = numberOfExercises - exerciseGroups.stream().filter(ExerciseGroup::getIsMandatory).count();
    // Ensure that all exercise groups have at least one exercise
    for (ExerciseGroup exerciseGroup : exam.getExerciseGroups()) {
        if (exerciseGroup.getExercises().isEmpty()) {
            throw new BadRequestAlertException("All exercise groups must have at least one exercise", "Exam", "artemisApp.exam.validation.atLeastOneExercisePerExerciseGroup");
        }
    }
    // Check that numberOfExercisesInExam is set
    if (exam.getNumberOfExercisesInExam() == null) {
        throw new BadRequestAlertException("The number of exercises in the exam is not set.", "Exam", "artemisApp.exam.validation.numberOfExercisesInExamNotSet");
    }
    // Check that there are enough exercise groups
    if (exam.getExerciseGroups().size() < exam.getNumberOfExercisesInExam()) {
        throw new BadRequestAlertException("The number of exercise groups is too small", "Exam", "artemisApp.exam.validation.tooFewExerciseGroups");
    }
    // Check that there are not too much mandatory exercise groups
    if (numberOfOptionalExercises < 0) {
        throw new BadRequestAlertException("The number of mandatory exercise groups is too large", "Exam", "artemisApp.exam.validation.tooManyMandatoryExerciseGroups");
    }
    // Ensure that all exercises in an exercise group have the same meaning for the exam score calculation
    for (ExerciseGroup exerciseGroup : exam.getExerciseGroups()) {
        Set<IncludedInOverallScore> meaningsForScoreCalculation = exerciseGroup.getExercises().stream().map(Exercise::getIncludedInOverallScore).collect(Collectors.toSet());
        if (meaningsForScoreCalculation.size() > 1) {
            throw new BadRequestAlertException("All exercises in an exercise group must have the same meaning for the exam score", "Exam", "artemisApp.exam.validation.allExercisesInExerciseGroupOfSameIncludedType");
        }
    }
    // Check that the exam max points is set
    if (exam.getMaxPoints() == 0) {
        throw new BadRequestAlertException("The exam max points can not be 0.", "Exam", "artemisApp.exam.validation.maxPointsNotSet");
    }
    // Ensure that all exercises in an exercise group have the same amount of max points and max bonus points
    for (ExerciseGroup exerciseGroup : exam.getExerciseGroups()) {
        Set<Double> allMaxPoints = exerciseGroup.getExercises().stream().map(Exercise::getMaxPoints).collect(Collectors.toSet());
        Set<Double> allBonusPoints = exerciseGroup.getExercises().stream().map(Exercise::getBonusPoints).collect(Collectors.toSet());
        if (allMaxPoints.size() > 1 || allBonusPoints.size() > 1) {
            throw new BadRequestAlertException("All exercises in an exercise group need to give the same amount of points", "Exam", "artemisApp.exam.validation.allExercisesInExerciseGroupGiveSameNumberOfPoints");
        }
    }
    // Ensure that the sum of all max points of mandatory exercise groups is not bigger than the max points set in the exam
    // At this point we are already sure that each exercise group has at least one exercise, all exercises in the group have the same no of points
    // and all are of the same calculation type, therefore we can just use any as representation for the group here
    Double pointsReachableByMandatoryExercises = 0.0;
    Set<ExerciseGroup> mandatoryExerciseGroups = exam.getExerciseGroups().stream().filter(ExerciseGroup::getIsMandatory).collect(Collectors.toSet());
    for (ExerciseGroup exerciseGroup : mandatoryExerciseGroups) {
        Exercise groupRepresentativeExercise = exerciseGroup.getExercises().stream().findAny().get();
        if (groupRepresentativeExercise.getIncludedInOverallScore().equals(IncludedInOverallScore.INCLUDED_COMPLETELY)) {
            pointsReachableByMandatoryExercises += groupRepresentativeExercise.getMaxPoints();
        }
    }
    if (pointsReachableByMandatoryExercises > exam.getMaxPoints()) {
        throw new BadRequestAlertException("Check that you set the exam max points correctly! The max points a student can earn in the mandatory exercise groups is too big", "Exam", "artemisApp.exam.validation.tooManyMaxPoints");
    }
    // Ensure that the sum of all max points of all exercise groups is at least as big as the max points set in the exam
    Double pointsReachable = 0.0;
    for (ExerciseGroup exerciseGroup : exam.getExerciseGroups()) {
        Exercise groupRepresentativeExercise = exerciseGroup.getExercises().stream().findAny().get();
        if (groupRepresentativeExercise.getIncludedInOverallScore().equals(IncludedInOverallScore.INCLUDED_COMPLETELY)) {
            pointsReachable += groupRepresentativeExercise.getMaxPoints();
        }
    }
    if (pointsReachable < exam.getMaxPoints()) {
        throw new BadRequestAlertException("Check that you set the exam max points correctly! The max points a student can earn in the exercise groups is too low", "Exam", "artemisApp.exam.validation.tooFewMaxPoints");
    }
}
Also used : BadRequestAlertException(de.tum.in.www1.artemis.web.rest.errors.BadRequestAlertException) QuizExercise(de.tum.in.www1.artemis.domain.quiz.QuizExercise) ModelingExercise(de.tum.in.www1.artemis.domain.modeling.ModelingExercise) ExerciseGroup(de.tum.in.www1.artemis.domain.exam.ExerciseGroup)

Example 37 with ExerciseGroup

use of de.tum.in.www1.artemis.domain.exam.ExerciseGroup in project Artemis by ls1intum.

the class ExerciseGroupResource method updateExerciseGroup.

/**
 * PUT /courses/{courseId}/exams/{examId}/exerciseGroups : Update an existing exercise group.
 *
 * @param courseId              the course to which the exercise group belongs to
 * @param examId                the exam to which the exercise group belongs to
 * @param updatedExerciseGroup  the exercise group to update
 * @return the ResponseEntity with status 200 (OK) and with the body of the updated exercise group
 * @throws URISyntaxException if the Location URI syntax is incorrect
 */
@PutMapping("/courses/{courseId}/exams/{examId}/exerciseGroups")
@PreAuthorize("hasRole('EDITOR')")
public ResponseEntity<ExerciseGroup> updateExerciseGroup(@PathVariable Long courseId, @PathVariable Long examId, @RequestBody ExerciseGroup updatedExerciseGroup) throws URISyntaxException {
    log.debug("REST request to update an exercise group : {}", updatedExerciseGroup);
    if (updatedExerciseGroup.getId() == null) {
        return createExerciseGroup(courseId, examId, updatedExerciseGroup);
    }
    if (updatedExerciseGroup.getExam() == null) {
        throw new ConflictException("The exercise group has to belong to an exam.", ENTITY_NAME, "missingExam");
    }
    examAccessService.checkCourseAndExamAndExerciseGroupAccessElseThrow(Role.EDITOR, courseId, examId, updatedExerciseGroup);
    ExerciseGroup result = exerciseGroupRepository.save(updatedExerciseGroup);
    return ResponseEntity.ok().headers(HeaderUtil.createEntityUpdateAlert(applicationName, true, ENTITY_NAME, result.getTitle())).body(result);
}
Also used : ConflictException(de.tum.in.www1.artemis.web.rest.errors.ConflictException) ExerciseGroup(de.tum.in.www1.artemis.domain.exam.ExerciseGroup) PreAuthorize(org.springframework.security.access.prepost.PreAuthorize)

Example 38 with ExerciseGroup

use of de.tum.in.www1.artemis.domain.exam.ExerciseGroup in project Artemis by ls1intum.

the class ExerciseGroupResource method deleteExerciseGroup.

/**
 * DELETE /courses/{courseId}/exams/{examId}/exerciseGroups/{exerciseGroupId} : Delete the exercise group with the given id.
 *
 * @param courseId          the course to which the exercise group belongs to
 * @param examId            the exam to which the exercise group belongs to
 * @param exerciseGroupId   the id of the exercise group to delete
 * @param deleteStudentReposBuildPlans boolean which states whether the corresponding student build plans should be deleted
 * @param deleteBaseReposBuildPlans boolean which states whether the corresponding base build plans should be deleted
 * @return the ResponseEntity with status 200 (OK)
 */
@DeleteMapping("/courses/{courseId}/exams/{examId}/exerciseGroups/{exerciseGroupId}")
@PreAuthorize("hasRole('INSTRUCTOR')")
public ResponseEntity<Void> deleteExerciseGroup(@PathVariable Long courseId, @PathVariable Long examId, @PathVariable Long exerciseGroupId, @RequestParam(defaultValue = "false") boolean deleteStudentReposBuildPlans, @RequestParam(defaultValue = "false") boolean deleteBaseReposBuildPlans) {
    log.info("REST request to delete exercise group : {}", exerciseGroupId);
    ExerciseGroup exerciseGroup = exerciseGroupRepository.findByIdWithExercisesElseThrow(exerciseGroupId);
    examAccessService.checkCourseAndExamAndExerciseGroupAccessElseThrow(Role.INSTRUCTOR, courseId, examId, exerciseGroup);
    User user = userRepository.getUser();
    AuditEvent auditEvent = new AuditEvent(user.getLogin(), Constants.DELETE_EXERCISE_GROUP, "exerciseGroup=" + exerciseGroup.getTitle());
    auditEventRepository.add(auditEvent);
    log.info("User {} has requested to delete the exercise group {}", user.getLogin(), exerciseGroup.getTitle());
    for (Exercise exercise : exerciseGroup.getExercises()) {
        exerciseDeletionService.delete(exercise.getId(), deleteStudentReposBuildPlans, deleteBaseReposBuildPlans);
    }
    // Remove the exercise group by removing it from the list of exercise groups of the corresponding exam.
    // This is necessary as @OrderColumn (exercise_group_order) needs continuous values. Otherwise the client will
    // receive null values for the gaps in exam.getExerciseGroups().
    Exam exam = examRepository.findByIdWithExerciseGroupsElseThrow(examId);
    List<ExerciseGroup> filteredExerciseGroups = exam.getExerciseGroups();
    filteredExerciseGroups.removeIf(exGroup -> exGroup.getId().equals(exerciseGroupId));
    exam.setExerciseGroups(filteredExerciseGroups);
    examRepository.save(exam);
    return ResponseEntity.ok().headers(HeaderUtil.createEntityDeletionAlert(applicationName, true, ENTITY_NAME, exerciseGroup.getTitle())).build();
}
Also used : User(de.tum.in.www1.artemis.domain.User) Exercise(de.tum.in.www1.artemis.domain.Exercise) AuditEvent(org.springframework.boot.actuate.audit.AuditEvent) ExerciseGroup(de.tum.in.www1.artemis.domain.exam.ExerciseGroup) Exam(de.tum.in.www1.artemis.domain.exam.Exam) PreAuthorize(org.springframework.security.access.prepost.PreAuthorize)

Example 39 with ExerciseGroup

use of de.tum.in.www1.artemis.domain.exam.ExerciseGroup in project Artemis by ls1intum.

the class ExerciseGroupResource method createExerciseGroup.

/**
 * POST /courses/{courseId}/exams/{examId}/exerciseGroups : Create a new exercise group.
 *
 * @param courseId      the course to which the exercise group belongs to
 * @param examId        the exam to which the exercise group belongs to
 * @param exerciseGroup the exercise group to create
 * @return the ResponseEntity with status 201 (Created) and with the new exerciseGroup as body,
 *         or with status 400 (Bad Request) if the exerciseGroup has already an ID
 * @throws URISyntaxException if the Location URI syntax is incorrect
 */
@PostMapping("/courses/{courseId}/exams/{examId}/exerciseGroups")
@PreAuthorize("hasRole('EDITOR')")
public ResponseEntity<ExerciseGroup> createExerciseGroup(@PathVariable Long courseId, @PathVariable Long examId, @RequestBody ExerciseGroup exerciseGroup) throws URISyntaxException {
    log.debug("REST request to create an exercise group : {}", exerciseGroup);
    if (exerciseGroup.getId() != null) {
        throw new BadRequestAlertException("A new exerciseGroup cannot already have an ID", ENTITY_NAME, "idexists");
    }
    if (exerciseGroup.getExam() == null) {
        throw new ConflictException("The exercise group has to belong no an exam.", ENTITY_NAME, "missingExam");
    }
    if (!exerciseGroup.getExam().getId().equals(examId)) {
        throw new ConflictException("The exam connected to this group does not have the given exam id.", ENTITY_NAME, "wrongExamId");
    }
    examAccessService.checkCourseAndExamAccessForEditorElseThrow(courseId, examId);
    // Save the exerciseGroup as part of the exam to ensure that the order column is set correctly
    Exam examFromDB = examRepository.findByIdWithExerciseGroupsElseThrow(examId);
    examFromDB.addExerciseGroup(exerciseGroup);
    Exam savedExam = examRepository.save(examFromDB);
    ExerciseGroup savedExerciseGroup = savedExam.getExerciseGroups().get(savedExam.getExerciseGroups().size() - 1);
    return ResponseEntity.created(new URI("/api/courses/" + courseId + "/exams/" + examId + "/exerciseGroups/" + savedExerciseGroup.getId())).headers(HeaderUtil.createEntityCreationAlert(applicationName, true, ENTITY_NAME, savedExerciseGroup.getTitle())).body(savedExerciseGroup);
}
Also used : BadRequestAlertException(de.tum.in.www1.artemis.web.rest.errors.BadRequestAlertException) ConflictException(de.tum.in.www1.artemis.web.rest.errors.ConflictException) ExerciseGroup(de.tum.in.www1.artemis.domain.exam.ExerciseGroup) URI(java.net.URI) Exam(de.tum.in.www1.artemis.domain.exam.Exam) PreAuthorize(org.springframework.security.access.prepost.PreAuthorize)

Example 40 with ExerciseGroup

use of de.tum.in.www1.artemis.domain.exam.ExerciseGroup in project Artemis by ls1intum.

the class ModelingExerciseResource method createModelingExercise.

// TODO: most of these calls should be done in the context of a course
/**
 * POST modeling-exercises : Create a new modelingExercise.
 *
 * @param modelingExercise the modelingExercise to create
 * @return the ResponseEntity with status 201 (Created) and with body the new modelingExercise, or with status 400 (Bad Request) if the modelingExercise has already an ID
 * @throws URISyntaxException if the Location URI syntax is incorrect
 */
// TODO: we should add courses/{courseId} here
@PostMapping("modeling-exercises")
@PreAuthorize("hasRole('EDITOR')")
public ResponseEntity<ModelingExercise> createModelingExercise(@RequestBody ModelingExercise modelingExercise) throws URISyntaxException {
    log.debug("REST request to save ModelingExercise : {}", modelingExercise);
    if (modelingExercise.getId() != null) {
        throw new BadRequestAlertException("A new modeling exercise cannot already have an ID", ENTITY_NAME, "idexists");
    }
    if (modelingExercise.getTitle() == null) {
        throw new BadRequestAlertException("A new modeling exercise needs a title", ENTITY_NAME, "missingtitle");
    }
    // validates general settings: points, dates
    modelingExercise.validateGeneralSettings();
    // Valid exercises have set either a course or an exerciseGroup
    modelingExercise.checkCourseAndExerciseGroupExclusivity(ENTITY_NAME);
    // Retrieve the course over the exerciseGroup or the given courseId
    Course course = courseService.retrieveCourseOverExerciseGroupOrCourseId(modelingExercise);
    // Check that the user is authorized to create the exercise
    authCheckService.checkHasAtLeastRoleInCourseElseThrow(Role.EDITOR, course, null);
    // if exercise is created from scratch we create new knowledge instance
    modelingExercise.setKnowledge(modelAssessmentKnowledgeService.createNewKnowledge());
    ModelingExercise result = modelingExerciseRepository.save(modelingExercise);
    modelingExerciseService.scheduleOperations(result.getId());
    groupNotificationService.checkNotificationsForNewExercise(modelingExercise, instanceMessageSendService);
    return ResponseEntity.created(new URI("/api/modeling-exercises/" + result.getId())).headers(HeaderUtil.createEntityCreationAlert(applicationName, true, ENTITY_NAME, result.getId().toString())).body(result);
}
Also used : BadRequestAlertException(de.tum.in.www1.artemis.web.rest.errors.BadRequestAlertException) ModelingExercise(de.tum.in.www1.artemis.domain.modeling.ModelingExercise) URI(java.net.URI) PreAuthorize(org.springframework.security.access.prepost.PreAuthorize)

Aggregations

ExerciseGroup (de.tum.in.www1.artemis.domain.exam.ExerciseGroup)126 WithMockUser (org.springframework.security.test.context.support.WithMockUser)76 Test (org.junit.jupiter.api.Test)70 Exam (de.tum.in.www1.artemis.domain.exam.Exam)64 StudentExam (de.tum.in.www1.artemis.domain.exam.StudentExam)46 ModelingExercise (de.tum.in.www1.artemis.domain.modeling.ModelingExercise)46 ParameterizedTest (org.junit.jupiter.params.ParameterizedTest)24 QuizExercise (de.tum.in.www1.artemis.domain.quiz.QuizExercise)22 PreAuthorize (org.springframework.security.access.prepost.PreAuthorize)22 AbstractSpringIntegrationBambooBitbucketJiraTest (de.tum.in.www1.artemis.AbstractSpringIntegrationBambooBitbucketJiraTest)20 BadRequestAlertException (de.tum.in.www1.artemis.web.rest.errors.BadRequestAlertException)20 StudentParticipation (de.tum.in.www1.artemis.domain.participation.StudentParticipation)18 EntityNotFoundException (de.tum.in.www1.artemis.web.rest.errors.EntityNotFoundException)14 TextExercise (de.tum.in.www1.artemis.domain.TextExercise)12 ZonedDateTime (java.time.ZonedDateTime)12 de.tum.in.www1.artemis.domain (de.tum.in.www1.artemis.domain)10 de.tum.in.www1.artemis.repository (de.tum.in.www1.artemis.repository)10 Collectors (java.util.stream.Collectors)10 ModelingSubmission (de.tum.in.www1.artemis.domain.modeling.ModelingSubmission)8 Participation (de.tum.in.www1.artemis.domain.participation.Participation)8