use of de.tum.in.www1.artemis.service.archival.ArchivalReportEntry in project ArTEMiS by ls1intum.
the class SubmissionExportService method createZipFileFromParticipations.
/**
* Creates a zip file from a list of participations for an exercise.
*
* The outputDir is used to store the zip file and temporary files used for zipping so make
* sure to delete it if it's no longer used.
*
* @param exercise the exercise in question
* @param participations a list of participations to include
* @param enableFilterAfterDueDate true, if all submissions that have been submitted after the due date should not be included in the file
* @param lateSubmissionFilter an optional date filter for submissions
* @param outputDir directory to store the temporary files in
* @param exportErrors a list of errors for submissions that couldn't be exported and are not included in the file
* @param reportData a list of all exercises and their statistics
* @return the zipped file
* @throws IOException if an error occurred while zipping
*/
private Optional<File> createZipFileFromParticipations(Exercise exercise, List<StudentParticipation> participations, boolean enableFilterAfterDueDate, @Nullable ZonedDateTime lateSubmissionFilter, Path outputDir, List<String> exportErrors, List<ArchivalReportEntry> reportData) throws IOException {
Course course = exercise.getCourseViaExerciseGroupOrCourseMember();
// Create unique name for directory
String zipGroupName = course.getShortName() + "-" + exercise.getTitle() + "-" + exercise.getId();
String cleanZipGroupName = fileService.removeIllegalCharacters(zipGroupName);
String zipFileName = cleanZipGroupName + "-" + ZonedDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd-Hmss")) + ".zip";
// Create directory
Path submissionsFolderPath = Path.of(outputDir.toString(), "zippedSubmissions", zipGroupName);
Path zipFilePath = Path.of(outputDir.toString(), "zippedSubmissions", zipFileName);
File submissionFolder = submissionsFolderPath.toFile();
if (!submissionFolder.exists() && !submissionFolder.mkdirs()) {
log.error("Couldn't create dir: {}", submissionFolder);
exportErrors.add("Cannot create directory: " + submissionFolder.toPath());
return Optional.empty();
}
// Create counter for log entry
MutableInt skippedEntries = new MutableInt();
// Save all Submissions
List<Path> submissionFilePaths = participations.stream().map(participation -> {
Submission latestSubmission = latestSubmission(participation, enableFilterAfterDueDate, lateSubmissionFilter);
if (latestSubmission == null) {
skippedEntries.increment();
return Optional.<Path>empty();
}
// create file path
String submissionFileName = exercise.getTitle() + "-" + participation.getParticipantIdentifier() + "-" + latestSubmission.getId() + this.getFileEndingForSubmission(latestSubmission);
Path submissionFilePath = Path.of(submissionsFolderPath.toString(), submissionFileName);
// store file
try {
this.saveSubmissionToFile(exercise, latestSubmission, submissionFilePath.toFile());
return Optional.of(submissionFilePath);
} catch (Exception ex) {
String message = "Could not create file " + submissionFilePath + " for exporting: " + ex.getMessage();
log.error(message, ex);
exportErrors.add(message);
return Optional.<Path>empty();
}
}).flatMap(Optional::stream).collect(Collectors.toList());
// Add report entry
reportData.add(new ArchivalReportEntry(exercise, fileService.removeIllegalCharacters(exercise.getTitle()), participations.size(), submissionFilePaths.size(), skippedEntries.intValue()));
if (submissionFilePaths.isEmpty()) {
return Optional.empty();
}
// zip stores submissions
try {
zipFileService.createZipFile(zipFilePath, submissionFilePaths, submissionsFolderPath);
} finally {
log.debug("Delete all temporary files");
fileService.deleteFiles(submissionFilePaths);
}
return Optional.of(zipFilePath.toFile());
}
use of de.tum.in.www1.artemis.service.archival.ArchivalReportEntry in project ArTEMiS by ls1intum.
the class SubmissionExportService method exportStudentSubmissions.
/**
* Exports student submissions to a zip file for an exercise.
*
* The outputDir is used to store the zip file and temporary files used for zipping so make
* sure to delete it if it's no longer used.
*
* @param exerciseId the id of the exercise to be exported
* @param submissionExportOptions the options for the export
* @param outputDir directory to store the temporary files in
* @param exportErrors a list of errors for submissions that couldn't be exported and are not included in the file
* @param reportData a list of all exercises and their statistics
* @return a reference to the zipped file
* @throws IOException if an error occurred while zipping
*/
public Optional<File> exportStudentSubmissions(Long exerciseId, SubmissionExportOptionsDTO submissionExportOptions, Path outputDir, List<String> exportErrors, List<ArchivalReportEntry> reportData) throws IOException {
Optional<Exercise> exerciseOpt = exerciseRepository.findWithEagerStudentParticipationsStudentAndSubmissionsById(exerciseId);
if (exerciseOpt.isEmpty()) {
return Optional.empty();
}
Exercise exercise = exerciseOpt.get();
// Select the participations that should be exported
List<StudentParticipation> exportedStudentParticipations;
if (submissionExportOptions.isExportAllParticipants()) {
exportedStudentParticipations = new ArrayList<>(exercise.getStudentParticipations());
} else {
List<String> participantIds = Arrays.stream(submissionExportOptions.getParticipantIdentifierList().split(",")).map(String::trim).toList();
exportedStudentParticipations = exercise.getStudentParticipations().stream().filter(participation -> participantIds.contains(participation.getParticipantIdentifier())).collect(Collectors.toList());
}
boolean enableFilterAfterDueDate = false;
ZonedDateTime filterLateSubmissionsDate = null;
if (submissionExportOptions.isFilterLateSubmissions()) {
if (submissionExportOptions.getFilterLateSubmissionsDate() == null) {
enableFilterAfterDueDate = true;
} else {
filterLateSubmissionsDate = submissionExportOptions.getFilterLateSubmissionsDate();
}
}
// Sort the student participations by id
exportedStudentParticipations.sort(Comparator.comparing(DomainObject::getId));
return this.createZipFileFromParticipations(exercise, exportedStudentParticipations, enableFilterAfterDueDate, filterLateSubmissionsDate, outputDir, exportErrors, reportData);
}
use of de.tum.in.www1.artemis.service.archival.ArchivalReportEntry in project Artemis by ls1intum.
the class SubmissionExportService method exportStudentSubmissions.
/**
* Exports student submissions to a zip file for an exercise.
*
* The outputDir is used to store the zip file and temporary files used for zipping so make
* sure to delete it if it's no longer used.
*
* @param exerciseId the id of the exercise to be exported
* @param submissionExportOptions the options for the export
* @param outputDir directory to store the temporary files in
* @param exportErrors a list of errors for submissions that couldn't be exported and are not included in the file
* @param reportData a list of all exercises and their statistics
* @return a reference to the zipped file
* @throws IOException if an error occurred while zipping
*/
public Optional<File> exportStudentSubmissions(Long exerciseId, SubmissionExportOptionsDTO submissionExportOptions, Path outputDir, List<String> exportErrors, List<ArchivalReportEntry> reportData) throws IOException {
Optional<Exercise> exerciseOpt = exerciseRepository.findWithEagerStudentParticipationsStudentAndSubmissionsById(exerciseId);
if (exerciseOpt.isEmpty()) {
return Optional.empty();
}
Exercise exercise = exerciseOpt.get();
// Select the participations that should be exported
List<StudentParticipation> exportedStudentParticipations;
if (submissionExportOptions.isExportAllParticipants()) {
exportedStudentParticipations = new ArrayList<>(exercise.getStudentParticipations());
} else {
List<String> participantIds = Arrays.stream(submissionExportOptions.getParticipantIdentifierList().split(",")).map(String::trim).toList();
exportedStudentParticipations = exercise.getStudentParticipations().stream().filter(participation -> participantIds.contains(participation.getParticipantIdentifier())).collect(Collectors.toList());
}
boolean enableFilterAfterDueDate = false;
ZonedDateTime filterLateSubmissionsDate = null;
if (submissionExportOptions.isFilterLateSubmissions()) {
if (submissionExportOptions.getFilterLateSubmissionsDate() == null) {
enableFilterAfterDueDate = true;
} else {
filterLateSubmissionsDate = submissionExportOptions.getFilterLateSubmissionsDate();
}
}
// Sort the student participations by id
exportedStudentParticipations.sort(Comparator.comparing(DomainObject::getId));
return this.createZipFileFromParticipations(exercise, exportedStudentParticipations, enableFilterAfterDueDate, filterLateSubmissionsDate, outputDir, exportErrors, reportData);
}
use of de.tum.in.www1.artemis.service.archival.ArchivalReportEntry in project Artemis by ls1intum.
the class ProgrammingExerciseExportService method exportProgrammingExerciseRepositories.
/**
* Export instructor repositories and optionally students' repositories in a zip file.
*
* The outputDir is used to store the zip file and temporary files used for zipping so make
* sure to delete it if it's no longer used.
*
* @param exercise the programming exercise
* @param includingStudentRepos flag for including the students repos as well
* @param outputDir the path to a directory that will be used to store the zipped programming exercise.
* @param exportErrors List of failures that occurred during the export
* @param reportData List of all exercises and their statistics
* @return the path to the zip file
*/
public Path exportProgrammingExerciseRepositories(ProgrammingExercise exercise, Boolean includingStudentRepos, Path outputDir, List<String> exportErrors, List<ArchivalReportEntry> reportData) {
log.info("Exporting programming exercise {} with title {}", exercise.getId(), exercise.getTitle());
// List to add paths of files that should be contained in the zip folder of exported programming exercise repositories:
// i.e., student repositories (if `includingStudentRepos` is true), instructor repositories template, solution and tests
var pathsToBeZipped = new ArrayList<Path>();
if (includingStudentRepos) {
// Lazy load student participation, sort by id, and set the export options
var studentParticipations = studentParticipationRepository.findByExerciseId(exercise.getId()).stream().map(studentParticipation -> (ProgrammingExerciseStudentParticipation) studentParticipation).sorted(Comparator.comparing(DomainObject::getId)).collect(Collectors.toList());
var exportOptions = new RepositoryExportOptionsDTO();
exportOptions.setHideStudentNameInZippedFolder(false);
// Export student repositories and add them to list
var exportedStudentRepositoryFiles = exportStudentRepositories(exercise, studentParticipations, exportOptions, outputDir, exportErrors).stream().filter(Objects::nonNull).toList();
pathsToBeZipped.addAll(exportedStudentRepositoryFiles);
}
// Export the template, solution, and tests repositories and add them to list
pathsToBeZipped.add(exportInstructorRepositoryForExercise(exercise.getId(), RepositoryType.TEMPLATE, outputDir, exportErrors).map(File::toPath).orElse(null));
pathsToBeZipped.add(exportInstructorRepositoryForExercise(exercise.getId(), RepositoryType.SOLUTION, outputDir, exportErrors).map(File::toPath).orElse(null));
pathsToBeZipped.add(exportInstructorRepositoryForExercise(exercise.getId(), RepositoryType.TESTS, outputDir, exportErrors).map(File::toPath).orElse(null));
List<AuxiliaryRepository> auxiliaryRepositories = auxiliaryRepositoryRepository.findByExerciseId(exercise.getId());
// Export the auxiliary repositories and add them to list
auxiliaryRepositories.forEach(auxiliaryRepository -> {
pathsToBeZipped.add(exportInstructorAuxiliaryRepositoryForExercise(exercise.getId(), auxiliaryRepository, outputDir, exportErrors).map(File::toPath).orElse(null));
});
// Setup path to store the zip file for the exported repositories
var timestamp = ZonedDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd-Hmss"));
String filename = exercise.getCourseViaExerciseGroupOrCourseMember().getShortName() + "-" + exercise.getTitle() + "-" + exercise.getId() + "-" + timestamp + ".zip";
String cleanFilename = fileService.removeIllegalCharacters(filename);
Path pathToZippedExercise = Path.of(outputDir.toString(), cleanFilename);
// Remove null elements and get the file path of each file to be included, i.e. each entry in the pathsToBeZipped list
List<Path> includedFilePathsNotNull = pathsToBeZipped.stream().filter(Objects::nonNull).collect(Collectors.toList());
String cleanProjectName = fileService.removeIllegalCharacters(exercise.getProjectName());
// Add report entry, programming repositories cannot be skipped
reportData.add(new ArchivalReportEntry(exercise, cleanProjectName, pathsToBeZipped.size(), includedFilePathsNotNull.size(), 0));
try {
// Only create zip file if there's files to zip
if (includedFilePathsNotNull.isEmpty()) {
String info = "Will not export programming exercise " + exercise.getId() + " with title " + exercise.getTitle() + " because it's empty";
log.info(info);
exportErrors.add(info);
return null;
}
// Create the zip folder of the exported programming exercise and return the path to the created folder
zipFileService.createZipFile(pathToZippedExercise, includedFilePathsNotNull, false);
return pathToZippedExercise;
} catch (Exception e) {
var error = "Failed to export programming exercise because the zip file " + pathToZippedExercise + " could not be created: " + e.getMessage();
log.info(error);
exportErrors.add(error);
return null;
}
}
use of de.tum.in.www1.artemis.service.archival.ArchivalReportEntry in project ArTEMiS by ls1intum.
the class ProgrammingExerciseExportService method exportProgrammingExerciseRepositories.
/**
* Export instructor repositories and optionally students' repositories in a zip file.
*
* The outputDir is used to store the zip file and temporary files used for zipping so make
* sure to delete it if it's no longer used.
*
* @param exercise the programming exercise
* @param includingStudentRepos flag for including the students repos as well
* @param outputDir the path to a directory that will be used to store the zipped programming exercise.
* @param exportErrors List of failures that occurred during the export
* @param reportData List of all exercises and their statistics
* @return the path to the zip file
*/
public Path exportProgrammingExerciseRepositories(ProgrammingExercise exercise, Boolean includingStudentRepos, Path outputDir, List<String> exportErrors, List<ArchivalReportEntry> reportData) {
log.info("Exporting programming exercise {} with title {}", exercise.getId(), exercise.getTitle());
// List to add paths of files that should be contained in the zip folder of exported programming exercise repositories:
// i.e., student repositories (if `includingStudentRepos` is true), instructor repositories template, solution and tests
var pathsToBeZipped = new ArrayList<Path>();
if (includingStudentRepos) {
// Lazy load student participation, sort by id, and set the export options
var studentParticipations = studentParticipationRepository.findByExerciseId(exercise.getId()).stream().map(studentParticipation -> (ProgrammingExerciseStudentParticipation) studentParticipation).sorted(Comparator.comparing(DomainObject::getId)).collect(Collectors.toList());
var exportOptions = new RepositoryExportOptionsDTO();
exportOptions.setHideStudentNameInZippedFolder(false);
// Export student repositories and add them to list
var exportedStudentRepositoryFiles = exportStudentRepositories(exercise, studentParticipations, exportOptions, outputDir, exportErrors).stream().filter(Objects::nonNull).toList();
pathsToBeZipped.addAll(exportedStudentRepositoryFiles);
}
// Export the template, solution, and tests repositories and add them to list
pathsToBeZipped.add(exportInstructorRepositoryForExercise(exercise.getId(), RepositoryType.TEMPLATE, outputDir, exportErrors).map(File::toPath).orElse(null));
pathsToBeZipped.add(exportInstructorRepositoryForExercise(exercise.getId(), RepositoryType.SOLUTION, outputDir, exportErrors).map(File::toPath).orElse(null));
pathsToBeZipped.add(exportInstructorRepositoryForExercise(exercise.getId(), RepositoryType.TESTS, outputDir, exportErrors).map(File::toPath).orElse(null));
List<AuxiliaryRepository> auxiliaryRepositories = auxiliaryRepositoryRepository.findByExerciseId(exercise.getId());
// Export the auxiliary repositories and add them to list
auxiliaryRepositories.forEach(auxiliaryRepository -> {
pathsToBeZipped.add(exportInstructorAuxiliaryRepositoryForExercise(exercise.getId(), auxiliaryRepository, outputDir, exportErrors).map(File::toPath).orElse(null));
});
// Setup path to store the zip file for the exported repositories
var timestamp = ZonedDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd-Hmss"));
String filename = exercise.getCourseViaExerciseGroupOrCourseMember().getShortName() + "-" + exercise.getTitle() + "-" + exercise.getId() + "-" + timestamp + ".zip";
String cleanFilename = fileService.removeIllegalCharacters(filename);
Path pathToZippedExercise = Path.of(outputDir.toString(), cleanFilename);
// Remove null elements and get the file path of each file to be included, i.e. each entry in the pathsToBeZipped list
List<Path> includedFilePathsNotNull = pathsToBeZipped.stream().filter(Objects::nonNull).collect(Collectors.toList());
String cleanProjectName = fileService.removeIllegalCharacters(exercise.getProjectName());
// Add report entry, programming repositories cannot be skipped
reportData.add(new ArchivalReportEntry(exercise, cleanProjectName, pathsToBeZipped.size(), includedFilePathsNotNull.size(), 0));
try {
// Only create zip file if there's files to zip
if (includedFilePathsNotNull.isEmpty()) {
String info = "Will not export programming exercise " + exercise.getId() + " with title " + exercise.getTitle() + " because it's empty";
log.info(info);
exportErrors.add(info);
return null;
}
// Create the zip folder of the exported programming exercise and return the path to the created folder
zipFileService.createZipFile(pathToZippedExercise, includedFilePathsNotNull, false);
return pathToZippedExercise;
} catch (Exception e) {
var error = "Failed to export programming exercise because the zip file " + pathToZippedExercise + " could not be created: " + e.getMessage();
log.info(error);
exportErrors.add(error);
return null;
}
}
Aggregations