Skip to content

Commit

Permalink
Merge branch 'develop' into feature/localVC/ssh-key-ui-1
Browse files Browse the repository at this point in the history
  • Loading branch information
SimonEntholzer authored Oct 12, 2024
2 parents 68c40fc + f358329 commit 90dc3cc
Show file tree
Hide file tree
Showing 131 changed files with 1,260 additions and 418 deletions.
2 changes: 1 addition & 1 deletion angular.json
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@
},
{
"glob": "**/*",
"input": "./node_modules/monaco-editor/min/vs",
"input": "./node_modules/monaco-editor/bundles/vs",
"output": "vs"
}
],
Expand Down
9 changes: 5 additions & 4 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -246,14 +246,15 @@ dependencies {
implementation "org.gitlab4j:gitlab4j-api:6.0.0-rc.5"

implementation "de.jplag:jplag:${jplag_version}"
implementation "de.jplag:java:${jplag_version}"
implementation "de.jplag:kotlin:${jplag_version}"

implementation "de.jplag:c:${jplag_version}"
implementation "de.jplag:swift:${jplag_version}"
implementation "de.jplag:java:${jplag_version}"
implementation "de.jplag:javascript:${jplag_version}"
implementation "de.jplag:kotlin:${jplag_version}"
implementation "de.jplag:python-3:${jplag_version}"
implementation "de.jplag:rlang:${jplag_version}"
implementation "de.jplag:rust:${jplag_version}"
implementation "de.jplag:javascript:${jplag_version}"
implementation "de.jplag:swift:${jplag_version}"
implementation "de.jplag:text:${jplag_version}"

// those are transitive dependencies of JPlag Text --> Stanford NLP
Expand Down
4 changes: 4 additions & 0 deletions docs/user/exercises/programming-exercise-features.inc
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ Instructors can still use those templates to generate programming exercises and
+----------------------+----------+---------+
| JavaScript | yes | yes |
+----------------------+----------+---------+
| R | yes | yes |
+----------------------+----------+---------+

- Not all ``templates`` support the same feature set and supported features can also change depending on the continuous integration system setup.
Depending on the feature set, some options might not be available during the creation of the programming exercise.
Expand Down Expand Up @@ -71,6 +73,8 @@ Instructors can still use those templates to generate programming exercises and
+----------------------+----------------------+----------------------+---------------------+--------------+------------------------------------------+------------------------------+----------------------------+------------------------+
| JavaScript | no | no | yes | no | n/a | no | no | L: yes, J: no |
+----------------------+----------------------+----------------------+---------------------+--------------+------------------------------------------+------------------------------+----------------------------+------------------------+
| R | no | no | yes | no | n/a | no | no | L: yes, J: no |
+----------------------+----------------------+----------------------+---------------------+--------------+------------------------------------------+------------------------------+----------------------------+------------------------+

- *Sequential Test Runs*: ``Artemis`` can generate a build plan which first executes structural and then behavioral tests. This feature can help students to better concentrate on the immediate challenge at hand.
- *Static Code Analysis*: ``Artemis`` can generate a build plan which additionally executes static code analysis tools.
Expand Down
9 changes: 4 additions & 5 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
Expand Up @@ -58,7 +58,7 @@
"jszip": "3.10.1",
"lodash-es": "4.17.21",
"mobile-drag-drop": "3.0.0-rc.0",
"monaco-editor": "0.51.0",
"monaco-editor": "0.52.0",
"ngx-infinite-scroll": "18.0.0",
"ngx-webstorage": "18.0.0",
"papaparse": "5.4.1",
Expand Down
30 changes: 26 additions & 4 deletions prebuild.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@
* - webpack.DefinePlugin and
* - MergeJsonWebpackPlugin
*/
import fs from "fs";
import path from "path";
import { hashElement } from "folder-hash";
import { fileURLToPath } from "url";
import fs from 'fs';
import path from 'path';
import { hashElement } from 'folder-hash';
import { fileURLToPath } from 'url';
import * as esbuild from 'esbuild';

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
Expand Down Expand Up @@ -111,4 +112,25 @@ for (const group of groups) {
}
}

/*
* The workers of the monaco editor must be bundled separately.
* Specialized workers are available in the vs/esm/language/ directory.
* Be sure to modify the MonacoConfig if you choose to add a worker here.
* For more details, refer to https://github.com/microsoft/monaco-editor/blob/main/samples/browser-esm-esbuild/build.js
*/
const workerEntryPoints = [
'vs/language/json/json.worker.js',
'vs/language/css/css.worker.js',
'vs/language/html/html.worker.js',
'vs/language/typescript/ts.worker.js',
'vs/editor/editor.worker.js'
];
await esbuild.build({
entryPoints: workerEntryPoints.map((entry) => `node_modules/monaco-editor/esm/${entry}`),
bundle: true,
format: 'esm',
outbase: 'node_modules/monaco-editor/esm',
outdir: 'node_modules/monaco-editor/bundles'
});

console.log("Pre-Build complete!");
Original file line number Diff line number Diff line change
Expand Up @@ -629,7 +629,7 @@ public boolean isAutomatic() {
* @return true if the result is an automatic AI Athena result
*/
@JsonIgnore
public boolean isAthenaAutomatic() {
public boolean isAthenaBased() {
return AssessmentType.AUTOMATIC_ATHENA == assessmentType;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,9 @@ public boolean equals(Object object) {
return false;
}
PushNotificationDeviceConfiguration that = (PushNotificationDeviceConfiguration) object;
return token.equals(that.token) && deviceType == that.deviceType && expirationDate.equals(that.expirationDate) && Arrays.equals(secretKey, that.secretKey)
// Use compareTo rather than equals for dates to ensure timestamps and dates with the same time are considered equal
// This is caused by Java internal design having different classes for Date (java.util) and Timestamp (java.sql)
return token.equals(that.token) && deviceType == that.deviceType && expirationDate.compareTo(that.expirationDate) == 0 && Arrays.equals(secretKey, that.secretKey)
&& owner.equals(that.owner);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,6 @@
import de.tum.cit.aet.artemis.core.security.jwt.TokenProvider;
import de.tum.cit.aet.artemis.core.service.AuthorizationCheckService;
import de.tum.cit.aet.artemis.exam.repository.ExamRepository;
import de.tum.cit.aet.artemis.exercise.domain.Exercise;
import de.tum.cit.aet.artemis.exercise.domain.participation.StudentParticipation;
import de.tum.cit.aet.artemis.exercise.repository.ExerciseRepository;
import de.tum.cit.aet.artemis.exercise.repository.StudentParticipationRepository;
Expand Down Expand Up @@ -309,8 +308,7 @@ private boolean allowSubscription(@Nullable Principal principal, String destinat

// TODO: Is it right that TAs are not allowed to subscribe to exam exercises?
if (exerciseRepository.isExamExercise(exerciseId)) {
Exercise exercise = exerciseRepository.findByIdElseThrow(exerciseId);
return authorizationCheckService.isAtLeastInstructorInCourse(login, exercise.getCourseViaExerciseGroupOrCourseMember().getId());
return authorizationCheckService.isAtLeastInstructorInExercise(login, exerciseId);
}
else {
return authorizationCheckService.isAtLeastTeachingAssistantInExercise(login, exerciseId);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ public class UserPublicInfoDTO {

private String lastName;

private String imageUrl;

private Boolean isInstructor;

private Boolean isEditor;
Expand All @@ -43,6 +45,7 @@ public UserPublicInfoDTO(User user) {
this.name = user.getName();
this.firstName = user.getFirstName();
this.lastName = user.getLastName();
this.imageUrl = user.getImageUrl();
}

/**
Expand Down Expand Up @@ -101,6 +104,14 @@ public void setLastName(String lastName) {
this.lastName = lastName;
}

public String getImageUrl() {
return imageUrl;
}

public void setImageUrl(String imageUrl) {
this.imageUrl = imageUrl;
}

public Boolean getIsInstructor() {
return isInstructor;
}
Expand Down Expand Up @@ -152,6 +163,7 @@ public int hashCode() {
@Override
public String toString() {
return "UserPublicInfoDTO{" + "id=" + id + ", login='" + login + '\'' + ", name='" + name + '\'' + ", firstName='" + firstName + '\'' + ", lastName='" + lastName + '\''
+ ", isInstructor=" + isInstructor + ", isEditor=" + isEditor + ", isTeachingAssistant=" + isTeachingAssistant + ", isStudent=" + isStudent + '}';
+ ", imageUrl='" + imageUrl + '\'' + ", isInstructor=" + isInstructor + ", isEditor=" + isEditor + ", isTeachingAssistant=" + isTeachingAssistant + ", isStudent="
+ isStudent + '}';
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import java.util.Set;
import java.util.function.Predicate;
import java.util.stream.Stream;
import java.util.zip.ZipEntry;
Expand Down Expand Up @@ -33,6 +34,12 @@ public class ZipFileService {

private final FileService fileService;

/**
* Set of file names that should be ignored when zipping.
* This currently only includes the gc.log.lock (garbage collector) file created by JGit in programming repositories.
*/
private static final Set<Path> IGNORED_ZIP_FILE_NAMES = Set.of(Path.of("gc.log.lock"));

public ZipFileService(FileService fileService) {
this.fileService = fileService;
}
Expand Down Expand Up @@ -113,7 +120,7 @@ private void createZipFileFromPathStream(Path zipFilePath, Stream<Path> paths, P
if (extraFilter != null) {
filteredPaths = filteredPaths.filter(extraFilter);
}
filteredPaths.forEach(path -> {
filteredPaths.filter(path -> !IGNORED_ZIP_FILE_NAMES.contains(path)).forEach(path -> {
ZipEntry zipEntry = new ZipEntry(pathsRoot.relativize(path).toString());
copyToZipFile(zipOutputStream, path, zipEntry);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -562,8 +562,9 @@ public Submission findLatestSubmissionWithRatedResultWithCompletionDate(Particip
boolean ratedOrPractice = Boolean.TRUE.equals(result.isRated()) || participation.isPracticeMode();
boolean noProgrammingAndAssessmentOver = !isProgrammingExercise && isAssessmentOver;
// For programming exercises we check that the assessment due date has passed (if set) for manual results otherwise we always show the automatic result
boolean programmingAfterAssessmentOrAutomatic = isProgrammingExercise && ((result.isManual() && isAssessmentOver) || result.isAutomatic());
if (ratedOrPractice && (noProgrammingAndAssessmentOver || programmingAfterAssessmentOrAutomatic)) {
boolean programmingAfterAssessmentOrAutomaticOrAthena = isProgrammingExercise
&& ((result.isManual() && isAssessmentOver) || result.isAutomatic() || result.isAthenaBased());
if (ratedOrPractice && (noProgrammingAndAssessmentOver || programmingAfterAssessmentOrAutomaticOrAthena)) {
// take the first found result that fulfills the above requirements
// or
// take newer results and thus disregard older ones
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ public Result getResultForCorrectionRound(int correctionRound) {
*/
@NotNull
private List<Result> filterNonAutomaticResults() {
return results.stream().filter(result -> result == null || !(result.isAutomatic() || result.isAthenaAutomatic())).toList();
return results.stream().filter(result -> result == null || !(result.isAutomatic() || result.isAthenaBased())).toList();
}

/**
Expand All @@ -188,8 +188,7 @@ public boolean hasResultForCorrectionRound(int correctionRound) {
*/
@JsonIgnore
public void removeAutomaticResults() {
this.results = this.results.stream().filter(result -> result == null || !(result.isAutomatic() || result.isAthenaAutomatic()))
.collect(Collectors.toCollection(ArrayList::new));
this.results = this.results.stream().filter(result -> result == null || !(result.isAutomatic() || result.isAthenaBased())).collect(Collectors.toCollection(ArrayList::new));
}

/**
Expand All @@ -214,7 +213,7 @@ public List<Result> getResults() {

@JsonIgnore
public List<Result> getManualResults() {
return results.stream().filter(result -> result != null && !result.isAutomatic() && !result.isAthenaAutomatic()).collect(Collectors.toCollection(ArrayList::new));
return results.stream().filter(result -> result != null && !result.isAutomatic() && !result.isAthenaBased()).collect(Collectors.toCollection(ArrayList::new));
}

/**
Expand All @@ -224,7 +223,7 @@ public List<Result> getManualResults() {
*/
@JsonIgnore
public List<Result> getNonAthenaResults() {
return results.stream().filter(result -> result != null && !result.isAthenaAutomatic()).collect(Collectors.toCollection(ArrayList::new));
return results.stream().filter(result -> result != null && !result.isAthenaBased()).collect(Collectors.toCollection(ArrayList::new));
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
import jakarta.annotation.Nullable;
import jakarta.validation.constraints.NotNull;

import org.apache.velocity.exception.ResourceNotFoundException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
Expand Down Expand Up @@ -382,7 +381,7 @@ private ResponseEntity<StudentParticipation> handleExerciseFeedbackRequest(Exerc
throw new BadRequestAlertException("Not intended for the use in exams", "participation", "preconditions not met");
}
if (exercise.getDueDate() != null && now().isAfter(exercise.getDueDate())) {
throw new BadRequestAlertException("The due date is over", "participation", "preconditions not met");
throw new BadRequestAlertException("The due date is over", "participation", "feedbackRequestAfterDueDate", true);
}
if (exercise instanceof ProgrammingExercise) {
((ProgrammingExercise) exercise).validateSettingsForFeedbackRequest();
Expand All @@ -393,7 +392,7 @@ private ResponseEntity<StudentParticipation> handleExerciseFeedbackRequest(Exerc
StudentParticipation participation = (exercise instanceof ProgrammingExercise)
? programmingExerciseParticipationService.findStudentParticipationByExerciseAndStudentId(exercise, principal.getName())
: studentParticipationRepository.findByExerciseIdAndStudentLogin(exercise.getId(), principal.getName())
.orElseThrow(() -> new ResourceNotFoundException("Participation not found"));
.orElseThrow(() -> new BadRequestAlertException("Submission not found", "participation", "noSubmissionExists", true));

checkAccessPermissionOwner(participation, user);
participation = studentParticipationRepository.findByIdWithResultsElseThrow(participation.getId());
Expand All @@ -406,15 +405,14 @@ private ResponseEntity<StudentParticipation> handleExerciseFeedbackRequest(Exerc
}
else if (exercise instanceof ProgrammingExercise) {
if (participation.findLatestLegalResult() == null) {
throw new BadRequestAlertException("User has not reached the conditions to submit a feedback request", "participation", "preconditions not met");
throw new BadRequestAlertException("You need to submit at least once and have the build results", "participation", "noSubmissionExists", true);
}
}

// Check if feedback has already been requested
var currentDate = now();
var participationIndividualDueDate = participation.getIndividualDueDate();
if (participationIndividualDueDate != null && currentDate.isAfter(participationIndividualDueDate)) {
throw new BadRequestAlertException("Request has already been sent", "participation", "already sent");
var latestResult = participation.findLatestResult();
if (latestResult != null && latestResult.getAssessmentType() == AssessmentType.AUTOMATIC_ATHENA && latestResult.getCompletionDate().isAfter(now())) {
throw new BadRequestAlertException("Request has already been sent", "participation", "feedbackRequestAlreadySent", true);
}

// Process feedback request
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
import de.jplag.options.JPlagOptions;
import de.jplag.python3.PythonLanguage;
import de.jplag.reporting.reportobject.ReportObjectFactory;
import de.jplag.rlang.RLanguage;
import de.jplag.rust.RustLanguage;
import de.jplag.swift.SwiftLanguage;
import de.tum.cit.aet.artemis.core.exception.BadRequestAlertException;
Expand Down Expand Up @@ -310,14 +311,15 @@ public void deleteTempLocalRepository(Repository repository) {

private Language getJPlagProgrammingLanguage(ProgrammingExercise programmingExercise) {
return switch (programmingExercise.getProgrammingLanguage()) {
case JAVA -> new JavaLanguage();
case C -> new CLanguage();
case PYTHON -> new PythonLanguage();
case SWIFT -> new SwiftLanguage();
case JAVA -> new JavaLanguage();
case JAVASCRIPT -> new JavaScriptLanguage();
case KOTLIN -> new KotlinLanguage();
case PYTHON -> new PythonLanguage();
case R -> new RLanguage();
case RUST -> new RustLanguage();
case JAVASCRIPT -> new JavaScriptLanguage();
case EMPTY, PHP, DART, HASKELL, ASSEMBLER, OCAML, C_SHARP, C_PLUS_PLUS, SQL, R, TYPESCRIPT, GO, MATLAB, BASH, VHDL, RUBY, POWERSHELL, ADA ->
case SWIFT -> new SwiftLanguage();
case EMPTY, PHP, DART, HASKELL, ASSEMBLER, OCAML, C_SHARP, C_PLUS_PLUS, SQL, TYPESCRIPT, GO, MATLAB, BASH, VHDL, RUBY, POWERSHELL, ADA ->
throw new BadRequestAlertException("Programming language " + programmingExercise.getProgrammingLanguage() + " not supported for plagiarism check.",
"ProgrammingExercise", "notSupported");
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -712,8 +712,7 @@ private boolean checkForRatedAndAssessedResult(Result result) {
* @return true if the result is manual and the assessment is over, or it is an automatic result, false otherwise
*/
private boolean checkForAssessedResult(Result result) {
return result.getCompletionDate() != null
&& ((result.isManual() && ExerciseDateService.isAfterAssessmentDueDate(this)) || result.isAutomatic() || result.isAthenaAutomatic());
return result.getCompletionDate() != null && ((result.isManual() && ExerciseDateService.isAfterAssessmentDueDate(this)) || result.isAutomatic() || result.isAthenaBased());
}

@Override
Expand Down
Loading

0 comments on commit 90dc3cc

Please sign in to comment.