From 32c5c6946e8305f0ceac31a4aa4f6e4cb6c9f852 Mon Sep 17 00:00:00 2001 From: entholzer Date: Sun, 27 Oct 2024 19:41:42 +0100 Subject: [PATCH 1/5] enable export of auxiliary repos --- .../service/ProgrammingExerciseExportService.java | 2 ++ .../service/ProgrammingExerciseImportFromFileService.java | 8 ++++++++ .../from-file/exercise-import-from-file.component.ts | 5 +++++ 3 files changed, 15 insertions(+) diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingExerciseExportService.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingExerciseExportService.java index c8a4ae521e55..548c220d7293 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingExerciseExportService.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingExerciseExportService.java @@ -175,6 +175,8 @@ private Path exportProgrammingExerciseMaterialWithStudentReposOptional(Programmi } // Add problem statement as .md file + exercise.setAuxiliaryRepositories(auxiliaryRepositoryRepository.findByExerciseId(exercise.getId())); + exportProblemStatementAndEmbeddedFilesAndExerciseDetails(exercise, exportErrors, exportDir.orElseThrow(), pathsToBeZipped); return exportDir.orElseThrow(); diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingExerciseImportFromFileService.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingExerciseImportFromFileService.java index 3c8b1671cba9..16ddb557c665 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingExerciseImportFromFileService.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingExerciseImportFromFileService.java @@ -8,6 +8,7 @@ import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; +import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.stream.Stream; @@ -33,6 +34,7 @@ import de.tum.cit.aet.artemis.core.service.FileService; import de.tum.cit.aet.artemis.core.service.ProfileService; import de.tum.cit.aet.artemis.core.service.ZipFileService; +import de.tum.cit.aet.artemis.programming.domain.AuxiliaryRepository; import de.tum.cit.aet.artemis.programming.domain.ProgrammingExercise; import de.tum.cit.aet.artemis.programming.domain.Repository; import de.tum.cit.aet.artemis.programming.domain.RepositoryType; @@ -170,6 +172,12 @@ private void importRepositoriesFromFile(ProgrammingExercise newExercise, Path ba Repository templateRepo = gitService.getOrCheckoutRepository(new VcsRepositoryUri(newExercise.getTemplateRepositoryUri()), false); Repository solutionRepo = gitService.getOrCheckoutRepository(new VcsRepositoryUri(newExercise.getSolutionRepositoryUri()), false); Repository testRepo = gitService.getOrCheckoutRepository(new VcsRepositoryUri(newExercise.getTestRepositoryUri()), false); + List auxiliaryRepositories = new ArrayList<>(); + for (AuxiliaryRepository auxiliaryRepository : newExercise.getAuxiliaryRepositories()) { + auxiliaryRepositories.add(gitService.getOrCheckoutRepository(new VcsRepositoryUri(auxiliaryRepository.getRepositoryUri()), false)); + } + + // TODO import auxiliary repos copyImportedExerciseContentToRepositories(templateRepo, solutionRepo, testRepo, basePath); replaceImportedExerciseShortName(Map.of(oldExerciseShortName, newExercise.getShortName()), templateRepo, solutionRepo, testRepo); diff --git a/src/main/webapp/app/exercises/shared/import/from-file/exercise-import-from-file.component.ts b/src/main/webapp/app/exercises/shared/import/from-file/exercise-import-from-file.component.ts index 688b7cd6fb44..d7e8a5343360 100644 --- a/src/main/webapp/app/exercises/shared/import/from-file/exercise-import-from-file.component.ts +++ b/src/main/webapp/app/exercises/shared/import/from-file/exercise-import-from-file.component.ts @@ -54,6 +54,11 @@ export class ExerciseImportFromFileComponent implements OnInit { if (!(this.exercise as ProgrammingExercise).buildConfig) { (this.exercise as ProgrammingExercise).buildConfig = copyBuildConfigFromExerciseJson(exerciseJson as ProgrammingExerciseBuildConfig); } + if ((this.exercise as ProgrammingExercise).auxiliaryRepositories) { + (this.exercise as ProgrammingExercise).auxiliaryRepositories!.forEach((repo, index) => { + (this.exercise as ProgrammingExercise).auxiliaryRepositories![index].id = undefined; + }); + } break; default: this.alertService.error('artemisApp.exercise.importFromFile.notSupportedExerciseType', { From ed031ce60919501ab54dca667a50913beda3da3a Mon Sep 17 00:00:00 2001 From: entholzer Date: Tue, 29 Oct 2024 16:19:12 +0100 Subject: [PATCH 2/5] fix import --- ...grammingExerciseImportFromFileService.java | 49 +++++++++++++------ 1 file changed, 33 insertions(+), 16 deletions(-) diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingExerciseImportFromFileService.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingExerciseImportFromFileService.java index 16ddb557c665..ce82684c82d4 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingExerciseImportFromFileService.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingExerciseImportFromFileService.java @@ -179,43 +179,60 @@ private void importRepositoriesFromFile(ProgrammingExercise newExercise, Path ba // TODO import auxiliary repos - copyImportedExerciseContentToRepositories(templateRepo, solutionRepo, testRepo, basePath); - replaceImportedExerciseShortName(Map.of(oldExerciseShortName, newExercise.getShortName()), templateRepo, solutionRepo, testRepo); + copyImportedExerciseContentToRepositories(templateRepo, solutionRepo, testRepo, auxiliaryRepositories, basePath); + replaceImportedExerciseShortName(Map.of(oldExerciseShortName, newExercise.getShortName()), List.of(solutionRepo, templateRepo, testRepo)); + replaceImportedExerciseShortName(Map.of(oldExerciseShortName, newExercise.getShortName()), auxiliaryRepositories); gitService.stageAllChanges(templateRepo); gitService.stageAllChanges(solutionRepo); gitService.stageAllChanges(testRepo); + for (Repository auxRepo : auxiliaryRepositories) { + gitService.stageAllChanges(auxRepo); + } gitService.commitAndPush(templateRepo, "Import template from file", true, user); gitService.commitAndPush(solutionRepo, "Import solution from file", true, user); gitService.commitAndPush(testRepo, "Import tests from file", true, user); + for (Repository auxRepo : auxiliaryRepositories) { + gitService.commitAndPush(auxRepo, "Import auxiliary repo from file", true, user); + } + } - private void replaceImportedExerciseShortName(Map replacements, Repository... repositories) { + private void replaceImportedExerciseShortName(Map replacements, List repositories) { for (Repository repository : repositories) { fileService.replaceVariablesInFileRecursive(repository.getLocalPath(), replacements, SHORT_NAME_REPLACEMENT_EXCLUSIONS); } } - private void copyImportedExerciseContentToRepositories(Repository templateRepo, Repository solutionRepo, Repository testRepo, Path basePath) throws IOException { + private void copyImportedExerciseContentToRepositories(Repository templateRepo, Repository solutionRepo, Repository testRepo, List auxiliaryRepositories, + Path basePath) throws IOException { repositoryService.deleteAllContentInRepository(templateRepo); repositoryService.deleteAllContentInRepository(solutionRepo); repositoryService.deleteAllContentInRepository(testRepo); - copyExerciseContentToRepository(templateRepo, RepositoryType.TEMPLATE, basePath); - copyExerciseContentToRepository(solutionRepo, RepositoryType.SOLUTION, basePath); - copyExerciseContentToRepository(testRepo, RepositoryType.TESTS, basePath); + for (Repository auxRepo : auxiliaryRepositories) { + repositoryService.deleteAllContentInRepository(auxRepo); + } + + copyExerciseContentToRepository(templateRepo, RepositoryType.TEMPLATE.getName(), basePath); + copyExerciseContentToRepository(solutionRepo, RepositoryType.SOLUTION.getName(), basePath); + copyExerciseContentToRepository(testRepo, RepositoryType.TESTS.getName(), basePath); + for (Repository auxRepo : auxiliaryRepositories) { + String auxRepoSuffix = auxRepo.getLocalPath().toString().split("-")[1]; + copyExerciseContentToRepository(auxRepo, auxRepoSuffix, basePath); + } } /** * Copies everything from the extracted zip file to the repository, except the .git folder * - * @param repository the repository to which the content should be copied - * @param repositoryType the type of the repository - * @param basePath the path to the extracted zip file + * @param repository the repository to which the content should be copied + * @param repoName the name of the repository + * @param basePath the path to the extracted zip file **/ - private void copyExerciseContentToRepository(Repository repository, RepositoryType repositoryType, Path basePath) throws IOException { - FileUtils.copyDirectory(retrieveRepositoryDirectoryPath(basePath, repositoryType.getName()).toFile(), repository.getLocalPath().toFile(), - new NotFileFilter(new NameFileFilter(".git"))); + private void copyExerciseContentToRepository(Repository repository, String repoName, Path basePath) throws IOException { + FileUtils.copyDirectory(retrieveRepositoryDirectoryPath(basePath, repoName).toFile(), repository.getLocalPath().toFile(), new NotFileFilter(new NameFileFilter(".git"))); + try (var files = Files.walk(repository.getLocalPath())) { files.filter(file -> "gradlew".equals(file.getFileName().toString())).forEach(file -> file.toFile().setExecutable(true)); } @@ -250,17 +267,17 @@ private void checkRepositoryForTypeExists(Path path, RepositoryType repoType) th } } - private Path retrieveRepositoryDirectoryPath(Path dirPath, String repoType) { + private Path retrieveRepositoryDirectoryPath(Path dirPath, String repoName) { List result; try (Stream walk = Files.walk(dirPath)) { - result = walk.filter(Files::isDirectory).filter(file -> file.getFileName().toString().endsWith("-" + repoType)).toList(); + result = walk.filter(Files::isDirectory).filter(file -> file.getFileName().toString().endsWith("-" + repoName)).toList(); } catch (IOException e) { throw new BadRequestAlertException("Could not read the directory", "programmingExercise", "couldnotreaddirectory"); } if (result.size() != 1) { throw new IllegalArgumentException( - "There are either no or more than one sub-directories containing " + repoType + " in their name. Please make sure that there is exactly one."); + "There are either no or more than one sub-directories containing " + repoName + " in their name. Please make sure that there is exactly one."); } return result.getFirst(); From c103c16dea71b1be161493424ea1074ebad93390 Mon Sep 17 00:00:00 2001 From: entholzer Date: Tue, 29 Oct 2024 16:34:03 +0100 Subject: [PATCH 3/5] move comment --- .../programming/service/ProgrammingExerciseExportService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingExerciseExportService.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingExerciseExportService.java index 548c220d7293..77bf4709d90c 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingExerciseExportService.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingExerciseExportService.java @@ -174,9 +174,9 @@ private Path exportProgrammingExerciseMaterialWithStudentReposOptional(Programmi exportErrors.add("Failed to export programming exercise repositories: " + e.getMessage()); } - // Add problem statement as .md file exercise.setAuxiliaryRepositories(auxiliaryRepositoryRepository.findByExerciseId(exercise.getId())); + // Add problem statement as .md file exportProblemStatementAndEmbeddedFilesAndExerciseDetails(exercise, exportErrors, exportDir.orElseThrow(), pathsToBeZipped); return exportDir.orElseThrow(); From 006589fb9d82ce31a9fd35de89bb476421dac364 Mon Sep 17 00:00:00 2001 From: entholzer Date: Tue, 29 Oct 2024 16:44:13 +0100 Subject: [PATCH 4/5] improve client code --- .../ProgrammingExerciseImportFromFileService.java | 10 +++++++--- .../from-file/exercise-import-from-file.component.ts | 12 +++++++----- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingExerciseImportFromFileService.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingExerciseImportFromFileService.java index ce82684c82d4..5741dc6f8715 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingExerciseImportFromFileService.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingExerciseImportFromFileService.java @@ -177,8 +177,6 @@ private void importRepositoriesFromFile(ProgrammingExercise newExercise, Path ba auxiliaryRepositories.add(gitService.getOrCheckoutRepository(new VcsRepositoryUri(auxiliaryRepository.getRepositoryUri()), false)); } - // TODO import auxiliary repos - copyImportedExerciseContentToRepositories(templateRepo, solutionRepo, testRepo, auxiliaryRepositories, basePath); replaceImportedExerciseShortName(Map.of(oldExerciseShortName, newExercise.getShortName()), List.of(solutionRepo, templateRepo, testRepo)); replaceImportedExerciseShortName(Map.of(oldExerciseShortName, newExercise.getShortName()), auxiliaryRepositories); @@ -231,7 +229,13 @@ private void copyImportedExerciseContentToRepositories(Repository templateRepo, * @param basePath the path to the extracted zip file **/ private void copyExerciseContentToRepository(Repository repository, String repoName, Path basePath) throws IOException { - FileUtils.copyDirectory(retrieveRepositoryDirectoryPath(basePath, repoName).toFile(), repository.getLocalPath().toFile(), new NotFileFilter(new NameFileFilter(".git"))); + // @formatter:off + FileUtils.copyDirectory( + retrieveRepositoryDirectoryPath(basePath, repoName).toFile(), + repository.getLocalPath().toFile(), + new NotFileFilter(new NameFileFilter(".git")) + ); + // @formatter:on try (var files = Files.walk(repository.getLocalPath())) { files.filter(file -> "gradlew".equals(file.getFileName().toString())).forEach(file -> file.toFile().setExecutable(true)); diff --git a/src/main/webapp/app/exercises/shared/import/from-file/exercise-import-from-file.component.ts b/src/main/webapp/app/exercises/shared/import/from-file/exercise-import-from-file.component.ts index d7e8a5343360..79083e180ea2 100644 --- a/src/main/webapp/app/exercises/shared/import/from-file/exercise-import-from-file.component.ts +++ b/src/main/webapp/app/exercises/shared/import/from-file/exercise-import-from-file.component.ts @@ -50,15 +50,17 @@ export class ExerciseImportFromFileComponent implements OnInit { switch (this.exerciseType) { case ExerciseType.PROGRAMMING: this.exercise = JSON.parse(exerciseDetails as string) as ProgrammingExercise; + const progEx = this.exercise as ProgrammingExercise; // This is needed to make sure that old exported programming exercises can be imported - if (!(this.exercise as ProgrammingExercise).buildConfig) { - (this.exercise as ProgrammingExercise).buildConfig = copyBuildConfigFromExerciseJson(exerciseJson as ProgrammingExerciseBuildConfig); + if (!progEx.buildConfig) { + progEx.buildConfig = copyBuildConfigFromExerciseJson(exerciseJson as ProgrammingExerciseBuildConfig); } - if ((this.exercise as ProgrammingExercise).auxiliaryRepositories) { - (this.exercise as ProgrammingExercise).auxiliaryRepositories!.forEach((repo, index) => { - (this.exercise as ProgrammingExercise).auxiliaryRepositories![index].id = undefined; + if (progEx.auxiliaryRepositories) { + progEx.auxiliaryRepositories!.forEach((repo, index) => { + progEx.auxiliaryRepositories![index].id = undefined; }); } + this.exercise = progEx; break; default: this.alertService.error('artemisApp.exercise.importFromFile.notSupportedExerciseType', { From 39081637f190b03b49b5782dd0b499b7490ca7fc Mon Sep 17 00:00:00 2001 From: entholzer Date: Thu, 31 Oct 2024 13:48:27 +0100 Subject: [PATCH 5/5] added suggestions --- .../service/ProgrammingExerciseExportService.java | 3 +-- .../service/ProgrammingExerciseImportFromFileService.java | 8 +++++--- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingExerciseExportService.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingExerciseExportService.java index 77bf4709d90c..0b3a676bd8ad 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingExerciseExportService.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingExerciseExportService.java @@ -174,8 +174,6 @@ private Path exportProgrammingExerciseMaterialWithStudentReposOptional(Programmi exportErrors.add("Failed to export programming exercise repositories: " + e.getMessage()); } - exercise.setAuxiliaryRepositories(auxiliaryRepositoryRepository.findByExerciseId(exercise.getId())); - // Add problem statement as .md file exportProblemStatementAndEmbeddedFilesAndExerciseDetails(exercise, exportErrors, exportDir.orElseThrow(), pathsToBeZipped); @@ -188,6 +186,7 @@ protected void exportProblemStatementAndEmbeddedFilesAndExerciseDetails(Exercise if (exercise instanceof ProgrammingExercise programmingExercise) { // Used for a save typecast, this should always be true since this class only works with programming exercises. programmingExerciseTaskService.replaceTestIdsWithNames(programmingExercise); + programmingExercise.setAuxiliaryRepositories(auxiliaryRepositoryRepository.findByExerciseId(exercise.getId())); } super.exportProblemStatementAndEmbeddedFilesAndExerciseDetails(exercise, exportErrors, exportDir, pathsToBeZipped); } diff --git a/src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingExerciseImportFromFileService.java b/src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingExerciseImportFromFileService.java index 5741dc6f8715..784e29599185 100644 --- a/src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingExerciseImportFromFileService.java +++ b/src/main/java/de/tum/cit/aet/artemis/programming/service/ProgrammingExerciseImportFromFileService.java @@ -9,6 +9,7 @@ import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.stream.Stream; @@ -174,7 +175,7 @@ private void importRepositoriesFromFile(ProgrammingExercise newExercise, Path ba Repository testRepo = gitService.getOrCheckoutRepository(new VcsRepositoryUri(newExercise.getTestRepositoryUri()), false); List auxiliaryRepositories = new ArrayList<>(); for (AuxiliaryRepository auxiliaryRepository : newExercise.getAuxiliaryRepositories()) { - auxiliaryRepositories.add(gitService.getOrCheckoutRepository(new VcsRepositoryUri(auxiliaryRepository.getRepositoryUri()), false)); + auxiliaryRepositories.add(gitService.getOrCheckoutRepository(auxiliaryRepository.getVcsRepositoryUri(), false)); } copyImportedExerciseContentToRepositories(templateRepo, solutionRepo, testRepo, auxiliaryRepositories, basePath); @@ -216,8 +217,9 @@ private void copyImportedExerciseContentToRepositories(Repository templateRepo, copyExerciseContentToRepository(solutionRepo, RepositoryType.SOLUTION.getName(), basePath); copyExerciseContentToRepository(testRepo, RepositoryType.TESTS.getName(), basePath); for (Repository auxRepo : auxiliaryRepositories) { - String auxRepoSuffix = auxRepo.getLocalPath().toString().split("-")[1]; - copyExerciseContentToRepository(auxRepo, auxRepoSuffix, basePath); + String[] parts = auxRepo.getLocalPath().toString().split("-"); + var auxRepoName = String.join("-", Arrays.copyOfRange(parts, 1, parts.length)); + copyExerciseContentToRepository(auxRepo, auxRepoName, basePath); } }