use of de.tum.in.www1.artemis.service.feature.FeatureToggle 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.service.feature.FeatureToggle in project ArTEMiS by ls1intum.
the class ProgrammingExerciseExportImportResource method exportSolutionRepository.
/**
* GET /programming-exercises/:exerciseId/export-solution-repository : sends a solution repository as a zip file without .git directory.
* @param exerciseId The id of the programming exercise
* @return ResponseEntity with status
* @throws IOException if something during the zip process went wrong
*/
@GetMapping(EXPORT_SOLUTION_REPOSITORY)
@PreAuthorize("hasRole('USER')")
@FeatureToggle(Feature.ProgrammingExercises)
public ResponseEntity<Resource> exportSolutionRepository(@PathVariable long exerciseId) throws IOException {
var programmingExercise = programmingExerciseRepository.findByIdElseThrow(exerciseId);
Role atLeastRole = programmingExercise.isExampleSolutionPublished() ? Role.STUDENT : Role.TEACHING_ASSISTANT;
authCheckService.checkHasAtLeastRoleForExerciseElseThrow(atLeastRole, programmingExercise, null);
long start = System.nanoTime();
Optional<File> zipFile = programmingExerciseExportService.exportSolutionRepositoryForExercise(programmingExercise.getId(), new ArrayList<>());
return returnZipFileForRepositoryExport(zipFile, RepositoryType.SOLUTION.getName(), programmingExercise, start);
}
use of de.tum.in.www1.artemis.service.feature.FeatureToggle in project ArTEMiS by ls1intum.
the class ProgrammingExerciseExportImportResource method exportInstructorExercise.
/**
* GET /programming-exercises/:exerciseId/export-instructor-exercise
* @param exerciseId The id of the programming exercise
* @return ResponseEntity with status
* @throws IOException if something during the zip process went wrong
*/
@GetMapping(EXPORT_INSTRUCTOR_EXERCISE)
@PreAuthorize("hasRole('INSTRUCTOR')")
@FeatureToggle(Feature.ProgrammingExercises)
public ResponseEntity<Resource> exportInstructorExercise(@PathVariable long exerciseId) throws IOException {
var programmingExercise = programmingExerciseRepository.findByIdElseThrow(exerciseId);
authCheckService.checkHasAtLeastRoleForExerciseElseThrow(Role.INSTRUCTOR, programmingExercise, null);
long start = System.nanoTime();
var path = programmingExerciseExportService.exportProgrammingExerciseInstructorMaterial(programmingExercise, new ArrayList<>());
if (path == null) {
return ResponseEntity.badRequest().headers(HeaderUtil.createFailureAlert(applicationName, true, ENTITY_NAME, "internalServerError", "There was an error on the server and the zip file could not be created.")).body(null);
}
var finalZipFile = path.toFile();
InputStreamResource resource = new InputStreamResource(new FileInputStream(finalZipFile));
log.info("Export of the programming exercise {} with title '{}' was successful in {}.", programmingExercise.getId(), programmingExercise.getTitle(), formatDurationFrom(start));
return ResponseEntity.ok().contentLength(finalZipFile.length()).contentType(MediaType.APPLICATION_OCTET_STREAM).header("filename", finalZipFile.getName()).body(resource);
}
use of de.tum.in.www1.artemis.service.feature.FeatureToggle in project ArTEMiS by ls1intum.
the class ProgrammingExercisePlagiarismResource method checkPlagiarism.
/**
* GET /programming-exercises/{exerciseId}/check-plagiarism : Start the automated plagiarism detection for the given exercise and return its result.
*
* @param exerciseId The ID of the programming exercise for which the plagiarism check should be executed
* @param similarityThreshold ignore comparisons whose similarity is below this threshold (%)
* @param minimumScore consider only submissions whose score is greater or equal to this value
* @return the ResponseEntity with status 200 (OK) and the list of at most 500 pair-wise submissions with a similarity above the given threshold (e.g. 50%).
* @throws ExitException is thrown if JPlag exits unexpectedly
* @throws IOException is thrown for file handling errors
*/
@GetMapping(CHECK_PLAGIARISM)
@PreAuthorize("hasRole('EDITOR')")
@FeatureToggle({ Feature.ProgrammingExercises, Feature.PlagiarismChecks })
public ResponseEntity<TextPlagiarismResult> checkPlagiarism(@PathVariable long exerciseId, @RequestParam float similarityThreshold, @RequestParam int minimumScore) throws ExitException, IOException {
ProgrammingExercise programmingExercise = programmingExerciseRepository.findByIdElseThrow(exerciseId);
authCheckService.checkHasAtLeastRoleForExerciseElseThrow(Role.EDITOR, programmingExercise, null);
ProgrammingLanguage language = programmingExercise.getProgrammingLanguage();
ProgrammingLanguageFeature programmingLanguageFeature = programmingLanguageFeatureService.get().getProgrammingLanguageFeatures(language);
if (!programmingLanguageFeature.isPlagiarismCheckSupported()) {
return ResponseEntity.badRequest().headers(HeaderUtil.createFailureAlert(applicationName, true, ENTITY_NAME, "programmingLanguageNotSupported", "Artemis does not support plagiarism checks for the programming language " + language)).body(null);
}
long start = System.nanoTime();
log.info("Start programmingPlagiarismDetectionService.checkPlagiarism for exercise {}", exerciseId);
TextPlagiarismResult result = programmingPlagiarismDetectionService.checkPlagiarism(exerciseId, similarityThreshold, minimumScore);
log.info("Finished programmingExerciseExportService.checkPlagiarism call for {} comparisons in {}", result.getComparisons().size(), TimeLogUtil.formatDurationFrom(start));
for (var comparison : result.getComparisons()) {
comparison.setPlagiarismResult(null);
comparison.getSubmissionA().setPlagiarismComparison(null);
comparison.getSubmissionB().setPlagiarismComparison(null);
}
return ResponseEntity.ok(result);
}
use of de.tum.in.www1.artemis.service.feature.FeatureToggle in project ArTEMiS by ls1intum.
the class ProgrammingExercisePlagiarismResource method getPlagiarismResult.
/**
* GET /programming-exercises/{exerciseId}/plagiarism-result : Return the latest plagiarism result or null, if no plagiarism was detected for this exercise yet.
*
* @param exerciseId ID of the programming exercise for which the plagiarism result should be returned
* @return The ResponseEntity with status 200 (Ok) or with status 400 (Bad Request) if the parameters are invalid
*/
@GetMapping(PLAGIARISM_RESULT)
@PreAuthorize("hasRole('EDITOR')")
@FeatureToggle(Feature.ProgrammingExercises)
public ResponseEntity<TextPlagiarismResult> getPlagiarismResult(@PathVariable long exerciseId) {
log.debug("REST request to get the latest plagiarism result for the programming exercise with id: {}", exerciseId);
ProgrammingExercise programmingExercise = programmingExerciseRepository.findByIdElseThrow(exerciseId);
authCheckService.checkHasAtLeastRoleForExerciseElseThrow(Role.EDITOR, programmingExercise, null);
var plagiarismResult = plagiarismResultRepository.findFirstByExerciseIdOrderByLastModifiedDateDescOrNull(programmingExercise.getId());
if (plagiarismResult != null) {
for (var comparison : plagiarismResult.getComparisons()) {
comparison.setPlagiarismResult(null);
comparison.getSubmissionA().setPlagiarismComparison(null);
comparison.getSubmissionB().setPlagiarismComparison(null);
}
}
return ResponseEntity.ok((TextPlagiarismResult) plagiarismResult);
}
Aggregations