Skip to content

Commit

Permalink
Merge branch 'develop' into feature/exams/quiz-pool-configuration
Browse files Browse the repository at this point in the history
  • Loading branch information
rriyaldhi authored Jul 14, 2023
2 parents 746e454 + 6f4b869 commit b61ec95
Show file tree
Hide file tree
Showing 49 changed files with 829 additions and 855 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import java.io.Serializable;
import java.util.Collections;

import javax.annotation.Nullable;
import javax.persistence.EntityManager;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.*;
Expand All @@ -11,7 +12,6 @@
import org.springframework.data.jpa.repository.support.JpaEntityInformation;
import org.springframework.data.jpa.repository.support.JpaEntityInformationSupport;
import org.springframework.data.jpa.repository.support.SimpleJpaRepository;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;

public class RepositoryImpl<T, ID extends Serializable> extends SimpleJpaRepository<T, ID> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.constraints.NotNull;

import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.filter.OncePerRequestFilter;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.constraints.NotNull;

import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.filter.OncePerRequestFilter;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@

import java.util.List;

import org.jetbrains.annotations.NotNull;
import javax.validation.constraints.NotNull;

import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,22 +67,29 @@ public ProgrammingExercise importProgrammingExerciseFromFile(ProgrammingExercise
if (!"zip".equals(FileNameUtils.getExtension(zipFile.getOriginalFilename()))) {
throw new BadRequestAlertException("The file is not a zip file", "programmingExercise", "fileNotZip");
}
Path importExerciseDir = Files.createTempDirectory("imported-exercise-dir");
Path exerciseFilePath = Files.createTempFile(importExerciseDir, "exercise-for-import", ".zip");

zipFile.transferTo(exerciseFilePath);
zipFileService.extractZipFileRecursively(exerciseFilePath);
checkRepositoriesExist(importExerciseDir);
var oldShortName = getProgrammingExerciseFromDetailsFile(importExerciseDir).getShortName();
programmingExerciseService.validateNewProgrammingExerciseSettings(programmingExerciseForImport, course);
var importedProgrammingExercise = programmingExerciseService.createProgrammingExercise(programmingExerciseForImport);
if (Boolean.TRUE.equals(programmingExerciseForImport.isStaticCodeAnalysisEnabled())) {
staticCodeAnalysisService.createDefaultCategories(importedProgrammingExercise);
Path importExerciseDir = null;
ProgrammingExercise importedProgrammingExercise;
try {
importExerciseDir = Files.createTempDirectory("imported-exercise-dir");
Path exerciseFilePath = Files.createTempFile(importExerciseDir, "exercise-for-import", ".zip");

zipFile.transferTo(exerciseFilePath);
zipFileService.extractZipFileRecursively(exerciseFilePath);
checkRepositoriesExist(importExerciseDir);
var oldShortName = getProgrammingExerciseFromDetailsFile(importExerciseDir).getShortName();
programmingExerciseService.validateNewProgrammingExerciseSettings(programmingExerciseForImport, course);
importedProgrammingExercise = programmingExerciseService.createProgrammingExercise(programmingExerciseForImport);
if (Boolean.TRUE.equals(programmingExerciseForImport.isStaticCodeAnalysisEnabled())) {
staticCodeAnalysisService.createDefaultCategories(importedProgrammingExercise);
}
copyEmbeddedFiles(exerciseFilePath.toAbsolutePath().getParent().resolve(FileNameUtils.getBaseName(exerciseFilePath.toString())));
importRepositoriesFromFile(importedProgrammingExercise, importExerciseDir, oldShortName);
importedProgrammingExercise.setCourse(course);
}
finally {
// want to make sure the directories are deleted, even if an exception is thrown
fileService.scheduleForDirectoryDeletion(importExerciseDir, 5);
}
copyEmbeddedFiles(exerciseFilePath.toAbsolutePath().getParent().resolve(FileNameUtils.getBaseName(exerciseFilePath.toString())));
importRepositoriesFromFile(importedProgrammingExercise, importExerciseDir, oldShortName);
importedProgrammingExercise.setCourse(course);
fileService.scheduleForDirectoryDeletion(importExerciseDir, 5);
return importedProgrammingExercise;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -300,31 +300,25 @@ public ResponseEntity<Void> deleteAssessment(@PathVariable Long participationId,
}

/**
* GET participations/:participationId/submissions/:submissionId/for-text-assessment
* Given an exerciseId and a submissionId, the method retrieves from the database all the data needed by the tutor to assess the submission. If the tutor has already started
* GET text-submissions/:submissionId/for-assessment
* Given a submissionId, the method retrieves from the database all the data needed by the tutor to assess the submission. If the tutor has already started
* assessing the submission, then we also return all the results the tutor has already inserted. If another tutor has already started working on this submission, the system
* returns an error
* In case an instructors calls, the resultId is used first. In case the resultId is not set, the correctionRound is used.
* In case neither resultId nor correctionRound are set, the first correctionRound is used.
*
* @param participationId the id of the participation the submissions belongs to
* @param submissionId the id of the submission we want
* @param correctionRound correction round for which we want the submission
* @param resultId if result already exists, we want to get the submission for this specific result
* @return a Participation of the tutor in the submission
*/
@GetMapping("participations/{participationId}/submissions/{submissionId}/for-text-assessment")
@GetMapping("text-submissions/{submissionId}/for-assessment")
@EnforceAtLeastTutor
public ResponseEntity<Participation> retrieveParticipationForSubmission(@PathVariable Long participationId, @PathVariable Long submissionId,
public ResponseEntity<Participation> retrieveParticipationForSubmission(@PathVariable Long submissionId,
@RequestParam(value = "correction-round", defaultValue = "0") int correctionRound, @RequestParam(value = "resultId", required = false) Long resultId) {

log.debug("REST request to get data for tutors text assessment submission: {}", submissionId);
final var textSubmission = textSubmissionRepository.findByIdWithParticipationExerciseResultAssessorElseThrow(submissionId);
final Participation participation = textSubmission.getParticipation();
if (!participation.getId().equals(participationId)) {
throw new BadRequestAlertException("participationId in Submission of submissionId " + submissionId + " doesn't match the paths participationId!", "participationId",
"participationIdMismatch");
}
final var exercise = participation.getExercise();
final User user = userRepository.getUserWithGroupsAndAuthorities();
checkAuthorization(exercise, user);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,23 +69,22 @@ <h3>
id="responseTextArea"
class="col-12 px-1"
rows="4"
[maxlength]="this.course!.maxComplaintResponseTextLimit!"
[maxlength]="maxComplaintResponseTextLimit"
[(ngModel)]="complaintResponse.responseText"
[readonly]="handled || isLockedForLoggedInUser"
[disabled]="handled || isLockedForLoggedInUser"
ondrop="return false;"
>
</textarea>
<jhi-textarea-counter [maxLength]="this.course!.maxComplaintResponseTextLimit!" [content]="complaintResponse.responseText" [visible]="!handled">
</jhi-textarea-counter>
<jhi-textarea-counter [maxLength]="maxComplaintResponseTextLimit" [content]="complaintResponse.responseText" [visible]="!handled"> </jhi-textarea-counter>
</div>
<div *ngIf="!handled && complaint.complaintType === ComplaintType.COMPLAINT" class="d-flex flex-wrap gap-1 justify-content-between mt-1">
<button
id="acceptComplaintButton"
type="button"
class="btn btn-success btn-block"
(click)="respondToComplaint(true)"
[disabled]="isLockedForLoggedInUser || complaintResponseTextLength() > this.course!.maxComplaintResponseTextLimit!"
[disabled]="isLockedForLoggedInUser || complaintResponseTextLength() > maxComplaintResponseTextLimit"
title="{{ 'artemisApp.complaintResponse.updateAssessmentTooltip' | artemisTranslate }}"
>
{{ 'artemisApp.complaintResponse.updateAssessment' | artemisTranslate }}
Expand All @@ -95,7 +94,7 @@ <h3>
type="button"
class="btn btn-danger btn-block"
(click)="respondToComplaint(false)"
[disabled]="isLockedForLoggedInUser || complaintResponseTextLength() > this.course!.maxComplaintResponseTextLimit!"
[disabled]="isLockedForLoggedInUser || complaintResponseTextLength() > maxComplaintResponseTextLimit"
title="{{ 'artemisApp.complaintResponse.rejectComplaintTooltip' | artemisTranslate }}"
>
{{ 'artemisApp.complaintResponse.rejectComplaint' | artemisTranslate }}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ export class ComplaintsForTutorComponent implements OnInit {
lockedByCurrentUser = false;
isLockedForLoggedInUser = false;
course?: Course;
maxComplaintResponseTextLimit: number;

constructor(
private alertService: AlertService,
Expand All @@ -51,6 +52,12 @@ export class ComplaintsForTutorComponent implements OnInit {
ngOnInit(): void {
this.course = getCourseFromExercise(this.exercise!);

this.maxComplaintResponseTextLimit = this.course?.maxComplaintResponseTextLimit ?? 0;
if (this.exercise?.exerciseGroup) {
// Exams should always allow at least 2000 characters
this.maxComplaintResponseTextLimit = Math.max(2000, this.maxComplaintResponseTextLimit);
}

if (this.complaint) {
this.complaintText = this.complaint.complaintText;
this.handled = this.complaint.accepted !== undefined;
Expand Down Expand Up @@ -147,10 +154,9 @@ export class ComplaintsForTutorComponent implements OnInit {
this.alertService.error('artemisApp.complaintResponse.noText');
return;
}
// eslint-disable-next-line @typescript-eslint/no-non-null-asserted-optional-chain
if (this.complaintResponse.responseText.length > this.course?.maxComplaintResponseTextLimit!) {
if (this.complaintResponse.responseText.length > this.maxComplaintResponseTextLimit) {
this.alertService.error('artemisApp.complaint.exceededComplaintResponseTextLimit', {
maxComplaintRespondTextLimit: this.course?.maxComplaintResponseTextLimit,
maxComplaintRespondTextLimit: this.maxComplaintResponseTextLimit,
});
return;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,16 +32,16 @@ <h3>{{ complaintType === ComplaintType.COMPLAINT ? ('artemisApp.complaint.title'
</p>

<div class="d-flex flex-column">
<textarea id="complainTextArea" class="col-12 px-1" rows="4" [maxLength]="this.course!.maxComplaintTextLimit!" [(ngModel)]="complaintText"> </textarea>
<jhi-textarea-counter [maxLength]="this.course!.maxComplaintTextLimit!" [content]="complaintText" [visible]="true"> </jhi-textarea-counter>
<textarea id="complainTextArea" class="col-12 px-1" rows="4" [maxLength]="this.maxComplaintTextLimit" [(ngModel)]="complaintText"> </textarea>
<jhi-textarea-counter [maxLength]="this.maxComplaintTextLimit" [content]="complaintText" [visible]="true"> </jhi-textarea-counter>
</div>

<div class="row">
<div class="col-6">
<button
id="submit-complaint"
class="btn btn-primary"
[disabled]="!complaintText || complaintTextLength() > this.course!.maxComplaintTextLimit!"
[disabled]="!complaintText || complaintTextLength() > this.maxComplaintTextLimit"
(click)="createComplaint()"
>
{{ complaintType === ComplaintType.COMPLAINT ? ('artemisApp.complaint.submit' | artemisTranslate) : ('artemisApp.moreFeedback.button' | artemisTranslate) }}
Expand Down
13 changes: 10 additions & 3 deletions src/main/webapp/app/complaints/form/complaints-form.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,23 @@ export class ComplaintsFormComponent implements OnInit {
// eslint-disable-next-line @angular-eslint/no-output-native
@Output() submit: EventEmitter<void> = new EventEmitter();
maxComplaintsPerCourse = 1;
maxComplaintTextLimit: number;
complaintText?: string;
ComplaintType = ComplaintType;
course?: Course;

readonly ComplaintType = ComplaintType;

constructor(private complaintService: ComplaintService, private alertService: AlertService) {}

ngOnInit(): void {
this.course = getCourseFromExercise(this.exercise);
this.maxComplaintTextLimit = this.course?.maxComplaintTextLimit ?? 0;
if (this.exercise.course) {
// only set the complaint limit for course exercises, there are unlimited complaints for exams
this.maxComplaintsPerCourse = this.exercise.teamMode ? this.exercise.course.maxTeamComplaints! : this.exercise.course.maxComplaints!;
} else {
// Complaints for exams should always allow at least 2000 characters. If the course limit is higher, the custom limit gets used.
this.maxComplaintTextLimit = Math.max(2000, this.maxComplaintTextLimit);
}
}

Expand All @@ -47,8 +54,8 @@ export class ComplaintsFormComponent implements OnInit {
complaint.complaintType = this.complaintType;

// TODO: Rethink global client error handling and adapt this line accordingly
if (complaint.complaintText !== undefined && this.course!.maxComplaintTextLimit! < complaint.complaintText!.length) {
this.alertService.error('artemisApp.complaint.exceededComplaintTextLimit', { maxComplaintTextLimit: this.course!.maxComplaintTextLimit! });
if (complaint.complaintText !== undefined && this.maxComplaintTextLimit < complaint.complaintText!.length) {
this.alertService.error('artemisApp.complaint.exceededComplaintTextLimit', { maxComplaintTextLimit: this.maxComplaintTextLimit! });
return;
}

Expand Down
4 changes: 2 additions & 2 deletions src/main/webapp/app/course/manage/course-update.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -428,8 +428,8 @@ export class CourseUpdateComponent implements OnInit {
this.courseForm.controls['maxComplaints'].setValue(0);
this.courseForm.controls['maxTeamComplaints'].setValue(0);
this.courseForm.controls['maxComplaintTimeDays'].setValue(0);
this.courseForm.controls['maxComplaintTextLimit'].setValue(0);
this.courseForm.controls['maxComplaintResponseTextLimit'].setValue(0);
this.courseForm.controls['maxComplaintTextLimit'].setValue(2000);
this.courseForm.controls['maxComplaintResponseTextLimit'].setValue(2000);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
$top-border-radius: 3px;

min-height: unset;
padding: 15px 25px;
margin: $margin $margin 0 $margin;
background-color: var(--header-participation-page-info-bar-background);
border-top-left-radius: $top-border-radius;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,6 @@ export class TextAssessmentService {
* @param textBlocks of type {TextBlock[]}
* @param complaintResponse of type {ComplaintResponse}
* @param submissionId of corresponding submission of type {number}
* @param participationId of the corresponding participation
*/
public updateAssessmentAfterComplaint(
feedbacks: Feedback[],
Expand Down Expand Up @@ -124,12 +123,11 @@ export class TextAssessmentService {
}

/**
* @param participationId id of the participation the submission belongs to
* @param submissionId id of the submission for which the feedback items should be retrieved of type {number}
* @param correctionRound
* @param resultId id of the searched result (if instructors search for a specific result)
*/
public getFeedbackDataForExerciseSubmission(participationId: number, submissionId: number, correctionRound = 0, resultId?: number): Observable<StudentParticipation> {
public getFeedbackDataForExerciseSubmission(submissionId: number, correctionRound = 0, resultId?: number): Observable<StudentParticipation> {
let params = new HttpParams();
if (resultId && resultId > 0) {
// in case resultId is set, we do not need the correction round
Expand All @@ -138,7 +136,7 @@ export class TextAssessmentService {
params = params.set('correction-round', correctionRound.toString());
}
return this.http
.get<StudentParticipation>(`${this.resourceUrl}/participations/${participationId}/submissions/${submissionId}/for-text-assessment`, { observe: 'response', params })
.get<StudentParticipation>(`${this.resourceUrl}/text-submissions/${submissionId}/for-assessment`, { observe: 'response', params })
.pipe<HttpResponse<StudentParticipation>, StudentParticipation>(
// Wire up Result and Submission
tap((response: HttpResponse<StudentParticipation>) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,15 +39,14 @@ export class StudentParticipationResolver implements Resolve<StudentParticipatio
* @param route
*/
resolve(route: ActivatedRouteSnapshot) {
const participationId = Number(route.paramMap.get('participationId'));
const submissionId = Number(route.paramMap.get('submissionId'));
const correctionRound = Number(route.queryParamMap.get('correction-round'));
const resultId = Number(route.paramMap.get('resultId'));
if (resultId) {
return this.textAssessmentService.getFeedbackDataForExerciseSubmission(participationId, submissionId, undefined, resultId).pipe(catchError(() => of(undefined)));
return this.textAssessmentService.getFeedbackDataForExerciseSubmission(submissionId, undefined, resultId).pipe(catchError(() => of(undefined)));
}
if (submissionId) {
return this.textAssessmentService.getFeedbackDataForExerciseSubmission(participationId, submissionId, correctionRound).pipe(catchError(() => of(undefined)));
return this.textAssessmentService.getFeedbackDataForExerciseSubmission(submissionId, correctionRound).pipe(catchError(() => of(undefined)));
}
return of(undefined);
}
Expand All @@ -69,7 +68,7 @@ export const textSubmissionAssessmentRoutes: Routes = [
canActivate: [UserRouteAccessService],
},
{
path: 'participations/:participationId/submissions/:submissionId/assessment',
path: 'submissions/:submissionId/assessment',
component: TextSubmissionAssessmentComponent,
data: {
authorities: [Authority.ADMIN, Authority.INSTRUCTOR, Authority.EDITOR, Authority.TA],
Expand All @@ -82,7 +81,7 @@ export const textSubmissionAssessmentRoutes: Routes = [
canActivate: [UserRouteAccessService],
},
{
path: 'participations/:participationId/submissions/:submissionId/assessments/:resultId',
path: 'submissions/:submissionId/assessments/:resultId',
component: TextSubmissionAssessmentComponent,
data: {
authorities: [Authority.ADMIN, Authority.INSTRUCTOR],
Expand Down
Loading

0 comments on commit b61ec95

Please sign in to comment.