Skip to content

Commit

Permalink
Merge branch 'develop' into feature/communication/implement-faq-search
Browse files Browse the repository at this point in the history
# Conflicts:
#	src/test/javascript/spec/component/exam/participate/exam-navigation-sidebar.component.spec.ts
  • Loading branch information
cremertim committed Oct 12, 2024
2 parents 362c798 + 4c98c06 commit 89236a5
Show file tree
Hide file tree
Showing 196 changed files with 1,722 additions and 1,728 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
2 changes: 1 addition & 1 deletion docs/dev/guidelines/database.rst
Original file line number Diff line number Diff line change
Expand Up @@ -295,7 +295,7 @@ Best Practices
// IrisSubSettings.java
@Column(name = "allowed_models")
@Convert(converter = IrisModelListConverter.class)
private TreeSet<String> allowedModels = new TreeSet<>();
private TreeSet<String> allowedVariants = new TreeSet<>();
* **Ordered Collection with duplicates**: When you want to order the collection of (potentially duplicated) objects of the relationship, then always use a ``List``. It is important to note here that there is no inherent order in a database table. One could argue that you can use the ``id`` field for the ordering, but there are edge cases where this can lead to problems. Therefore, for an ordered collection with duplicates, **always** annotate it with ``@OrderColumn``. An order column indicates to Hibernate that we want to order our collection based on a specific column of our data table. By default, the column name it expects is *tablenameS\_order*. For ordered collections, we also recommend that you annotate them with ``cascade = CascadeType.ALL`` and ``orphanRemoval = true``. E.g.:
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
Loading

0 comments on commit 89236a5

Please sign in to comment.