From bb3d7d223dcace0a132f6abf6b5d8f48e541878a Mon Sep 17 00:00:00 2001 From: LeonWehrhahn Date: Thu, 9 Jan 2025 15:49:21 +0100 Subject: [PATCH 01/17] Refactor feedback utility functions to separate manual and automatic unreferenced feedback filtering --- .../participate/modeling-submission.component.ts | 4 ++-- .../app/exercises/shared/result/result.utils.ts | 15 ++++++++++----- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/src/main/webapp/app/exercises/modeling/participate/modeling-submission.component.ts b/src/main/webapp/app/exercises/modeling/participate/modeling-submission.component.ts index b77b05f098dd..3c0e259e7707 100644 --- a/src/main/webapp/app/exercises/modeling/participate/modeling-submission.component.ts +++ b/src/main/webapp/app/exercises/modeling/participate/modeling-submission.component.ts @@ -15,7 +15,7 @@ import { ModelingAssessmentService } from 'app/exercises/modeling/assess/modelin import { ModelingSubmissionService } from 'app/exercises/modeling/participate/modeling-submission.service'; import { ModelingEditorComponent } from 'app/exercises/modeling/shared/modeling-editor.component'; import { getExerciseDueDate, hasExerciseDueDatePassed } from 'app/exercises/shared/exercise/exercise.utils'; -import { addParticipationToResult, getUnreferencedFeedback } from 'app/exercises/shared/result/result.utils'; +import { addParticipationToResult, getAutomaticUnreferencedFeedback } from 'app/exercises/shared/result/result.utils'; import { AccountService } from 'app/core/auth/account.service'; import { GuidedTourService } from 'app/guided-tour/guided-tour.service'; import { modelingTour } from 'app/guided-tour/tours/modeling-tour'; @@ -664,7 +664,7 @@ export class ModelingSubmissionComponent implements OnInit, OnDestroy, Component get unreferencedFeedback(): Feedback[] | undefined { if (this.assessmentResult?.feedbacks) { checkSubsequentFeedbackInAssessment(this.assessmentResult.feedbacks); - return getUnreferencedFeedback(this.assessmentResult.feedbacks); + return getAutomaticUnreferencedFeedback(this.assessmentResult.feedbacks); } return undefined; } diff --git a/src/main/webapp/app/exercises/shared/result/result.utils.ts b/src/main/webapp/app/exercises/shared/result/result.utils.ts index c77cc3a9fcf2..8e2b2eb99767 100644 --- a/src/main/webapp/app/exercises/shared/result/result.utils.ts +++ b/src/main/webapp/app/exercises/shared/result/result.utils.ts @@ -121,11 +121,16 @@ export const addParticipationToResult = (result: Result | undefined, participati * @returns an array with the unreferenced feedback of the result */ export const getUnreferencedFeedback = (feedbacks: Feedback[] | undefined): Feedback[] | undefined => { - return feedbacks - ? feedbacks.filter( - (feedbackElement) => !feedbackElement.reference && (feedbackElement.type === FeedbackType.MANUAL_UNREFERENCED || feedbackElement.type === FeedbackType.AUTOMATIC), - ) - : undefined; + return feedbacks ? feedbacks.filter((feedbackElement) => !feedbackElement.reference && feedbackElement.type === FeedbackType.MANUAL_UNREFERENCED) : undefined; +}; + +/** + * searches for all unreferenced feedback of type AUTOMATIC in an array of feedbacks of a result + * @param feedbacks the feedback of a result + * @returns an array with the unreferenced feedback of the result + */ +export const getAutomaticUnreferencedFeedback = (feedbacks: Feedback[] | undefined): Feedback[] | undefined => { + return feedbacks ? feedbacks.filter((feedbackElement) => !feedbackElement.reference && feedbackElement.type === FeedbackType.AUTOMATIC) : undefined; }; export function isAIResultAndFailed(result: Result | undefined): boolean { From 7b54b4b110f7245dbb81fa64ed42fa10b821d062 Mon Sep 17 00:00:00 2001 From: LeonWehrhahn Date: Thu, 9 Jan 2025 18:06:54 +0100 Subject: [PATCH 02/17] Rename feedback utility function --- .../participate/file-upload-submission.component.ts | 4 ++-- .../participate/code-editor-student-container.component.ts | 4 ++-- src/main/webapp/app/exercises/shared/result/result.utils.ts | 6 +++--- .../app/exercises/text/participate/text-editor.component.ts | 4 ++-- .../javascript/spec/component/utils/result.utils.spec.ts | 4 ++-- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/main/webapp/app/exercises/file-upload/participate/file-upload-submission.component.ts b/src/main/webapp/app/exercises/file-upload/participate/file-upload-submission.component.ts index ebd49234be13..2157ebc343b1 100644 --- a/src/main/webapp/app/exercises/file-upload/participate/file-upload-submission.component.ts +++ b/src/main/webapp/app/exercises/file-upload/participate/file-upload-submission.component.ts @@ -22,7 +22,7 @@ import { ButtonType } from 'app/shared/components/button.component'; import { Result } from 'app/entities/result.model'; import { AccountService } from 'app/core/auth/account.service'; import { getFirstResultWithComplaint, getLatestSubmissionResult } from 'app/entities/submission.model'; -import { addParticipationToResult, getUnreferencedFeedback } from 'app/exercises/shared/result/result.utils'; +import { addParticipationToResult, getManualUnreferencedFeedback } from 'app/exercises/shared/result/result.utils'; import { Feedback, checkSubsequentFeedbackInAssessment } from 'app/entities/feedback.model'; import { onError } from 'app/shared/util/global.utils'; import { getCourseFromExercise } from 'app/entities/exercise.model'; @@ -247,7 +247,7 @@ export class FileUploadSubmissionComponent implements OnInit, ComponentCanDeacti get unreferencedFeedback(): Feedback[] | undefined { if (this.result?.feedbacks) { checkSubsequentFeedbackInAssessment(this.result.feedbacks); - return getUnreferencedFeedback(this.result.feedbacks); + return getManualUnreferencedFeedback(this.result.feedbacks); } return undefined; } diff --git a/src/main/webapp/app/exercises/programming/participate/code-editor-student-container.component.ts b/src/main/webapp/app/exercises/programming/participate/code-editor-student-container.component.ts index 697507764dac..bb9a508331d6 100644 --- a/src/main/webapp/app/exercises/programming/participate/code-editor-student-container.component.ts +++ b/src/main/webapp/app/exercises/programming/participate/code-editor-student-container.component.ts @@ -14,7 +14,7 @@ import { DomainType } from 'app/exercises/programming/shared/code-editor/model/c import { ActivatedRoute } from '@angular/router'; import { CodeEditorContainerComponent } from 'app/exercises/programming/shared/code-editor/container/code-editor-container.component'; import { ProgrammingExerciseStudentParticipation } from 'app/entities/participation/programming-exercise-student-participation.model'; -import { getUnreferencedFeedback } from 'app/exercises/shared/result/result.utils'; +import { getManualUnreferencedFeedback } from 'app/exercises/shared/result/result.utils'; import { SubmissionType } from 'app/entities/submission.model'; import { SubmissionPolicyType } from 'app/entities/submission-policy.model'; import { Course } from 'app/entities/course.model'; @@ -160,7 +160,7 @@ export class CodeEditorStudentContainerComponent implements OnInit, OnDestroy { get unreferencedFeedback(): Feedback[] { if (this.latestResult?.feedbacks) { checkSubsequentFeedbackInAssessment(this.latestResult.feedbacks); - return getUnreferencedFeedback(this.latestResult.feedbacks) ?? []; + return getManualUnreferencedFeedback(this.latestResult.feedbacks) ?? []; } return []; } diff --git a/src/main/webapp/app/exercises/shared/result/result.utils.ts b/src/main/webapp/app/exercises/shared/result/result.utils.ts index 8e2b2eb99767..85b30f694334 100644 --- a/src/main/webapp/app/exercises/shared/result/result.utils.ts +++ b/src/main/webapp/app/exercises/shared/result/result.utils.ts @@ -116,16 +116,16 @@ export const addParticipationToResult = (result: Result | undefined, participati }; /** - * searches for all unreferenced feedback in an array of feedbacks of a result + * searches for all manual unreferenced feedback in an array of feedbacks of a result * @param feedbacks the feedback of a result * @returns an array with the unreferenced feedback of the result */ -export const getUnreferencedFeedback = (feedbacks: Feedback[] | undefined): Feedback[] | undefined => { +export const getManualUnreferencedFeedback = (feedbacks: Feedback[] | undefined): Feedback[] | undefined => { return feedbacks ? feedbacks.filter((feedbackElement) => !feedbackElement.reference && feedbackElement.type === FeedbackType.MANUAL_UNREFERENCED) : undefined; }; /** - * searches for all unreferenced feedback of type AUTOMATIC in an array of feedbacks of a result + * searches for all automatic unreferenced feedback in an array of feedbacks of a result * @param feedbacks the feedback of a result * @returns an array with the unreferenced feedback of the result */ diff --git a/src/main/webapp/app/exercises/text/participate/text-editor.component.ts b/src/main/webapp/app/exercises/text/participate/text-editor.component.ts index f45eae358ac0..7e556f44aeac 100644 --- a/src/main/webapp/app/exercises/text/participate/text-editor.component.ts +++ b/src/main/webapp/app/exercises/text/participate/text-editor.component.ts @@ -20,7 +20,7 @@ import { TextSubmission } from 'app/entities/text/text-submission.model'; import { StringCountService } from 'app/exercises/text/participate/string-count.service'; import { AccountService } from 'app/core/auth/account.service'; import { getFirstResultWithComplaint, getLatestSubmissionResult, setLatestSubmissionResult } from 'app/entities/submission.model'; -import { getUnreferencedFeedback, isAthenaAIResult } from 'app/exercises/shared/result/result.utils'; +import { getManualUnreferencedFeedback, isAthenaAIResult } from 'app/exercises/shared/result/result.utils'; import { onError } from 'app/shared/util/global.utils'; import { Course } from 'app/entities/course.model'; import { getCourseFromExercise } from 'app/entities/exercise.model'; @@ -312,7 +312,7 @@ export class TextEditorComponent implements OnInit, OnDestroy, ComponentCanDeact * Check whether or not a result exists and if, returns the unreferenced feedback of it */ get unreferencedFeedback(): Feedback[] | undefined { - return this.result ? getUnreferencedFeedback(this.result.feedbacks) : undefined; + return this.result ? getManualUnreferencedFeedback(this.result.feedbacks) : undefined; } get wordCount(): number { diff --git a/src/test/javascript/spec/component/utils/result.utils.spec.ts b/src/test/javascript/spec/component/utils/result.utils.spec.ts index d8c0a7f4c0e4..ef15c7735b38 100644 --- a/src/test/javascript/spec/component/utils/result.utils.spec.ts +++ b/src/test/javascript/spec/component/utils/result.utils.spec.ts @@ -1,9 +1,9 @@ import { ResultTemplateStatus, breakCircularResultBackReferences, + getManualUnreferencedFeedback, getResultIconClass, getTextColorClass, - getUnreferencedFeedback, isOnlyCompilationTested, } from 'app/exercises/shared/result/result.utils'; import { Feedback, FeedbackType, STATIC_CODE_ANALYSIS_FEEDBACK_IDENTIFIER } from 'app/entities/feedback.model'; @@ -20,7 +20,7 @@ import dayjs from 'dayjs/esm'; describe('ResultUtils', () => { it('should filter out all non unreferenced feedbacks', () => { const feedbacks = [{ reference: 'foo' }, { reference: 'foo', type: FeedbackType.MANUAL_UNREFERENCED }, { type: FeedbackType.MANUAL_UNREFERENCED }, {}]; - const unreferencedFeedbacks = getUnreferencedFeedback(feedbacks); + const unreferencedFeedbacks = getManualUnreferencedFeedback(feedbacks); expect(unreferencedFeedbacks).toEqual([{ type: FeedbackType.MANUAL_UNREFERENCED }]); }); From e285f5ba63e33b32fde3deb9aea50ca3e5cf3627 Mon Sep 17 00:00:00 2001 From: LeonWehrhahn Date: Thu, 9 Jan 2025 18:50:11 +0100 Subject: [PATCH 03/17] Update tests --- .../exercise/participation/ParticipationIntegrationTest.java | 2 +- .../modeling-submission/modeling-submission.component.spec.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/test/java/de/tum/cit/aet/artemis/exercise/participation/ParticipationIntegrationTest.java b/src/test/java/de/tum/cit/aet/artemis/exercise/participation/ParticipationIntegrationTest.java index bbe46c5b9b19..a2ee371bde2f 100644 --- a/src/test/java/de/tum/cit/aet/artemis/exercise/participation/ParticipationIntegrationTest.java +++ b/src/test/java/de/tum/cit/aet/artemis/exercise/participation/ParticipationIntegrationTest.java @@ -802,7 +802,7 @@ void requestModelingFeedbackSuccess_withAthenaFailure() throws Exception { request.putWithResponseBody("/api/exercises/" + modelingExercise.getId() + "/request-feedback", null, StudentParticipation.class, HttpStatus.OK); - verify(resultWebsocketService, timeout(2000).times(1)).broadcastNewResult(any(), resultCaptor.capture()); + verify(resultWebsocketService, timeout(2000).times(2)).broadcastNewResult(any(), resultCaptor.capture()); Result invokedModelingResult = resultCaptor.getAllValues().getFirst(); assertThat(invokedModelingResult).isNotNull(); diff --git a/src/test/javascript/spec/component/modeling-submission/modeling-submission.component.spec.ts b/src/test/javascript/spec/component/modeling-submission/modeling-submission.component.spec.ts index 5117ae0429b7..603e093efca5 100644 --- a/src/test/javascript/spec/component/modeling-submission/modeling-submission.component.spec.ts +++ b/src/test/javascript/spec/component/modeling-submission/modeling-submission.component.spec.ts @@ -706,7 +706,7 @@ describe('ModelingSubmissionComponent', () => { detailText: 'feedback1', credits: 1, gradingInstruction, - type: FeedbackType.MANUAL_UNREFERENCED, + type: FeedbackType.AUTOMATIC, } as Feedback, { id: 2, From dc58d1c8858734ee97de382c5a395e7b0467e387 Mon Sep 17 00:00:00 2001 From: LeonWehrhahn Date: Thu, 9 Jan 2025 19:00:13 +0100 Subject: [PATCH 04/17] Update modeling submission test to inlcude correct feedback type --- .../modeling-submission-team.component.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/javascript/spec/component/modeling-submission/modeling-submission-team.component.spec.ts b/src/test/javascript/spec/component/modeling-submission/modeling-submission-team.component.spec.ts index 1ebae9d40337..2a1eeea28e7d 100644 --- a/src/test/javascript/spec/component/modeling-submission/modeling-submission-team.component.spec.ts +++ b/src/test/javascript/spec/component/modeling-submission/modeling-submission-team.component.spec.ts @@ -505,7 +505,7 @@ describe('ModelingSubmissionComponent', () => { detailText: 'feedback1', credits: 1, gradingInstruction, - type: FeedbackType.MANUAL_UNREFERENCED, + type: FeedbackType.AUTOMATIC, } as Feedback, { id: 2, From 1846a52dda998317412007f9bc32c8bc4cb4845c Mon Sep 17 00:00:00 2001 From: LeonWehrhahn Date: Sat, 11 Jan 2025 18:18:34 +0100 Subject: [PATCH 05/17] Add feedback filtering tests to include automatic unreferenced feedback test --- .../spec/component/utils/result.utils.spec.ts | 23 +++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/src/test/javascript/spec/component/utils/result.utils.spec.ts b/src/test/javascript/spec/component/utils/result.utils.spec.ts index ef15c7735b38..9b4463b3c353 100644 --- a/src/test/javascript/spec/component/utils/result.utils.spec.ts +++ b/src/test/javascript/spec/component/utils/result.utils.spec.ts @@ -1,6 +1,7 @@ import { ResultTemplateStatus, breakCircularResultBackReferences, + getAutomaticUnreferencedFeedback, getManualUnreferencedFeedback, getResultIconClass, getTextColorClass, @@ -18,12 +19,30 @@ import { Result } from 'app/entities/result.model'; import dayjs from 'dayjs/esm'; describe('ResultUtils', () => { - it('should filter out all non unreferenced feedbacks', () => { - const feedbacks = [{ reference: 'foo' }, { reference: 'foo', type: FeedbackType.MANUAL_UNREFERENCED }, { type: FeedbackType.MANUAL_UNREFERENCED }, {}]; + it('should filter out all non unreferenced feedbacks that do not have type MANUAL_UNREFERENCED', () => { + const feedbacks = [ + { reference: 'foo' }, + { reference: 'foo', type: FeedbackType.MANUAL_UNREFERENCED }, + { type: FeedbackType.AUTOMATIC }, + { type: FeedbackType.MANUAL_UNREFERENCED }, + {}, + ]; const unreferencedFeedbacks = getManualUnreferencedFeedback(feedbacks); expect(unreferencedFeedbacks).toEqual([{ type: FeedbackType.MANUAL_UNREFERENCED }]); }); + it('should filter out all non unreferenced feedbacks that do not have type AUTOMATIC', () => { + const feedbacks = [ + { reference: 'foo' }, + { reference: 'foo', type: FeedbackType.AUTOMATIC }, + { type: FeedbackType.AUTOMATIC }, + { type: FeedbackType.MANUAL_UNREFERENCED }, + {}, + ]; + const unreferencedFeedbacks = getAutomaticUnreferencedFeedback(feedbacks); + expect(unreferencedFeedbacks).toEqual([{ type: FeedbackType.AUTOMATIC }]); + }); + it.each([ { result: { From d9cb6c156dcf63a3fe91f86b7ec25d566669a152 Mon Sep 17 00:00:00 2001 From: LeonWehrhahn Date: Tue, 21 Jan 2025 13:52:40 +0100 Subject: [PATCH 06/17] Adjust layout of modeling inline feedback view --- .../modeling-submission.component.html | 214 +++++++++--------- 1 file changed, 107 insertions(+), 107 deletions(-) diff --git a/src/main/webapp/app/exercises/modeling/participate/modeling-submission.component.html b/src/main/webapp/app/exercises/modeling/participate/modeling-submission.component.html index 8b2c80371e6b..323aa4073607 100644 --- a/src/main/webapp/app/exercises/modeling/participate/modeling-submission.component.html +++ b/src/main/webapp/app/exercises/modeling/participate/modeling-submission.component.html @@ -65,50 +65,50 @@ @if (!submission || submission.submitted || !isExamSummary) { -
-
- @if (modelingExercise.teamMode && (isActive || isLate)) { -
- -
- } -
- -
-
- @if (submission && (isActive || isLate) && !result && (!isLate || !submission.submitted) && !isFeedbackView) { -
- - - @if (modelingExercise.teamMode) { - +
+
+ @if (modelingExercise.teamMode && (isActive || isLate)) { +
+ +
+ } +
+ +
+
+ @if (submission && (isActive || isLate) && !result && (!isLate || !submission.submitted) && !isFeedbackView) { +
+ - } -
- } - @if (((!isActive || result) && (!isLate || submission.submitted)) || isFeedbackView) { -
-
+ + @if (modelingExercise.teamMode) { + + } +
+ } + @if (((!isActive || result) && (!isLate || submission.submitted)) || isFeedbackView) { +
-
- } - @if ((submission?.submitted && (!isActive || result)) || isFeedbackView) { -
-

- @if (!assessmentResult || !assessmentResult!.feedbacks || assessmentResult!.feedbacks!.length === 0) { -

- } - @if (assessmentResult && assessmentResult!.feedbacks && assessmentResult!.feedbacks!.length > 0) { -

- - - - - - - - @if (assessmentsNames) { - - @for (feedback of referencedFeedback; track feedback) { - - - - - } - - } -
- @if (feedback.reference) { - {{ assessmentsNames[feedback.referenceId!]?.type }} - } - @if (feedback.reference) { - {{ assessmentsNames[feedback.referenceId!]?.name }} - } - @if (feedback.reference) { -
- } - @if (feedback.text || feedback.detailText || feedback.gradingInstruction) { - Feedback: - } -
- {{ feedback.credits | number: '1.0-1' }} - @if (feedback.isSubsequent) { - - } -
- } -
- } + } + @if ((submission?.submitted && (!isActive || result)) || isFeedbackView) { +
+

+ @if (!assessmentResult || !assessmentResult!.feedbacks || assessmentResult!.feedbacks!.length === 0) { +

+ } + @if (assessmentResult && assessmentResult!.feedbacks && assessmentResult!.feedbacks!.length > 0) { +

+ + + + + + + + @if (assessmentsNames) { + + @for (feedback of referencedFeedback; track feedback) { + + + + + } + + } +
+ @if (feedback.reference) { + {{ assessmentsNames[feedback.referenceId!]?.type }} + } + @if (feedback.reference) { + {{ assessmentsNames[feedback.referenceId!]?.name }} + } + @if (feedback.reference) { +
+ } + @if (feedback.text || feedback.detailText || feedback.gradingInstruction) { + Feedback: + } +
+ {{ feedback.credits | number: '1.0-1' }} + @if (feedback.isSubsequent) { + + } +
+ } +
+ } +
-
- + +
@if (unreferencedFeedback && unreferencedFeedback!.length > 0) { /> }
- @if (result) { + @if (result && !isFeedbackView) {
} - @if (result && !examMode) { + @if (result && !examMode && !isFeedbackView) { Date: Fri, 31 Jan 2025 15:17:41 +0100 Subject: [PATCH 07/17] Fix layout --- .../participate/modeling-submission.component.html | 8 ++++---- .../participate/modeling-submission.component.scss | 5 +++++ 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/main/webapp/app/exercises/modeling/participate/modeling-submission.component.html b/src/main/webapp/app/exercises/modeling/participate/modeling-submission.component.html index 1e5b603af49c..d022ed99dc96 100644 --- a/src/main/webapp/app/exercises/modeling/participate/modeling-submission.component.html +++ b/src/main/webapp/app/exercises/modeling/participate/modeling-submission.component.html @@ -55,13 +55,13 @@ @if (isFeedbackView && showResultHistory) {
- +
} @if (modelingExercise) { - + @if (!submission || submission.submitted || !isExamSummary) { @@ -108,7 +108,7 @@
} @if (((!isActive || result) && (!isLate || submission.submitted)) || isFeedbackView) { -
+
} @if ((submission?.submitted && (!isActive || result)) || isFeedbackView) { -
+

@if (!assessmentResult || !assessmentResult!.feedbacks || assessmentResult!.feedbacks!.length === 0) {

diff --git a/src/main/webapp/app/exercises/modeling/participate/modeling-submission.component.scss b/src/main/webapp/app/exercises/modeling/participate/modeling-submission.component.scss index cb9db9e61d40..3aab262e16ce 100644 --- a/src/main/webapp/app/exercises/modeling/participate/modeling-submission.component.scss +++ b/src/main/webapp/app/exercises/modeling/participate/modeling-submission.component.scss @@ -14,6 +14,11 @@ jhi-modeling-assessment { min-height: 100vh; } +.editor-assessment { + max-width: 70vw; + max-height: 100vh; +} + .rg-bottom { align-self: center; cursor: row-resize; From 1229d66c783ce6182a5f3504afbc3ad14fe1cbac Mon Sep 17 00:00:00 2001 From: LeonWehrhahn Date: Fri, 31 Jan 2025 15:51:33 +0100 Subject: [PATCH 08/17] Referenced Feedback --- .../modeling/participate/modeling-submission.component.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/webapp/app/exercises/modeling/participate/modeling-submission.component.ts b/src/main/webapp/app/exercises/modeling/participate/modeling-submission.component.ts index c94cfe598d02..340af07a8b78 100644 --- a/src/main/webapp/app/exercises/modeling/participate/modeling-submission.component.ts +++ b/src/main/webapp/app/exercises/modeling/participate/modeling-submission.component.ts @@ -15,7 +15,7 @@ import { ModelingSubmissionService } from 'app/exercises/modeling/participate/mo import { ModelingEditorComponent } from 'app/exercises/modeling/shared/modeling-editor.component'; import { HeaderParticipationPageComponent } from 'app/exercises/shared/exercise-headers/header-participation-page.component'; import { getExerciseDueDate, hasExerciseDueDatePassed } from 'app/exercises/shared/exercise/exercise.utils'; -import { addParticipationToResult, getAutomaticUnreferencedFeedback } from 'app/exercises/shared/result/result.utils'; +import { addParticipationToResult, getUnreferencedFeedback } from 'app/exercises/shared/result/result.utils'; import { AccountService } from 'app/core/auth/account.service'; import { TeamSubmissionSyncComponent } from 'app/exercises/shared/team-submission-sync/team-submission-sync.component'; import { TeamParticipateInfoBoxComponent } from 'app/exercises/shared/team/team-participate/team-participate-info-box.component'; @@ -696,7 +696,7 @@ export class ModelingSubmissionComponent implements OnInit, OnDestroy, Component get unreferencedFeedback(): Feedback[] | undefined { if (this.assessmentResult?.feedbacks) { checkSubsequentFeedbackInAssessment(this.assessmentResult.feedbacks); - return getAutomaticUnreferencedFeedback(this.assessmentResult.feedbacks); + return getUnreferencedFeedback(this.assessmentResult.feedbacks); } return undefined; } From ab41948362a350d1294a6e8b5620bcb66b42607f Mon Sep 17 00:00:00 2001 From: LeonWehrhahn Date: Fri, 31 Jan 2025 15:53:54 +0100 Subject: [PATCH 09/17] Referenced Feedback --- package-lock.json | 13 ++++++++++--- .../app/exercises/shared/result/result.utils.ts | 10 +++++++--- .../spec/component/utils/result.utils.spec.ts | 9 +++++---- 3 files changed, 22 insertions(+), 10 deletions(-) diff --git a/package-lock.json b/package-lock.json index 044c2ebf8d4f..1a1fd03cd3fe 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7551,6 +7551,12 @@ "@types/node": "*" } }, + "node_modules/@types/prop-types": { + "version": "15.7.14", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.14.tgz", + "integrity": "sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ==", + "license": "MIT" + }, "node_modules/@types/qs": { "version": "6.9.18", "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.18.tgz", @@ -7568,11 +7574,12 @@ "peer": true }, "node_modules/@types/react": { - "version": "19.0.8", - "resolved": "https://registry.npmjs.org/@types/react/-/react-19.0.8.tgz", - "integrity": "sha512-9P/o1IGdfmQxrujGbIMDyYaaCykhLKc0NGCtYcECNUr9UAaDe4gwvV9bR6tvd5Br1SG0j+PBpbKr2UYY8CwqSw==", + "version": "18.3.18", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.18.tgz", + "integrity": "sha512-t4yC+vtgnkYjNSKlFx1jkAhH8LgTo2N/7Qvi83kdEaUtMDiwpbLAktKDaAMlRcJ5eSxZkH74eEGt1ky31d7kfQ==", "license": "MIT", "dependencies": { + "@types/prop-types": "*", "csstype": "^3.0.2" } }, diff --git a/src/main/webapp/app/exercises/shared/result/result.utils.ts b/src/main/webapp/app/exercises/shared/result/result.utils.ts index 85b30f694334..4ea2f86dfae7 100644 --- a/src/main/webapp/app/exercises/shared/result/result.utils.ts +++ b/src/main/webapp/app/exercises/shared/result/result.utils.ts @@ -125,12 +125,16 @@ export const getManualUnreferencedFeedback = (feedbacks: Feedback[] | undefined) }; /** - * searches for all automatic unreferenced feedback in an array of feedbacks of a result + * searches for all unreferenced feedback in an array of feedbacks of a result * @param feedbacks the feedback of a result * @returns an array with the unreferenced feedback of the result */ -export const getAutomaticUnreferencedFeedback = (feedbacks: Feedback[] | undefined): Feedback[] | undefined => { - return feedbacks ? feedbacks.filter((feedbackElement) => !feedbackElement.reference && feedbackElement.type === FeedbackType.AUTOMATIC) : undefined; +export const getUnreferencedFeedback = (feedbacks: Feedback[] | undefined): Feedback[] | undefined => { + return feedbacks + ? feedbacks.filter( + (feedbackElement) => !feedbackElement.reference && (feedbackElement.type === FeedbackType.MANUAL_UNREFERENCED || feedbackElement.type === FeedbackType.AUTOMATIC), + ) + : undefined; }; export function isAIResultAndFailed(result: Result | undefined): boolean { diff --git a/src/test/javascript/spec/component/utils/result.utils.spec.ts b/src/test/javascript/spec/component/utils/result.utils.spec.ts index 9b4463b3c353..a7d50078c51a 100644 --- a/src/test/javascript/spec/component/utils/result.utils.spec.ts +++ b/src/test/javascript/spec/component/utils/result.utils.spec.ts @@ -1,10 +1,10 @@ import { ResultTemplateStatus, breakCircularResultBackReferences, - getAutomaticUnreferencedFeedback, getManualUnreferencedFeedback, getResultIconClass, getTextColorClass, + getUnreferencedFeedback, isOnlyCompilationTested, } from 'app/exercises/shared/result/result.utils'; import { Feedback, FeedbackType, STATIC_CODE_ANALYSIS_FEEDBACK_IDENTIFIER } from 'app/entities/feedback.model'; @@ -31,16 +31,17 @@ describe('ResultUtils', () => { expect(unreferencedFeedbacks).toEqual([{ type: FeedbackType.MANUAL_UNREFERENCED }]); }); - it('should filter out all non unreferenced feedbacks that do not have type AUTOMATIC', () => { + it('should filter out all non unreferenced feedbacks', () => { const feedbacks = [ { reference: 'foo' }, { reference: 'foo', type: FeedbackType.AUTOMATIC }, { type: FeedbackType.AUTOMATIC }, { type: FeedbackType.MANUAL_UNREFERENCED }, + { reference: 'foo', type: FeedbackType.AUTOMATIC_ADAPTED }, {}, ]; - const unreferencedFeedbacks = getAutomaticUnreferencedFeedback(feedbacks); - expect(unreferencedFeedbacks).toEqual([{ type: FeedbackType.AUTOMATIC }]); + const unreferencedFeedbacks = getUnreferencedFeedback(feedbacks); + expect(unreferencedFeedbacks).toEqual([{ type: FeedbackType.AUTOMATIC }, { type: FeedbackType.MANUAL_UNREFERENCED }]); }); it.each([ From d5038e21e06bdd0eb5e851b214c41a3307fddfb1 Mon Sep 17 00:00:00 2001 From: LeonWehrhahn Date: Fri, 31 Jan 2025 16:48:11 +0100 Subject: [PATCH 10/17] Fix modeling submission history timeline ordering --- .../participate/modeling-submission.component.html | 2 +- .../participate/modeling-submission.component.ts | 13 ++++++------- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/src/main/webapp/app/exercises/modeling/participate/modeling-submission.component.html b/src/main/webapp/app/exercises/modeling/participate/modeling-submission.component.html index d022ed99dc96..0071da821676 100644 --- a/src/main/webapp/app/exercises/modeling/participate/modeling-submission.component.html +++ b/src/main/webapp/app/exercises/modeling/participate/modeling-submission.component.html @@ -196,7 +196,7 @@

/> }
- @if (result && !isFeedbackView) { + @if (result) {
diff --git a/src/main/webapp/app/exercises/modeling/participate/modeling-submission.component.ts b/src/main/webapp/app/exercises/modeling/participate/modeling-submission.component.ts index 340af07a8b78..74ec20a21ec5 100644 --- a/src/main/webapp/app/exercises/modeling/participate/modeling-submission.component.ts +++ b/src/main/webapp/app/exercises/modeling/participate/modeling-submission.component.ts @@ -239,15 +239,14 @@ export class ModelingSubmissionComponent implements OnInit, OnDestroy, Component }), tap((submissions: ModelingSubmission[]) => { this.sortedSubmissionHistory = submissions.sort((a, b) => { - // Get the latest result for each submission (sorted by completionDate descending) const latestResultA = this.sortResultsByCompletionDate(a.results ?? [])[0]; const latestResultB = this.sortResultsByCompletionDate(b.results ?? [])[0]; // Use the latest result's completionDate for comparison - const dateA = latestResultA?.completionDate ? latestResultA.completionDate.valueOf() : 0; - const dateB = latestResultB?.completionDate ? latestResultB.completionDate.valueOf() : 0; + const dateA = latestResultA?.completionDate ? dayjs(latestResultA.completionDate).valueOf() : 0; + const dateB = latestResultB?.completionDate ? dayjs(latestResultB.completionDate).valueOf() : 0; - return dateB - dateA; // Sort submissions by latest result's completionDate in descending order + return dateA - dateB; }); this.sortedResultHistory = this.sortedSubmissionHistory.map((submission) => { const result = getLatestSubmissionResult(submission)!; @@ -260,9 +259,9 @@ export class ModelingSubmissionComponent implements OnInit, OnDestroy, Component private sortResultsByCompletionDate(results: Result[]): Result[] { return results.sort((a, b) => { - const dateA = a.completionDate ? a.completionDate.valueOf() : 0; - const dateB = b.completionDate ? b.completionDate.valueOf() : 0; - return dateB - dateA; // Descending + const dateA = a.completionDate ? dayjs(a.completionDate).valueOf() : 0; + const dateB = b.completionDate ? dayjs(b.completionDate).valueOf() : 0; + return dateB - dateA; }); } From 407e8d8479e0cb764d2fb3926d36a8a88ff7ef87 Mon Sep 17 00:00:00 2001 From: LeonWehrhahn Date: Fri, 31 Jan 2025 17:31:37 +0100 Subject: [PATCH 11/17] Fix modeling submission history timeline ordering --- .../modeling-submission.component.ts | 27 +++++++++++++++---- .../modeling-submission.component.spec.ts | 4 +-- 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/src/main/webapp/app/exercises/modeling/participate/modeling-submission.component.ts b/src/main/webapp/app/exercises/modeling/participate/modeling-submission.component.ts index 74ec20a21ec5..f8c87d606e98 100644 --- a/src/main/webapp/app/exercises/modeling/participate/modeling-submission.component.ts +++ b/src/main/webapp/app/exercises/modeling/participate/modeling-submission.component.ts @@ -248,11 +248,28 @@ export class ModelingSubmissionComponent implements OnInit, OnDestroy, Component return dateA - dateB; }); - this.sortedResultHistory = this.sortedSubmissionHistory.map((submission) => { - const result = getLatestSubmissionResult(submission)!; - result.participation = submission.participation; - return result; - }); + this.sortedResultHistory = this.sortedSubmissionHistory + .map((submission) => { + let latestResult: Result | undefined; // Initialize latestResult + + if (submission?.results && submission.results.length > 0) { + // Sort results inline to find the latest one + const sortedResults = [...submission.results].sort((a, b) => { + const dateA = a.completionDate ? dayjs(a.completionDate).valueOf() : 0; + const dateB = b.completionDate ? dayjs(b.completionDate).valueOf() : 0; + return dateB - dateA; // Descending order (latest date first) + }); + latestResult = sortedResults[0]; // Get the first element after sorting + } else { + latestResult = undefined; // Handle cases with no results + } + + if (latestResult) { + latestResult.participation = submission.participation; // Attach participation if result exists + } + return latestResult; // Return the latest result (or undefined if no results) + }) + .filter((result): result is Result => !!result); }), ); } diff --git a/src/test/javascript/spec/component/modeling-submission/modeling-submission.component.spec.ts b/src/test/javascript/spec/component/modeling-submission/modeling-submission.component.spec.ts index b665f96043b6..bc12506c3d36 100644 --- a/src/test/javascript/spec/component/modeling-submission/modeling-submission.component.spec.ts +++ b/src/test/javascript/spec/component/modeling-submission/modeling-submission.component.spec.ts @@ -811,8 +811,8 @@ describe('ModelingSubmissionComponent', () => { createSubmission(2, [results[5]]), // Latest is date5 (Fri 11:20 AM) ]; - const expectedSortedSubmissions = [submissions[2], submissions[1], submissions[0]]; - const expectedSortedResults = [results[5], results[4], results[1]]; + const expectedSortedSubmissions = [submissions[0], submissions[1], submissions[2]]; + const expectedSortedResults = [results[2], results[3], results[5]]; // Mock the service call const submissionsWithResultsSpy = jest.spyOn(service, 'getSubmissionsWithResultsForParticipation').mockReturnValue(of([submissions[2], submissions[1], submissions[0]])); From bd07a037ea1e2d68b1215e99b89080cefa453f9e Mon Sep 17 00:00:00 2001 From: LeonWehrhahn Date: Fri, 31 Jan 2025 17:57:24 +0100 Subject: [PATCH 12/17] Fix modeling submission history timeline ordering --- .../modeling/participate/modeling-submission.component.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/main/webapp/app/exercises/modeling/participate/modeling-submission.component.ts b/src/main/webapp/app/exercises/modeling/participate/modeling-submission.component.ts index f8c87d606e98..7ea79c944ef2 100644 --- a/src/main/webapp/app/exercises/modeling/participate/modeling-submission.component.ts +++ b/src/main/webapp/app/exercises/modeling/participate/modeling-submission.component.ts @@ -260,8 +260,6 @@ export class ModelingSubmissionComponent implements OnInit, OnDestroy, Component return dateB - dateA; // Descending order (latest date first) }); latestResult = sortedResults[0]; // Get the first element after sorting - } else { - latestResult = undefined; // Handle cases with no results } if (latestResult) { From e3f9739c10f30a458e934f27f75e583cc501372d Mon Sep 17 00:00:00 2001 From: LeonWehrhahn Date: Sat, 1 Feb 2025 17:43:40 +0100 Subject: [PATCH 13/17] Prevent initial notification trigger on page load for new assessments --- .../modeling/participate/modeling-submission.component.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/webapp/app/exercises/modeling/participate/modeling-submission.component.ts b/src/main/webapp/app/exercises/modeling/participate/modeling-submission.component.ts index 7ea79c944ef2..70753409bd74 100644 --- a/src/main/webapp/app/exercises/modeling/participate/modeling-submission.component.ts +++ b/src/main/webapp/app/exercises/modeling/participate/modeling-submission.component.ts @@ -448,6 +448,7 @@ export class ModelingSubmissionComponent implements OnInit, OnDestroy, Component // Handle initial results (no skip) this.manualResultUpdateListener = resultStream$ .pipe( + skip(1), filter((result): result is Result => !!result), filter((result) => !result.assessmentType || result.assessmentType !== AssessmentType.AUTOMATIC_ATHENA), ) @@ -471,7 +472,6 @@ export class ModelingSubmissionComponent implements OnInit, OnDestroy, Component if (!result.completionDate) { return; } - this.assessmentResult = this.modelingAssessmentService.convertResult(result); this.prepareAssessmentData(); this.alertService.info('artemisApp.modelingEditor.newAssessment'); From c56fb295e26c11ed9813137668e82dd8e9a96bbd Mon Sep 17 00:00:00 2001 From: LeonWehrhahn Date: Sat, 1 Feb 2025 17:53:28 +0100 Subject: [PATCH 14/17] Display Athena assessments on page load before due date --- .../modeling/participate/modeling-submission.component.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/webapp/app/exercises/modeling/participate/modeling-submission.component.ts b/src/main/webapp/app/exercises/modeling/participate/modeling-submission.component.ts index 70753409bd74..70f125e97915 100644 --- a/src/main/webapp/app/exercises/modeling/participate/modeling-submission.component.ts +++ b/src/main/webapp/app/exercises/modeling/participate/modeling-submission.component.ts @@ -423,7 +423,8 @@ export class ModelingSubmissionComponent implements OnInit, OnDestroy, Component this.umlModel = JSON.parse(this.submission.model); this.hasElements = this.umlModel.elements && Object.values(this.umlModel.elements).length !== 0; } - if (getLatestSubmissionResult(this.submission) && getLatestSubmissionResult(this.submission)!.completionDate && this.isAfterAssessmentDueDate) { + const latestResult = getLatestSubmissionResult(this.submission); + if (latestResult && latestResult.completionDate && (this.isAfterAssessmentDueDate || latestResult.assessmentType === AssessmentType.AUTOMATIC_ATHENA)) { this.modelingAssessmentService.getAssessment(this.submission.id!).subscribe((assessmentResult: Result) => { this.assessmentResult = assessmentResult; this.prepareAssessmentData(); From 53ebc9f88c20bce8966678637009e1b31dea0fb0 Mon Sep 17 00:00:00 2001 From: LeonWehrhahn Date: Sat, 1 Feb 2025 18:23:24 +0100 Subject: [PATCH 15/17] Update unreferenced feedback location --- .../modeling-submission.component.html | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/src/main/webapp/app/exercises/modeling/participate/modeling-submission.component.html b/src/main/webapp/app/exercises/modeling/participate/modeling-submission.component.html index 0071da821676..f134e9700c47 100644 --- a/src/main/webapp/app/exercises/modeling/participate/modeling-submission.component.html +++ b/src/main/webapp/app/exercises/modeling/participate/modeling-submission.component.html @@ -186,16 +186,18 @@

-
- @if (unreferencedFeedback && unreferencedFeedback!.length > 0) { - - } -
+ @if ((submission?.submitted && (!isActive || result)) || isFeedbackView) { +
+ @if (unreferencedFeedback && unreferencedFeedback!.length > 0) { + + } +
+ } @if (result) {
From 5f6c6bf3696c06cdbbf845687c8e73dafd1a681e Mon Sep 17 00:00:00 2001 From: LeonWehrhahn Date: Sat, 1 Feb 2025 18:30:23 +0100 Subject: [PATCH 16/17] Update modeling assessment editor --- .../modeling/participate/modeling-submission.component.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/webapp/app/exercises/modeling/participate/modeling-submission.component.scss b/src/main/webapp/app/exercises/modeling/participate/modeling-submission.component.scss index 3aab262e16ce..d9766640dc3d 100644 --- a/src/main/webapp/app/exercises/modeling/participate/modeling-submission.component.scss +++ b/src/main/webapp/app/exercises/modeling/participate/modeling-submission.component.scss @@ -16,7 +16,7 @@ jhi-modeling-assessment { .editor-assessment { max-width: 70vw; - max-height: 100vh; + height: 100vh; } .rg-bottom { From d55c6bf16b93b22b858e5a2c5cc23180248771b8 Mon Sep 17 00:00:00 2001 From: LeonWehrhahn Date: Sat, 1 Feb 2025 18:37:19 +0100 Subject: [PATCH 17/17] Update, manualResultUpdateListener --- .../modeling/participate/modeling-submission.component.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/webapp/app/exercises/modeling/participate/modeling-submission.component.ts b/src/main/webapp/app/exercises/modeling/participate/modeling-submission.component.ts index 70f125e97915..ea5fd63188ba 100644 --- a/src/main/webapp/app/exercises/modeling/participate/modeling-submission.component.ts +++ b/src/main/webapp/app/exercises/modeling/participate/modeling-submission.component.ts @@ -449,7 +449,6 @@ export class ModelingSubmissionComponent implements OnInit, OnDestroy, Component // Handle initial results (no skip) this.manualResultUpdateListener = resultStream$ .pipe( - skip(1), filter((result): result is Result => !!result), filter((result) => !result.assessmentType || result.assessmentType !== AssessmentType.AUTOMATIC_ATHENA), )