Search in sources :

Example 1 with RepositoryExportOptionsDTO

use of de.tum.in.www1.artemis.web.rest.dto.RepositoryExportOptionsDTO in project ArTEMiS by ls1intum.

the class ProgrammingExerciseExportService method createZipForRepositoryWithParticipation.

/**
 * Checks out the repository for the given participation, zips it and adds the path to the given list of already
 * zipped repos.
 *
 * @param programmingExercise     The programming exercise for the participation
 * @param participation           The participation, for which the repository should get zipped
 * @param repositoryExportOptions The options, that should get applied to the zipped repo
 * @param outputDir The directory used for downloading and zipping the repository
 * @return The checked out and zipped repository
 * @throws IOException if zip file creation failed
 */
private Path createZipForRepositoryWithParticipation(final ProgrammingExercise programmingExercise, final ProgrammingExerciseStudentParticipation participation, final RepositoryExportOptionsDTO repositoryExportOptions, Path outputDir) throws IOException, UncheckedIOException {
    if (participation.getVcsRepositoryUrl() == null) {
        log.warn("Ignore participation {} for export, because its repository URL is null", participation.getId());
        return null;
    }
    try {
        // Checkout the repository
        Repository repository = gitService.getOrCheckoutRepository(participation, outputDir.toString());
        if (repository == null) {
            log.warn("Cannot checkout repository for participation id: {}", participation.getId());
            return null;
        }
        gitService.resetToOriginHead(repository);
        if (repositoryExportOptions.isFilterLateSubmissions()) {
            filterLateSubmissions(repositoryExportOptions, participation, repository);
        }
        if (repositoryExportOptions.isAddParticipantName()) {
            log.debug("Adding student or team name to participation {}", participation);
            addParticipantIdentifierToProjectName(repository, programmingExercise, participation);
        }
        if (repositoryExportOptions.isCombineStudentCommits()) {
            log.debug("Combining commits for participation {}", participation);
            gitService.combineAllStudentCommits(repository, programmingExercise, repositoryExportOptions.isAnonymizeStudentCommits());
        }
        if (repositoryExportOptions.isAnonymizeStudentCommits()) {
            log.debug("Anonymizing commits for participation {}", participation);
            gitService.anonymizeStudentCommits(repository, programmingExercise);
        }
        if (repositoryExportOptions.isNormalizeCodeStyle()) {
            try {
                log.debug("Normalizing code style for participation {}", participation);
                fileService.normalizeLineEndingsDirectory(repository.getLocalPath().toString());
                fileService.convertToUTF8Directory(repository.getLocalPath().toString());
            } catch (IOException ex) {
                log.warn("Cannot normalize code style in the repository {} due to the following exception: {}", repository.getLocalPath(), ex.getMessage());
            }
        }
        log.debug("Create temporary zip file for repository {}", repository.getLocalPath().toString());
        return gitService.zipRepositoryWithParticipation(repository, outputDir.toString(), repositoryExportOptions.isHideStudentNameInZippedFolder());
    } catch (GitAPIException | GitException ex) {
        log.error("Failed to create zip for participation id {} with exercise id {} because of the following exception ", participation.getId(), participation.getProgrammingExercise().getId(), ex);
        return null;
    }
}
Also used : GitAPIException(org.eclipse.jgit.api.errors.GitAPIException) StudentParticipationRepository(de.tum.in.www1.artemis.repository.StudentParticipationRepository) AuxiliaryRepositoryRepository(de.tum.in.www1.artemis.repository.AuxiliaryRepositoryRepository) ProgrammingExerciseRepository(de.tum.in.www1.artemis.repository.ProgrammingExerciseRepository) GitException(de.tum.in.www1.artemis.exception.GitException) UncheckedIOException(java.io.UncheckedIOException) IOException(java.io.IOException)

Example 2 with RepositoryExportOptionsDTO

use of de.tum.in.www1.artemis.web.rest.dto.RepositoryExportOptionsDTO 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;
    }
}
Also used : Path(java.nio.file.Path) XPath(javax.xml.xpath.XPath) RepositoryExportOptionsDTO(de.tum.in.www1.artemis.web.rest.dto.RepositoryExportOptionsDTO) ArchivalReportEntry(de.tum.in.www1.artemis.service.archival.ArchivalReportEntry) File(java.io.File) GitException(de.tum.in.www1.artemis.exception.GitException) GitAPIException(org.eclipse.jgit.api.errors.GitAPIException) UncheckedIOException(java.io.UncheckedIOException) SAXException(org.xml.sax.SAXException) TransformerException(javax.xml.transform.TransformerException) XPathException(javax.xml.xpath.XPathException) IOException(java.io.IOException) ParserConfigurationException(javax.xml.parsers.ParserConfigurationException)

Example 3 with RepositoryExportOptionsDTO

use of de.tum.in.www1.artemis.web.rest.dto.RepositoryExportOptionsDTO in project Artemis by ls1intum.

the class ProgrammingExerciseExportImportResource method exportSubmissionsByStudentLogins.

/**
 * POST /programming-exercises/:exerciseId/export-repos-by-participant-identifiers/:participantIdentifiers : sends all submissions from participantIdentifiers as zip
 *
 * @param exerciseId              the id of the exercise to get the repos from
 * @param participantIdentifiers  the identifiers of the participants (student logins or team short names) for whom to zip the submissions, separated by commas
 * @param repositoryExportOptions the options that should be used for the export
 * @return ResponseEntity with status
 * @throws IOException if something during the zip process went wrong
 */
@PostMapping(EXPORT_SUBMISSIONS_BY_PARTICIPANTS)
@PreAuthorize("hasRole('TA')")
@FeatureToggle(Feature.ProgrammingExercises)
public ResponseEntity<Resource> exportSubmissionsByStudentLogins(@PathVariable long exerciseId, @PathVariable String participantIdentifiers, @RequestBody RepositoryExportOptionsDTO repositoryExportOptions) throws IOException {
    var programmingExercise = programmingExerciseRepository.findByIdWithStudentParticipationsAndLegalSubmissionsElseThrow(exerciseId);
    User user = userRepository.getUserWithGroupsAndAuthorities();
    authCheckService.checkHasAtLeastRoleForExerciseElseThrow(Role.TEACHING_ASSISTANT, programmingExercise, user);
    if (repositoryExportOptions.isExportAllParticipants()) {
        // only instructors are allowed to download all repos
        authCheckService.checkHasAtLeastRoleForExerciseElseThrow(Role.INSTRUCTOR, programmingExercise, user);
    }
    if (repositoryExportOptions.getFilterLateSubmissionsDate() == null) {
        repositoryExportOptions.setFilterLateSubmissionsIndividualDueDate(true);
        repositoryExportOptions.setFilterLateSubmissionsDate(programmingExercise.getDueDate());
    }
    List<String> participantIdentifierList = new ArrayList<>();
    if (!repositoryExportOptions.isExportAllParticipants()) {
        participantIdentifiers = participantIdentifiers.replaceAll("\\s+", "");
        participantIdentifierList = Arrays.asList(participantIdentifiers.split(","));
    }
    // Select the participations that should be exported
    List<ProgrammingExerciseStudentParticipation> exportedStudentParticipations = new ArrayList<>();
    for (StudentParticipation studentParticipation : programmingExercise.getStudentParticipations()) {
        ProgrammingExerciseStudentParticipation programmingStudentParticipation = (ProgrammingExerciseStudentParticipation) studentParticipation;
        if (repositoryExportOptions.isExportAllParticipants() || (programmingStudentParticipation.getRepositoryUrl() != null && studentParticipation.getParticipant() != null && participantIdentifierList.contains(studentParticipation.getParticipantIdentifier()))) {
            exportedStudentParticipations.add(programmingStudentParticipation);
        }
    }
    return provideZipForParticipations(exportedStudentParticipations, programmingExercise, repositoryExportOptions);
}
Also used : User(de.tum.in.www1.artemis.domain.User) ArrayList(java.util.ArrayList) ProgrammingExerciseStudentParticipation(de.tum.in.www1.artemis.domain.participation.ProgrammingExerciseStudentParticipation) ProgrammingExerciseStudentParticipation(de.tum.in.www1.artemis.domain.participation.ProgrammingExerciseStudentParticipation) StudentParticipation(de.tum.in.www1.artemis.domain.participation.StudentParticipation) FeatureToggle(de.tum.in.www1.artemis.service.feature.FeatureToggle) PreAuthorize(org.springframework.security.access.prepost.PreAuthorize)

Example 4 with RepositoryExportOptionsDTO

use of de.tum.in.www1.artemis.web.rest.dto.RepositoryExportOptionsDTO in project ArTEMiS by ls1intum.

the class ProgrammingExerciseExportImportResource method exportSubmissionsByStudentLogins.

/**
 * POST /programming-exercises/:exerciseId/export-repos-by-participant-identifiers/:participantIdentifiers : sends all submissions from participantIdentifiers as zip
 *
 * @param exerciseId              the id of the exercise to get the repos from
 * @param participantIdentifiers  the identifiers of the participants (student logins or team short names) for whom to zip the submissions, separated by commas
 * @param repositoryExportOptions the options that should be used for the export
 * @return ResponseEntity with status
 * @throws IOException if something during the zip process went wrong
 */
@PostMapping(EXPORT_SUBMISSIONS_BY_PARTICIPANTS)
@PreAuthorize("hasRole('TA')")
@FeatureToggle(Feature.ProgrammingExercises)
public ResponseEntity<Resource> exportSubmissionsByStudentLogins(@PathVariable long exerciseId, @PathVariable String participantIdentifiers, @RequestBody RepositoryExportOptionsDTO repositoryExportOptions) throws IOException {
    var programmingExercise = programmingExerciseRepository.findByIdWithStudentParticipationsAndLegalSubmissionsElseThrow(exerciseId);
    User user = userRepository.getUserWithGroupsAndAuthorities();
    authCheckService.checkHasAtLeastRoleForExerciseElseThrow(Role.TEACHING_ASSISTANT, programmingExercise, user);
    if (repositoryExportOptions.isExportAllParticipants()) {
        // only instructors are allowed to download all repos
        authCheckService.checkHasAtLeastRoleForExerciseElseThrow(Role.INSTRUCTOR, programmingExercise, user);
    }
    if (repositoryExportOptions.getFilterLateSubmissionsDate() == null) {
        repositoryExportOptions.setFilterLateSubmissionsIndividualDueDate(true);
        repositoryExportOptions.setFilterLateSubmissionsDate(programmingExercise.getDueDate());
    }
    List<String> participantIdentifierList = new ArrayList<>();
    if (!repositoryExportOptions.isExportAllParticipants()) {
        participantIdentifiers = participantIdentifiers.replaceAll("\\s+", "");
        participantIdentifierList = Arrays.asList(participantIdentifiers.split(","));
    }
    // Select the participations that should be exported
    List<ProgrammingExerciseStudentParticipation> exportedStudentParticipations = new ArrayList<>();
    for (StudentParticipation studentParticipation : programmingExercise.getStudentParticipations()) {
        ProgrammingExerciseStudentParticipation programmingStudentParticipation = (ProgrammingExerciseStudentParticipation) studentParticipation;
        if (repositoryExportOptions.isExportAllParticipants() || (programmingStudentParticipation.getRepositoryUrl() != null && studentParticipation.getParticipant() != null && participantIdentifierList.contains(studentParticipation.getParticipantIdentifier()))) {
            exportedStudentParticipations.add(programmingStudentParticipation);
        }
    }
    return provideZipForParticipations(exportedStudentParticipations, programmingExercise, repositoryExportOptions);
}
Also used : User(de.tum.in.www1.artemis.domain.User) ArrayList(java.util.ArrayList) ProgrammingExerciseStudentParticipation(de.tum.in.www1.artemis.domain.participation.ProgrammingExerciseStudentParticipation) ProgrammingExerciseStudentParticipation(de.tum.in.www1.artemis.domain.participation.ProgrammingExerciseStudentParticipation) StudentParticipation(de.tum.in.www1.artemis.domain.participation.StudentParticipation) FeatureToggle(de.tum.in.www1.artemis.service.feature.FeatureToggle) PreAuthorize(org.springframework.security.access.prepost.PreAuthorize)

Example 5 with RepositoryExportOptionsDTO

use of de.tum.in.www1.artemis.web.rest.dto.RepositoryExportOptionsDTO 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;
    }
}
Also used : Path(java.nio.file.Path) XPath(javax.xml.xpath.XPath) RepositoryExportOptionsDTO(de.tum.in.www1.artemis.web.rest.dto.RepositoryExportOptionsDTO) ArchivalReportEntry(de.tum.in.www1.artemis.service.archival.ArchivalReportEntry) File(java.io.File) GitException(de.tum.in.www1.artemis.exception.GitException) GitAPIException(org.eclipse.jgit.api.errors.GitAPIException) UncheckedIOException(java.io.UncheckedIOException) SAXException(org.xml.sax.SAXException) TransformerException(javax.xml.transform.TransformerException) XPathException(javax.xml.xpath.XPathException) IOException(java.io.IOException) ParserConfigurationException(javax.xml.parsers.ParserConfigurationException)

Aggregations

GitException (de.tum.in.www1.artemis.exception.GitException)6 IOException (java.io.IOException)6 UncheckedIOException (java.io.UncheckedIOException)6 GitAPIException (org.eclipse.jgit.api.errors.GitAPIException)6 Path (java.nio.file.Path)4 ParserConfigurationException (javax.xml.parsers.ParserConfigurationException)4 TransformerException (javax.xml.transform.TransformerException)4 XPath (javax.xml.xpath.XPath)4 XPathException (javax.xml.xpath.XPathException)4 SAXException (org.xml.sax.SAXException)4 User (de.tum.in.www1.artemis.domain.User)2 ProgrammingExerciseStudentParticipation (de.tum.in.www1.artemis.domain.participation.ProgrammingExerciseStudentParticipation)2 StudentParticipation (de.tum.in.www1.artemis.domain.participation.StudentParticipation)2 AuxiliaryRepositoryRepository (de.tum.in.www1.artemis.repository.AuxiliaryRepositoryRepository)2 ProgrammingExerciseRepository (de.tum.in.www1.artemis.repository.ProgrammingExerciseRepository)2 StudentParticipationRepository (de.tum.in.www1.artemis.repository.StudentParticipationRepository)2 ArchivalReportEntry (de.tum.in.www1.artemis.service.archival.ArchivalReportEntry)2 FeatureToggle (de.tum.in.www1.artemis.service.feature.FeatureToggle)2 RepositoryExportOptionsDTO (de.tum.in.www1.artemis.web.rest.dto.RepositoryExportOptionsDTO)2 File (java.io.File)2