Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Programming exercises: Export and import build plan from file #7624

Merged
merged 21 commits into from
Jan 10, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
6dcdf7d
`Programming exercises`: Export and import build plan from file
tobias-lippert Nov 18, 2023
9106612
remove superfluous empty line
tobias-lippert Nov 18, 2023
5fc19ca
add javadoc
tobias-lippert Nov 18, 2023
cec9057
code review comments and test improvement
tobias-lippert Nov 20, 2023
cb43e75
Merge branch 'develop' into enhancement/export-import-buildplan
tobias-lippert Nov 20, 2023
2182490
Merge branch 'develop' into enhancement/export-import-buildplan
tobias-lippert Nov 25, 2023
c9567a0
Merge branch 'develop' into enhancement/export-import-buildplan
tobias-lippert Nov 25, 2023
f34c1a3
Merge branch 'develop' into enhancement/export-import-buildplan
tobias-lippert Nov 26, 2023
2489f18
Merge branch 'develop' into enhancement/export-import-buildplan
tobias-lippert Nov 27, 2023
38de650
Update BuildPlanRepository.java
tobias-lippert Nov 28, 2023
14aa9d6
Merge branch 'develop' into enhancement/export-import-buildplan
b-fein Nov 29, 2023
5b226fc
Merge branch 'develop' into enhancement/export-import-buildplan
tobias-lippert Nov 30, 2023
9b39fef
fix path to build plan for import and test the import of the build plan
tobias-lippert Dec 1, 2023
72c4b47
Merge remote-tracking branch 'origin/enhancement/export-import-buildp…
tobias-lippert Dec 1, 2023
41ff521
Merge branch 'develop' into enhancement/export-import-buildplan
tobias-lippert Dec 1, 2023
204ac97
Merge branch 'develop' into enhancement/export-import-buildplan
b-fein Dec 12, 2023
1c781e9
Merge branch 'develop' into enhancement/export-import-buildplan
tobias-lippert Dec 14, 2023
c91c4a1
Merge branch 'develop' into enhancement/export-import-buildplan
tobias-lippert Dec 19, 2023
74b752a
Merge branch 'develop' into enhancement/export-import-buildplan
b-fein Dec 27, 2023
b29a3a8
Merge branch 'develop' into enhancement/export-import-buildplan
tobias-lippert Jan 2, 2024
3d3adc0
Merge branch 'develop' into enhancement/export-import-buildplan
b-fein Jan 8, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@
return isProfileActive("bamboo");
}

public boolean isGitlabCiOrJenkins() {

Check warning on line 32 in src/main/java/de/tum/in/www1/artemis/service/ProfileService.java

View check run for this annotation

Teamscale / teamscale-findings

src/main/java/de/tum/in/www1/artemis/service/ProfileService.java#L32

In this file 6 interface comments are missing. Consider adding explanatory comments or restricting the visibility. View in Teamscale: Line 12: https://teamscale.io/findings.html#details/GitHub-ls1intum-Artemis?t=enhancement%2Fexport-import-buildplan%3AHEAD&id=042377BB6DAE5FACFB14804C0BED347D Line 16: https://teamscale.io/findings.html#details/GitHub-ls1intum-Artemis?t=enhancement%2Fexport-import-buildplan%3AHEAD&id=2AB9D0BF27962F6CE0A9EE6E6414B41C Line 20: https://teamscale.io/findings.html#details/GitHub-ls1intum-Artemis?t=enhancement%2Fexport-import-buildplan%3AHEAD&id=854F150B3196F97CC4FB3E9973E258E9 Line 24: https://teamscale.io/findings.html#details/GitHub-ls1intum-Artemis?t=enhancement%2Fexport-import-buildplan%3AHEAD&id=3F332DF5733CB5892EB172760D8A22A9 Line 28: https://teamscale.io/findings.html#details/GitHub-ls1intum-Artemis?t=enhancement%2Fexport-import-buildplan%3AHEAD&id=E50571400A8E8D6B885B0DD7D150716C Line 32: https://teamscale.io/findings.html#details/GitHub-ls1intum-Artemis?t=enhancement%2Fexport-import-buildplan%3AHEAD&id=E61FD5C5B7A98F4C0B74662164421D97
return isProfileActive("gitlabci") || isProfileActive("jenkins");
}
b-fein marked this conversation as resolved.
Show resolved Hide resolved

private boolean isProfileActive(String profile) {
return Set.of(this.environment.getActiveProfiles()).contains(profile);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,15 +48,15 @@
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

import com.google.common.base.Charsets;

import de.tum.in.www1.artemis.domain.*;
import de.tum.in.www1.artemis.domain.enumeration.ProgrammingLanguage;
import de.tum.in.www1.artemis.domain.enumeration.RepositoryType;
import de.tum.in.www1.artemis.domain.participation.ProgrammingExerciseStudentParticipation;
import de.tum.in.www1.artemis.domain.participation.StudentParticipation;
import de.tum.in.www1.artemis.exception.GitException;
import de.tum.in.www1.artemis.repository.AuxiliaryRepositoryRepository;
import de.tum.in.www1.artemis.repository.ProgrammingExerciseRepository;
import de.tum.in.www1.artemis.repository.StudentParticipationRepository;
import de.tum.in.www1.artemis.repository.*;

Check warning on line 59 in src/main/java/de/tum/in/www1/artemis/service/export/ProgrammingExerciseExportService.java

View check run for this annotation

Teamscale / teamscale-findings

src/main/java/de/tum/in/www1/artemis/service/export/ProgrammingExerciseExportService.java#L59

Star import of `de.tum.in.www1.artemis.repository.*` should not be used https://teamscale.io/findings.html#details/GitHub-ls1intum-Artemis?t=enhancement%2Fexport-import-buildplan%3AHEAD&id=2993A59E379895C460CF743C8406A518
b-fein marked this conversation as resolved.
Show resolved Hide resolved
import de.tum.in.www1.artemis.service.ExerciseDateService;
import de.tum.in.www1.artemis.service.FileService;
import de.tum.in.www1.artemis.service.ZipFileService;
Expand Down Expand Up @@ -91,13 +91,17 @@

private final ZipFileService zipFileService;

private final BuildPlanRepository buildPlanRepository;

public static final String EXPORTED_EXERCISE_DETAILS_FILE_PREFIX = "Exercise-Details";

public static final String EXPORTED_EXERCISE_PROBLEM_STATEMENT_FILE_PREFIX = "Problem-Statement";

public static final String BUILD_PLAN_FILE_NAME = "buildPlan.txt";

Check warning on line 100 in src/main/java/de/tum/in/www1/artemis/service/export/ProgrammingExerciseExportService.java

View check run for this annotation

Teamscale / teamscale-findings

src/main/java/de/tum/in/www1/artemis/service/export/ProgrammingExerciseExportService.java#L100

In this file 4 interface comments are missing. Consider adding explanatory comments or restricting the visibility. View in Teamscale: Line 96: https://teamscale.io/findings.html#details/GitHub-ls1intum-Artemis?t=enhancement%2Fexport-import-buildplan%3AHEAD&id=CC9527330DCE11A04DCE3ADA86A50FA7 Line 98: https://teamscale.io/findings.html#details/GitHub-ls1intum-Artemis?t=enhancement%2Fexport-import-buildplan%3AHEAD&id=807AE334AB82C3E16A6BA3DA903760E3 Line 100: https://teamscale.io/findings.html#details/GitHub-ls1intum-Artemis?t=enhancement%2Fexport-import-buildplan%3AHEAD&id=25D8CDBD47F36DDF32FD4F4F3BC1F123 Line 102: https://teamscale.io/findings.html#details/GitHub-ls1intum-Artemis?t=enhancement%2Fexport-import-buildplan%3AHEAD&id=2CA03CC4E3F096112183F0E8FBBA72AD

public ProgrammingExerciseExportService(ProgrammingExerciseRepository programmingExerciseRepository, ProgrammingExerciseTaskService programmingExerciseTaskService,
StudentParticipationRepository studentParticipationRepository, FileService fileService, GitService gitService, ZipFileService zipFileService,
MappingJackson2HttpMessageConverter springMvcJacksonConverter, AuxiliaryRepositoryRepository auxiliaryRepositoryRepository) {
MappingJackson2HttpMessageConverter springMvcJacksonConverter, AuxiliaryRepositoryRepository auxiliaryRepositoryRepository, BuildPlanRepository buildPlanRepository) {
// Programming exercises do not have a submission export service
super(fileService, springMvcJacksonConverter, null);
this.programmingExerciseRepository = programmingExerciseRepository;
Expand All @@ -107,6 +111,7 @@
this.gitService = gitService;
this.zipFileService = zipFileService;
this.auxiliaryRepositoryRepository = auxiliaryRepositoryRepository;
this.buildPlanRepository = buildPlanRepository;
}

/**
Expand All @@ -132,8 +137,8 @@
exportDir = Optional.of(fileService.getTemporaryUniquePathWithoutPathCreation(repoDownloadClonePath, 5));
}

// Add the exported zip folder containing template, solution, and tests repositories
// wrap this in a try catch block to prevent the problem statement and exercise details not being exported if the repositories fail to export
// Add the exported zip folder containing template, solution, and tests repositories. Also export the build plan if one exists.
// Wrap this in a try catch block to prevent the problem statement and exercise details not being exported if the repositories fail to export
try {
var repoExportsPaths = exportProgrammingExerciseRepositories(exercise, includeStudentRepos, shouldZipZipFiles, repoDownloadClonePath, exportDir.orElseThrow(),
exportErrors, archivalReportEntries);
Expand All @@ -142,6 +147,15 @@
pathsToBeZipped.add(path);
}
});

// Export the build plan of a programming exercise, if one exists. Only relevant for Gitlab/Jenkins or Gitlab/GitlabCI setups.
var buildPlan = buildPlanRepository.findByProgrammingExercises_IdWithProgrammingExercises(exercise.getId());
tobias-lippert marked this conversation as resolved.
Show resolved Hide resolved
if (buildPlan.isPresent()) {
Path buildPlanPath = exportDir.orElseThrow().resolve(BUILD_PLAN_FILE_NAME);
FileUtils.writeStringToFile(buildPlanPath.toFile(), buildPlan.orElseThrow().getBuildPlan(), Charsets.UTF_8);
tobias-lippert marked this conversation as resolved.
Show resolved Hide resolved
pathsToBeZipped.add(buildPlanPath);
}

}
catch (Exception e) {
exportErrors.add("Failed to export programming exercise repositories: " + e.getMessage());
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package de.tum.in.www1.artemis.service.programming;

import static de.tum.in.www1.artemis.service.export.ProgrammingExerciseExportService.BUILD_PLAN_FILE_NAME;

import java.io.IOException;
import java.net.URISyntaxException;
import java.nio.file.Files;
Expand All @@ -13,21 +15,28 @@
import org.apache.commons.io.filefilter.NameFileFilter;
import org.apache.commons.io.filefilter.NotFileFilter;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.base.Charsets;

import de.tum.in.www1.artemis.domain.*;
import de.tum.in.www1.artemis.domain.enumeration.RepositoryType;
import de.tum.in.www1.artemis.repository.BuildPlanRepository;
import de.tum.in.www1.artemis.service.*;
import de.tum.in.www1.artemis.service.connectors.GitService;
import de.tum.in.www1.artemis.service.export.ProgrammingExerciseExportService;
import de.tum.in.www1.artemis.web.rest.errors.BadRequestAlertException;

@Service
public class ProgrammingExerciseImportFromFileService {

private final Logger log = LoggerFactory.getLogger(ProgrammingExerciseExportService.class);

Check warning on line 38 in src/main/java/de/tum/in/www1/artemis/service/programming/ProgrammingExerciseImportFromFileService.java

View check run for this annotation

Teamscale / teamscale-findings

src/main/java/de/tum/in/www1/artemis/service/programming/ProgrammingExerciseImportFromFileService.java#L38

Logger should be specified with `ProgrammingExerciseImportFromFileService.class` https://teamscale.io/findings.html#details/GitHub-ls1intum-Artemis?t=enhancement%2Fexport-import-buildplan%3AHEAD&id=91677DFB15617EED90619BC444707E05
tobias-lippert marked this conversation as resolved.
Show resolved Hide resolved

private final ProgrammingExerciseService programmingExerciseService;

private final ZipFileService zipFileService;
Expand All @@ -40,16 +49,23 @@

private final FileService fileService;

private final ProfileService profileService;

private final BuildPlanRepository buildPlanRepository;

private static final List<String> SHORT_NAME_REPLACEMENT_EXCLUSIONS = List.of("gradle-wrapper.jar");

public ProgrammingExerciseImportFromFileService(ProgrammingExerciseService programmingExerciseService, ZipFileService zipFileService,

Check warning on line 58 in src/main/java/de/tum/in/www1/artemis/service/programming/ProgrammingExerciseImportFromFileService.java

View check run for this annotation

Teamscale / teamscale-findings

src/main/java/de/tum/in/www1/artemis/service/programming/ProgrammingExerciseImportFromFileService.java#L58

Method `ProgrammingExerciseImportFromFileService` has 8 parameters but no more than 7 parameters are allowed https://teamscale.io/findings.html#details/GitHub-ls1intum-Artemis?t=enhancement%2Fexport-import-buildplan%3AHEAD&id=2008D172D06787EF45A93ECEFF96E0D7
StaticCodeAnalysisService staticCodeAnalysisService, RepositoryService repositoryService, GitService gitService, FileService fileService) {
StaticCodeAnalysisService staticCodeAnalysisService, RepositoryService repositoryService, GitService gitService, FileService fileService, ProfileService profileService,
BuildPlanRepository buildPlanRepository) {
this.programmingExerciseService = programmingExerciseService;
this.zipFileService = zipFileService;
this.staticCodeAnalysisService = staticCodeAnalysisService;
this.repositoryService = repositoryService;
this.gitService = gitService;
this.fileService = fileService;
this.profileService = profileService;
this.buildPlanRepository = buildPlanRepository;
}

/**
Expand Down Expand Up @@ -87,6 +103,10 @@
copyEmbeddedFiles(exerciseFilePath.toAbsolutePath().getParent().resolve(FileNameUtils.getBaseName(exerciseFilePath.toString())));
importRepositoriesFromFile(importedProgrammingExercise, importExerciseDir, oldShortName, user);
importedProgrammingExercise.setCourse(course);
// It doesn't make sense to import a build plan on a bamboo or local CI setup.
if (profileService.isGitlabCiOrJenkins()) {
importBuildPlanIfExisting(importedProgrammingExercise, importExerciseDir);
}
}
finally {
// want to make sure the directories are deleted, even if an exception is thrown
Expand All @@ -95,6 +115,25 @@
return importedProgrammingExercise;
}

/**
* Imports a build plan if it exists in the extracted zip file
* If the file cannot be read, the build plan is skipped
*
* @param programmingExercise the programming exercise for which the build plan should be imported
* @param importExerciseDir the directory where the extracted zip file is located
*/
private void importBuildPlanIfExisting(ProgrammingExercise programmingExercise, Path importExerciseDir) {
Path buildPlanPath = importExerciseDir.resolve(BUILD_PLAN_FILE_NAME);
if (Files.exists(buildPlanPath)) {
try {
buildPlanRepository.setBuildPlanForExercise(FileUtils.readFileToString(buildPlanPath.toFile(), Charsets.UTF_8), programmingExercise);
tobias-lippert marked this conversation as resolved.
Show resolved Hide resolved
}
catch (IOException e) {
log.warn("Could not read build plan file. Continue importing the exercise but skipping the build plan.", e);
}
}
}

/**
* Copy embedded files from the extracted zip file to the markdown folder, so they can be used in the problem statement
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,7 @@
import static de.tum.in.www1.artemis.domain.enumeration.ExerciseMode.INDIVIDUAL;
import static de.tum.in.www1.artemis.domain.enumeration.ExerciseMode.TEAM;
import static de.tum.in.www1.artemis.domain.enumeration.ProgrammingLanguage.*;
import static de.tum.in.www1.artemis.service.export.ProgrammingExerciseExportService.EXPORTED_EXERCISE_DETAILS_FILE_PREFIX;
import static de.tum.in.www1.artemis.service.export.ProgrammingExerciseExportService.EXPORTED_EXERCISE_PROBLEM_STATEMENT_FILE_PREFIX;
import static de.tum.in.www1.artemis.service.export.ProgrammingExerciseExportService.*;
b-fein marked this conversation as resolved.
Show resolved Hide resolved
import static de.tum.in.www1.artemis.util.TestConstants.COMMIT_HASH_OBJECT_ID;
import static de.tum.in.www1.artemis.web.rest.ProgrammingExerciseResourceEndpoints.*;
import static org.assertj.core.api.Assertions.assertThat;
Expand Down Expand Up @@ -206,6 +205,9 @@
@Autowired
private ParticipationUtilService participationUtilService;

@Autowired
private BuildPlanRepository buildPlanRepository;

public Course course;

public ProgrammingExercise exercise;
Expand Down Expand Up @@ -1392,39 +1394,43 @@
* @throws Exception if the export fails
*/
void exportProgrammingExerciseInstructorMaterial_shouldReturnFile(boolean saveEmbeddedFiles) throws Exception {
var zipFile = exportProgrammingExerciseInstructorMaterial(HttpStatus.OK, false, true, saveEmbeddedFiles);
// Assure, that the zip folder is already created and not 'in creation' which would lead to a failure when extracting it in the next step
await().until(zipFile::exists);
assertThat(zipFile).isNotNull();
String embeddedFileName1 = "Markdown_2023-05-06T16-17-46-410_ad323711.jpg";
String embeddedFileName2 = "Markdown_2023-05-06T16-17-46-822_b921f475.jpg";
// delete the files to not only make a test pass because a previous test run succeeded
Path embeddedFilePath1 = FilePathService.getMarkdownFilePath().resolve(embeddedFileName1);
Path embeddedFilePath2 = FilePathService.getMarkdownFilePath().resolve(embeddedFileName2);
if (Files.exists(embeddedFilePath1)) {
Files.delete(embeddedFilePath1);
}
if (Files.exists(embeddedFilePath2)) {
Files.delete(embeddedFilePath2);
}
// Recursively unzip the exported file, to make sure there is no erroneous content
Path extractedZipDir = zipFileTestUtilService.extractZipFileRecursively(zipFile.getAbsolutePath());

// Check that the contents we created exist in the unzipped exported folder
try (var files = Files.walk(extractedZipDir)) {
List<Path> listOfIncludedFiles = files.filter(Files::isRegularFile).map(Path::getFileName).toList();
assertThat(listOfIncludedFiles).anyMatch((filename) -> filename.toString().matches(".*-exercise.zip"))
.anyMatch((filename) -> filename.toString().matches(".*-solution.zip")).anyMatch((filename) -> filename.toString().matches(".*-tests.zip"))
.anyMatch((filename) -> filename.toString().matches(EXPORTED_EXERCISE_PROBLEM_STATEMENT_FILE_PREFIX + ".*.md"))
.anyMatch((filename) -> filename.toString().matches(EXPORTED_EXERCISE_DETAILS_FILE_PREFIX + ".*.json"));
.anyMatch((filename) -> filename.toString().matches(EXPORTED_EXERCISE_DETAILS_FILE_PREFIX + ".*.json"))
.anyMatch((filename) -> BUILD_PLAN_FILE_NAME.equals(filename.toString()));
if (saveEmbeddedFiles) {
assertThat(listOfIncludedFiles).anyMatch((filename) -> filename.toString().equals(embeddedFileName1))
.anyMatch((filename) -> filename.toString().equals(embeddedFileName2));
}
Path buildPlanPath = listOfIncludedFiles.stream().filter(file -> BUILD_PLAN_FILE_NAME.equals(file.getFileName().toString())).findFirst().orElseThrow();
String buildPlanContent = Files.readString(extractedZipDir.resolve(buildPlanPath), StandardCharsets.UTF_8);
assertThat(buildPlanContent).isEqualTo("my build plan");
b-fein marked this conversation as resolved.
Show resolved Hide resolved
}

FileUtils.deleteDirectory(extractedZipDir.toFile());
FileUtils.delete(zipFile);

Check warning on line 1433 in src/test/java/de/tum/in/www1/artemis/exercise/programmingexercise/ProgrammingExerciseTestService.java

View check run for this annotation

Teamscale / teamscale-findings

src/test/java/de/tum/in/www1/artemis/exercise/programmingexercise/ProgrammingExerciseTestService.java#L1397-L1433

This method is a bit lengthy [0]. Consider shortening it, e.g. by extracting code blocks into separate methods. [0] https://teamscale.io/findings.html#details/GitHub-ls1intum-Artemis?t=enhancement%2Fexport-import-buildplan%3AHEAD&id=2E9606F65E9B22C715550E789A9B7E16
}

void exportProgrammingExerciseInstructorMaterial_problemStatementNull_success() throws Exception {
Expand Down Expand Up @@ -1545,6 +1551,7 @@
FilePathService.getMarkdownFilePath().resolve(embeddedFileName2).toFile());
}
exercise = programmingExerciseRepository.save(exercise);
buildPlanRepository.setBuildPlanForExercise("my build plan", exercise);
exercise = programmingExerciseUtilService.addTemplateParticipationForProgrammingExercise(exercise);
exercise = programmingExerciseUtilService.addSolutionParticipationForProgrammingExercise(exercise);
exercise = programmingExerciseRepository.findWithTemplateAndSolutionParticipationById(exercise.getId()).orElseThrow();
Expand Down
Loading