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);
}
use of de.tum.in.www1.artemis.domain.exam.ExerciseGroup in project ArTEMiS by ls1intum.
the class ModelingExerciseResource method updateModelingExercise.
/**
* PUT modeling-exercises : Updates an existing modelingExercise.
*
* @param modelingExercise the modelingExercise to update
* @param notificationText the text shown to students
* @return the ResponseEntity with status 200 (OK) and with body the updated modelingExercise, or with status 400 (Bad Request) if the modelingExercise is not valid, or with
* status 500 (Internal Server Error) if the modelingExercise couldn't be updated
* @throws URISyntaxException if the Location URI syntax is incorrect
*/
@PutMapping("modeling-exercises")
@PreAuthorize("hasRole('EDITOR')")
public ResponseEntity<ModelingExercise> updateModelingExercise(@RequestBody ModelingExercise modelingExercise, @RequestParam(value = "notificationText", required = false) String notificationText) throws URISyntaxException {
log.debug("REST request to update ModelingExercise : {}", modelingExercise);
if (modelingExercise.getId() == null) {
return createModelingExercise(modelingExercise);
}
// validates general settings: points, dates
modelingExercise.validateGeneralSettings();
// Valid exercises have set either a course or an exerciseGroup
modelingExercise.checkCourseAndExerciseGroupExclusivity(ENTITY_NAME);
// Check that the user is authorized to update the exercise
var user = userRepository.getUserWithGroupsAndAuthorities();
// Important: use the original exercise for permission check
final ModelingExercise modelingExerciseBeforeUpdate = modelingExerciseRepository.findByIdElseThrow(modelingExercise.getId());
authCheckService.checkHasAtLeastRoleForExerciseElseThrow(Role.EDITOR, modelingExerciseBeforeUpdate, user);
// Forbid changing the course the exercise belongs to.
if (!Objects.equals(modelingExerciseBeforeUpdate.getCourseViaExerciseGroupOrCourseMember().getId(), modelingExercise.getCourseViaExerciseGroupOrCourseMember().getId())) {
throw new ConflictException("Exercise course id does not match the stored course id", ENTITY_NAME, "cannotChangeCourseId");
}
// Forbid conversion between normal course exercise and exam exercise
exerciseService.checkForConversionBetweenExamAndCourseExercise(modelingExercise, modelingExerciseBeforeUpdate, ENTITY_NAME);
ModelingExercise updatedModelingExercise = modelingExerciseRepository.save(modelingExercise);
exerciseService.logUpdate(modelingExercise, modelingExercise.getCourseViaExerciseGroupOrCourseMember(), user);
exerciseService.updatePointsInRelatedParticipantScores(modelingExerciseBeforeUpdate, updatedModelingExercise);
participationRepository.removeIndividualDueDatesIfBeforeDueDate(updatedModelingExercise, modelingExerciseBeforeUpdate.getDueDate());
modelingExerciseService.scheduleOperations(updatedModelingExercise.getId());
exerciseService.checkExampleSubmissions(updatedModelingExercise);
groupNotificationService.checkAndCreateAppropriateNotificationsWhenUpdatingExercise(modelingExerciseBeforeUpdate, updatedModelingExercise, notificationText, instanceMessageSendService);
return ResponseEntity.ok().headers(HeaderUtil.createEntityUpdateAlert(applicationName, true, ENTITY_NAME, modelingExercise.getId().toString())).body(updatedModelingExercise);
}
use of de.tum.in.www1.artemis.domain.exam.ExerciseGroup in project ArTEMiS by ls1intum.
the class ProgrammingExerciseExportImportResource method importProgrammingExercise.
/**
* POST /programming-exercises/import: Imports an existing programming exercise into an existing course
* <p>
* This will import the whole exercise, including all base build plans (template, solution) and repositories
* (template, solution, test). Referenced entities, s.a. the test cases or the hints will get cloned and assigned
* a new id. For a concrete list of what gets copied and what not have a look
* at {@link ProgrammingExerciseImportService#importProgrammingExerciseBasis(ProgrammingExercise, ProgrammingExercise)}
*
* @param sourceExerciseId The ID of the original exercise which should get imported
* @param newExercise The new exercise containing values that should get overwritten in the imported exercise, s.a. the title or difficulty
* @param recreateBuildPlans Option determining whether the build plans should be copied or re-created from scratch
* @param updateTemplate Option determining whether the template files should be updated with the most recent template version
* @return The imported exercise (200), a not found error (404) if the template does not exist, or a forbidden error
* (403) if the user is not at least an instructor in the target course.
* @see ProgrammingExerciseImportService#importProgrammingExerciseBasis(ProgrammingExercise, ProgrammingExercise)
* @see ProgrammingExerciseImportService#importBuildPlans(ProgrammingExercise, ProgrammingExercise)
* @see ProgrammingExerciseImportService#importRepositories(ProgrammingExercise, ProgrammingExercise)
*/
@PostMapping(IMPORT)
@PreAuthorize("hasRole('EDITOR')")
@FeatureToggle(Feature.ProgrammingExercises)
public ResponseEntity<ProgrammingExercise> importProgrammingExercise(@PathVariable long sourceExerciseId, @RequestBody ProgrammingExercise newExercise, @RequestParam(defaultValue = "false") boolean recreateBuildPlans, @RequestParam(defaultValue = "false") boolean updateTemplate) {
if (sourceExerciseId < 0) {
throw new BadRequestAlertException("Invalid source id when importing programming exercises", ENTITY_NAME, "invalidSourceExerciseId");
}
// Valid exercises have set either a course or an exerciseGroup
newExercise.checkCourseAndExerciseGroupExclusivity(ENTITY_NAME);
log.debug("REST request to import programming exercise {} into course {}", sourceExerciseId, newExercise.getCourseViaExerciseGroupOrCourseMember().getId());
newExercise.validateGeneralSettings();
newExercise.validateProgrammingSettings();
validateStaticCodeAnalysisSettings(newExercise);
final var user = userRepository.getUserWithGroupsAndAuthorities();
Course course = courseService.retrieveCourseOverExerciseGroupOrCourseId(newExercise);
authCheckService.checkHasAtLeastRoleInCourseElseThrow(Role.EDITOR, course, user);
// Validate course settings
programmingExerciseRepository.validateCourseSettings(newExercise, course);
final var originalProgrammingExercise = programmingExerciseRepository.findByIdWithEagerTestCasesStaticCodeAnalysisCategoriesHintsAndTemplateAndSolutionParticipationsAndAuxReposAndTasksWithTestCases(sourceExerciseId).orElseThrow(() -> new EntityNotFoundException("ProgrammingExercise", sourceExerciseId));
// The static code analysis flag can only change, if the build plans are recreated and the template is upgraded
if (newExercise.isStaticCodeAnalysisEnabled() != originalProgrammingExercise.isStaticCodeAnalysisEnabled() && !(recreateBuildPlans && updateTemplate)) {
throw new BadRequestAlertException("Static code analysis can only change, if the recreation of build plans and update of template files is activated", ENTITY_NAME, "staticCodeAnalysisCannotChange");
}
// If the new exercise has a submission policy, it must be validated.
if (newExercise.getSubmissionPolicy() != null) {
submissionPolicyService.validateSubmissionPolicy(newExercise.getSubmissionPolicy());
}
// Check if the user has the rights to access the original programming exercise
Course originalCourse = courseService.retrieveCourseOverExerciseGroupOrCourseId(originalProgrammingExercise);
authCheckService.checkHasAtLeastRoleInCourseElseThrow(Role.EDITOR, originalCourse, user);
newExercise.generateAndSetProjectKey();
programmingExerciseService.checkIfProjectExists(newExercise);
final var importedProgrammingExercise = programmingExerciseImportService.importProgrammingExerciseBasis(originalProgrammingExercise, newExercise);
programmingExerciseImportService.importRepositories(originalProgrammingExercise, importedProgrammingExercise);
// Update the template files
if (updateTemplate) {
TemplateUpgradeService upgradeService = templateUpgradePolicy.getUpgradeService(importedProgrammingExercise.getProgrammingLanguage());
upgradeService.upgradeTemplate(importedProgrammingExercise);
}
HttpHeaders responseHeaders;
// Copy or recreate the build plans
try {
if (recreateBuildPlans) {
// Create completely new build plans for the exercise
programmingExerciseService.setupBuildPlansForNewExercise(importedProgrammingExercise);
} else {
// We have removed the automatic build trigger from test to base for new programming exercises.
// We also remove this build trigger in the case of an import as the source exercise might still have this trigger.
// The importBuildPlans method includes this process
programmingExerciseImportService.importBuildPlans(originalProgrammingExercise, importedProgrammingExercise);
}
responseHeaders = HeaderUtil.createEntityCreationAlert(applicationName, true, ENTITY_NAME, importedProgrammingExercise.getTitle());
} catch (Exception e) {
responseHeaders = HeaderUtil.createFailureAlert(applicationName, true, ENTITY_NAME, "importExerciseTriggerPlanFail", "Unable to trigger imported build plans");
}
programmingExerciseService.scheduleOperations(importedProgrammingExercise.getId());
// Remove unnecessary fields
importedProgrammingExercise.setTestCases(null);
importedProgrammingExercise.setStaticCodeAnalysisCategories(null);
importedProgrammingExercise.setTemplateParticipation(null);
importedProgrammingExercise.setSolutionParticipation(null);
importedProgrammingExercise.setExerciseHints(null);
importedProgrammingExercise.setTasks(null);
return ResponseEntity.ok().headers(responseHeaders).body(importedProgrammingExercise);
}
use of de.tum.in.www1.artemis.domain.exam.ExerciseGroup in project ArTEMiS by ls1intum.
the class ModelingExerciseIntegrationTest method testUpdateModelingExerciseForExam_invalidExercise_dates.
@ParameterizedTest(name = "{displayName} [{index}] {argumentsWithNames}")
@ArgumentsSource(InvalidExamExerciseDatesArgumentProvider.class)
@WithMockUser(username = "instructor1", roles = "INSTRUCTOR")
public void testUpdateModelingExerciseForExam_invalidExercise_dates(InvalidExamExerciseDateConfiguration invalidDates) throws Exception {
ExerciseGroup exerciseGroup = database.addExerciseGroupWithExamAndCourse(true);
ModelingExercise modelingExercise = ModelFactory.generateModelingExerciseForExam(DiagramType.ClassDiagram, exerciseGroup);
modelingExerciseRepository.save(modelingExercise);
request.postWithResponseBody("/api/modeling-exercises/", invalidDates.applyTo(modelingExercise), ProgrammingExercise.class, HttpStatus.BAD_REQUEST);
}
use of de.tum.in.www1.artemis.domain.exam.ExerciseGroup in project ArTEMiS by ls1intum.
the class ModelingExerciseIntegrationTest method createModelingExerciseForExam.
@Test
@WithMockUser(username = "instructor1", roles = "INSTRUCTOR")
public void createModelingExerciseForExam() throws Exception {
ExerciseGroup exerciseGroup = database.addExerciseGroupWithExamAndCourse(true);
ModelingExercise modelingExercise = ModelFactory.generateModelingExerciseForExam(DiagramType.ClassDiagram, exerciseGroup);
String title = "New Exam Modeling Exercise";
DifficultyLevel difficulty = DifficultyLevel.HARD;
modelingExercise.setTitle(title);
modelingExercise.setDifficulty(difficulty);
ModelingExercise newModelingExercise = request.postWithResponseBody("/api/modeling-exercises/", modelingExercise, ModelingExercise.class, HttpStatus.CREATED);
assertThat(newModelingExercise.getTitle()).as("modeling exercise title was correctly set").isEqualTo(title);
assertThat(newModelingExercise.getDifficulty()).as("modeling exercise difficulty was correctly set").isEqualTo(difficulty);
assertThat(newModelingExercise.isCourseExercise()).as("course was not set for exam exercise").isFalse();
assertThat(newModelingExercise.getExerciseGroup()).as("exerciseGroup was set for exam exercise").isNotNull();
assertThat(newModelingExercise.getExerciseGroup().getId()).as("exerciseGroupId was set correctly").isEqualTo(exerciseGroup.getId());
}
Aggregations