Skip to content

Commit

Permalink
Merge branch 'develop' into bugfix/communication/disable-dropdown-whe…
Browse files Browse the repository at this point in the history
…n-there-is-link

# Conflicts:
#	src/main/webapp/app/shared/metis/answer-post/answer-post.component.ts
#	src/test/javascript/spec/component/shared/metis/answer-post/answer-post.component.spec.ts
#	src/test/javascript/spec/component/shared/metis/post/post.component.spec.ts
  • Loading branch information
asliayk committed Nov 27, 2024
2 parents 6b5f153 + 4eaba4e commit e32c071
Show file tree
Hide file tree
Showing 170 changed files with 5,233 additions and 423 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ Refer to [Using JHipster in production](http://www.jhipster.tech/production) for
The following command can automate the deployment to a server. The example shows the deployment to the main Artemis test server (which runs a virtual machine):

```shell
./artemis-server-cli deploy [email protected] -w build/libs/Artemis-7.7.2.war
./artemis-server-cli deploy [email protected] -w build/libs/Artemis-7.7.3.war
```

## Architecture
Expand Down
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ plugins {
}

group = "de.tum.cit.aet.artemis"
version = "7.7.2"
version = "7.7.3"
description = "Interactive Learning with Individual Feedback"

java {
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "artemis",
"version": "7.7.2",
"version": "7.7.3",
"description": "Interactive Learning with Individual Feedback",
"private": true,
"license": "MIT",
Expand Down
20 changes: 20 additions & 0 deletions src/main/java/de/tum/cit/aet/artemis/assessment/domain/Result.java
Original file line number Diff line number Diff line change
Expand Up @@ -108,8 +108,12 @@ public class Result extends DomainObject implements Comparable<Result> {
@JsonView(QuizView.Before.class)
private List<Feedback> feedbacks = new ArrayList<>();

/**
* @deprecated: Will be removed for 8.0, please use submission.participation instead
*/
@ManyToOne
@JsonView(QuizView.Before.class)
@Deprecated(since = "7.7", forRemoval = true)
private Participation participation;

@ManyToOne(fetch = FetchType.LAZY)
Expand Down Expand Up @@ -385,15 +389,31 @@ private boolean feedbackTextHasChanged(String existingText, String newText) {
return !Objects.equals(existingText, newText);
}

/**
* @deprecated: Will be removed for 8.0, please use submission.participation instead
* @return the participation
*/
@Deprecated(since = "7.7", forRemoval = true)
public Participation getParticipation() {
return participation;
}

/**
* @deprecated: Will be removed for 8.0, please use submission.participation instead
* @param participation the participation to set
* @return the result
*/
@Deprecated(since = "7.7", forRemoval = true)
public Result participation(Participation participation) {
this.participation = participation;
return this;
}

/**
* @deprecated: Will be removed for 8.0, please use submission.participation instead
* @param participation the participation to set
*/
@Deprecated(since = "7.7", forRemoval = true)
public void setParticipation(Participation participation) {
this.participation = participation;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -692,4 +692,15 @@ public void deleteLongFeedback(List<Feedback> feedbackList, Result result) {
List<Feedback> feedbacks = new ArrayList<>(feedbackList);
result.updateAllFeedbackItems(feedbacks, true);
}

/**
* Retrieves the number of students affected by a specific feedback detail text for a given exercise.
*
* @param exerciseId for which the affected student count is requested.
* @param detailText used to filter affected students.
* @return the total number of distinct students affected by the feedback detail text.
*/
public long getAffectedStudentCountByFeedbackDetailText(long exerciseId, String detailText) {
return studentParticipationRepository.countAffectedStudentsByFeedbackDetailText(exerciseId, detailText);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -373,4 +373,18 @@ public ResponseEntity<Page<FeedbackAffectedStudentDTO>> getAffectedStudentsWithF

return ResponseEntity.ok(participation);
}

/**
* GET /exercises/{exerciseId}/feedback-detail/affected-students : Retrieves the count of students affected by a specific feedback detail text.
*
* @param exerciseId The ID of the exercise for which affected students are counted.
* @param detailText The feedback detail text to filter by.
* @return A {@link ResponseEntity} containing the count of affected students.
*/
@GetMapping("exercises/{exerciseId}/feedback-detail/affected-students")
@EnforceAtLeastEditorInExercise
public ResponseEntity<Long> countAffectedStudentsByFeedbackDetailText(@PathVariable long exerciseId, @RequestParam("detailText") String detailText) {
long affectedStudentCount = resultService.getAffectedStudentCountByFeedbackDetailText(exerciseId, detailText);
return ResponseEntity.ok(affectedStudentCount);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package de.tum.cit.aet.artemis.buildagent.dto;

import java.io.Serializable;
import java.time.ZonedDateTime;

import jakarta.validation.constraints.NotNull;

import com.fasterxml.jackson.annotation.JsonInclude;

@JsonInclude(JsonInclude.Include.NON_EMPTY)
public record BuildLogDTO(@NotNull ZonedDateTime time, @NotNull String log) implements Serializable {

}
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
// in the future are migrated or cleared. Changes should be communicated in release notes as potentially breaking changes.
@JsonIgnoreProperties(ignoreUnknown = true)
@JsonInclude(JsonInclude.Include.NON_EMPTY)
// TODO: this should be a record in the future
public class BuildResult extends AbstractBuildResultNotificationDTO implements Serializable {

private final String assignmentRepoBranchName;
Expand All @@ -41,7 +42,7 @@ public class BuildResult extends AbstractBuildResultNotificationDTO implements S

private final List<LocalCIJobDTO> jobs;

private List<BuildLogEntry> buildLogEntries = new ArrayList<>();
private List<BuildLogDTO> buildLogEntries = new ArrayList<>();

private final List<StaticCodeAnalysisReportDTO> staticCodeAnalysisReports;

Expand Down Expand Up @@ -123,15 +124,16 @@ public boolean hasLogs() {

@Override
public List<BuildLogEntry> extractBuildLogs() {
return buildLogEntries;
// convert the buildLogEntry DTOs to BuildLogEntry objects
return buildLogEntries.stream().map(log -> new BuildLogEntry(log.time(), log.log())).toList();
}

/**
* Setter for the buildLogEntries
*
* @param buildLogEntries the buildLogEntries to be set
*/
public void setBuildLogEntries(List<BuildLogEntry> buildLogEntries) {
public void setBuildLogEntries(List<BuildLogDTO> buildLogEntries) {
this.buildLogEntries = buildLogEntries;
hasLogs = true;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,8 @@

import com.fasterxml.jackson.annotation.JsonInclude;

import de.tum.cit.aet.artemis.programming.domain.build.BuildLogEntry;

// NOTE: this data structure is used in shared code between core and build agent nodes. Changing it requires that the shared data structures in Hazelcast (or potentially Redis)
// in the future are migrated or cleared. Changes should be communicated in release notes as potentially breaking changes.
@JsonInclude(JsonInclude.Include.NON_EMPTY)
// TODO: this data structure should not use BuildLogEntry because it's an entity class (and not a DTO)
public record ResultQueueItem(BuildResult buildResult, BuildJobQueueItem buildJobQueueItem, List<BuildLogEntry> buildLogs, Throwable exception) implements Serializable {
public record ResultQueueItem(BuildResult buildResult, BuildJobQueueItem buildJobQueueItem, List<BuildLogDTO> buildLogs, Throwable exception) implements Serializable {
}
Original file line number Diff line number Diff line change
Expand Up @@ -180,16 +180,14 @@ public static class MyPullImageResultCallback extends PullImageResultCallback {
@Override
public void onNext(PullResponseItem item) {
String msg = "~~~~~~~~~~~~~~~~~~~~ Pull image progress: " + item.getStatus() + " ~~~~~~~~~~~~~~~~~~~~";
log.info(msg);
buildLogsMap.appendBuildLogEntry(buildJobId, msg);
log.debug(msg);
super.onNext(item);
}

@Override
public void onComplete() {
String msg = "~~~~~~~~~~~~~~~~~~~~ Pull image complete ~~~~~~~~~~~~~~~~~~~~";
log.info(msg);
buildLogsMap.appendBuildLogEntry(buildJobId, msg);
log.debug(msg);
super.onComplete();
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,9 @@
import com.github.dockerjava.api.model.Frame;
import com.github.dockerjava.api.model.HostConfig;

import de.tum.cit.aet.artemis.buildagent.dto.BuildLogDTO;
import de.tum.cit.aet.artemis.core.exception.LocalCIException;
import de.tum.cit.aet.artemis.programming.domain.ProgrammingLanguage;
import de.tum.cit.aet.artemis.programming.domain.build.BuildLogEntry;
import de.tum.cit.aet.artemis.programming.service.ci.ContinuousIntegrationService.RepositoryCheckoutPath;

/**
Expand Down Expand Up @@ -414,7 +414,7 @@ private void executeDockerCommand(String containerId, String buildJobId, boolean
@Override
public void onNext(Frame item) {
String text = new String(item.getPayload());
BuildLogEntry buildLogEntry = new BuildLogEntry(ZonedDateTime.now(), text);
BuildLogDTO buildLogEntry = new BuildLogDTO(ZonedDateTime.now(), text);
if (buildJobId != null) {
buildLogsMap.appendBuildLogEntry(buildJobId, buildLogEntry);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -345,7 +345,7 @@ private BuildResult runScriptAndParseResults(BuildJobQueueItem buildJob, String
try {
buildResult = parseTestResults(testResultsTarInputStream, buildJob.buildConfig().branch(), assignmentRepoCommitHash, testRepoCommitHash, buildCompletedDate,
buildJob.id());
buildResult.setBuildLogEntries(buildLogsMap.getBuildLogs(buildJob.id()));
buildResult.setBuildLogEntries(buildLogsMap.getAndTruncateBuildLogs(buildJob.id()));
}
catch (IOException | IllegalStateException e) {
msg = "Error while parsing test results";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,9 @@
import com.hazelcast.topic.ITopic;

import de.tum.cit.aet.artemis.buildagent.dto.BuildJobQueueItem;
import de.tum.cit.aet.artemis.buildagent.dto.BuildLogDTO;
import de.tum.cit.aet.artemis.buildagent.dto.BuildResult;
import de.tum.cit.aet.artemis.core.exception.LocalCIException;
import de.tum.cit.aet.artemis.programming.domain.build.BuildLogEntry;

/**
* This service is responsible for adding build jobs to the Integrated Code Lifecycle executor service.
Expand Down Expand Up @@ -171,7 +171,7 @@ public CompletableFuture<BuildResult> executeBuildJob(BuildJobQueueItem buildJob
finishCancelledBuildJob(buildJobItem.repositoryInfo().assignmentRepositoryUri(), buildJobItem.id(), containerName);
String msg = "Build job with id " + buildJobItem.id() + " was cancelled.";
String stackTrace = stackTraceToString(e);
buildLogsMap.appendBuildLogEntry(buildJobItem.id(), new BuildLogEntry(ZonedDateTime.now(), msg + "\n" + stackTrace));
buildLogsMap.appendBuildLogEntry(buildJobItem.id(), new BuildLogDTO(ZonedDateTime.now(), msg + "\n" + stackTrace));
throw new CompletionException(msg, e);
}
else {
Expand Down Expand Up @@ -232,7 +232,7 @@ private CompletableFuture<BuildResult> createCompletableFuture(Supplier<BuildRes
private void finishBuildJobExceptionally(String buildJobId, String containerName, Exception exception) {
String msg = "Error while executing build job " + buildJobId + ": " + exception.getMessage();
String stackTrace = stackTraceToString(exception);
buildLogsMap.appendBuildLogEntry(buildJobId, new BuildLogEntry(ZonedDateTime.now(), msg + "\n" + stackTrace));
buildLogsMap.appendBuildLogEntry(buildJobId, new BuildLogDTO(ZonedDateTime.now(), msg + "\n" + stackTrace));
log.error(msg);

log.info("Getting ID of running container {}", containerName);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,30 +8,77 @@
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Component;

import de.tum.cit.aet.artemis.programming.domain.build.BuildLogEntry;
import de.tum.cit.aet.artemis.buildagent.dto.BuildLogDTO;

@Profile(PROFILE_BUILDAGENT)
@Component
public class BuildLogsMap {

private final ConcurrentMap<String, List<BuildLogEntry>> buildLogsMap = new ConcurrentHashMap<>();
@Value("${artemis.continuous-integration.build-logs.max-lines-per-job:10000}")
private int maxLogLinesPerBuildJob;

public List<BuildLogEntry> getBuildLogs(String buildLogId) {
return buildLogsMap.get(buildLogId);
@Value("${artemis.continuous-integration.build-logs.max-chars-per-line:1024}")
private int maxCharsPerLine;

// buildJobId --> List of build logs
private final ConcurrentMap<String, List<BuildLogDTO>> buildLogsMap = new ConcurrentHashMap<>();

/**
* Appends a new build log entry to the build logs for the specified build job ID.
*
* @param buildJobId the ID of the build job to append a log message to
* @param message the message to append to the build log
*/
public void appendBuildLogEntry(String buildJobId, String message) {
appendBuildLogEntry(buildJobId, new BuildLogDTO(ZonedDateTime.now(), message + "\n"));
}

public void appendBuildLogEntry(String buildLogId, String message) {
appendBuildLogEntry(buildLogId, new BuildLogEntry(ZonedDateTime.now(), message + "\n"));
/**
* Appends a new build log entry to the build logs for the specified build job ID.
* Only the first maxCharsPerLine characters of the log message will be appended. Longer characters will be truncated to avoid memory issues.
* Only the first maxLogLinesPerBuildJob log entries will be stored. Newer logs will be ignored to avoid memory issues
*
* @param buildJobId the ID of the build job to append a log message to
* @param buildLog the build log entry to append to the build log
*/
public void appendBuildLogEntry(String buildJobId, BuildLogDTO buildLog) {
List<BuildLogDTO> buildLogs = buildLogsMap.computeIfAbsent(buildJobId, k -> new ArrayList<>());
if (buildLogs.size() < maxLogLinesPerBuildJob) {
if (buildLog.log() != null && buildLog.log().length() > maxCharsPerLine) {
buildLog = new BuildLogDTO(buildLog.time(), buildLog.log().substring(0, maxCharsPerLine) + "\n");
}
buildLogs.add(buildLog);
}
}

public void appendBuildLogEntry(String buildLogId, BuildLogEntry buildLog) {
buildLogsMap.computeIfAbsent(buildLogId, k -> new ArrayList<>()).add(buildLog);
public void removeBuildLogs(String buildJobId) {
buildLogsMap.remove(buildJobId);
}

public void removeBuildLogs(String buildLogId) {
buildLogsMap.remove(buildLogId);
/**
* Retrieves and truncates the build logs for the specified build job ID. Does not modify the original build logs.
*
* @param buildJobId the ID of the build job to retrieve and truncate
* @return a list of truncated build log entries, or null if no logs are found for the specified ID
*/
public List<BuildLogDTO> getAndTruncateBuildLogs(String buildJobId) {
List<BuildLogDTO> buildLogs = buildLogsMap.get(buildJobId);

if (buildLogs == null) {
return null;
}

// Truncate the build logs to maxLogLinesPerBuildJob
if (buildLogs.size() > maxLogLinesPerBuildJob) {
List<BuildLogDTO> truncatedBuildLogs = new ArrayList<>(buildLogs.subList(0, maxLogLinesPerBuildJob));
truncatedBuildLogs.add(new BuildLogDTO(ZonedDateTime.now(), "Truncated build logs...\n"));
buildLogs = truncatedBuildLogs;
}

return buildLogs;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,11 @@
import de.tum.cit.aet.artemis.buildagent.dto.BuildAgentDTO;
import de.tum.cit.aet.artemis.buildagent.dto.BuildAgentInformation;
import de.tum.cit.aet.artemis.buildagent.dto.BuildJobQueueItem;
import de.tum.cit.aet.artemis.buildagent.dto.BuildLogDTO;
import de.tum.cit.aet.artemis.buildagent.dto.BuildResult;
import de.tum.cit.aet.artemis.buildagent.dto.JobTimingInfo;
import de.tum.cit.aet.artemis.buildagent.dto.ResultQueueItem;
import de.tum.cit.aet.artemis.core.security.SecurityUtils;
import de.tum.cit.aet.artemis.programming.domain.build.BuildLogEntry;
import de.tum.cit.aet.artemis.programming.domain.build.BuildStatus;

/**
Expand Down Expand Up @@ -360,7 +360,9 @@ private BuildAgentInformation getUpdatedLocalBuildAgentInformation(BuildJobQueue
}

private List<BuildJobQueueItem> getProcessingJobsOfNode(String memberAddress) {
return processingJobs.values().stream().filter(job -> Objects.equals(job.buildAgent().memberAddress(), memberAddress)).toList();
// NOTE: we should not use streams with IMap, because it can be unstable, when many items are added at the same time and there is a slow network condition
List<BuildJobQueueItem> processingJobsList = new ArrayList<>(processingJobs.values());
return processingJobsList.stream().filter(job -> Objects.equals(job.buildAgent().memberAddress(), memberAddress)).toList();
}

private void removeOfflineNodes() {
Expand Down Expand Up @@ -397,7 +399,7 @@ private void processBuild(BuildJobQueueItem buildJob) {
buildJob.exerciseId(), buildJob.retryCount(), buildJob.priority(), BuildStatus.SUCCESSFUL, buildJob.repositoryInfo(), jobTimingInfo, buildJob.buildConfig(),
null);

List<BuildLogEntry> buildLogs = buildLogsMap.getBuildLogs(buildJob.id());
List<BuildLogDTO> buildLogs = buildLogsMap.getAndTruncateBuildLogs(buildJob.id());
buildLogsMap.removeBuildLogs(buildJob.id());

ResultQueueItem resultQueueItem = new ResultQueueItem(buildResult, finishedJob, buildLogs, null);
Expand Down Expand Up @@ -435,7 +437,7 @@ private void processBuild(BuildJobQueueItem buildJob) {

job = new BuildJobQueueItem(buildJob, completionDate, status);

List<BuildLogEntry> buildLogs = buildLogsMap.getBuildLogs(buildJob.id());
List<BuildLogDTO> buildLogs = buildLogsMap.getAndTruncateBuildLogs(buildJob.id());
buildLogsMap.removeBuildLogs(buildJob.id());

BuildResult failedResult = new BuildResult(buildJob.buildConfig().branch(), buildJob.buildConfig().assignmentCommitHash(), buildJob.buildConfig().testCommitHash(),
Expand Down
Loading

0 comments on commit e32c071

Please sign in to comment.