Skip to content

Programming exercises: Improve integration of Monaco code editor and Athena Preliminary Feedback #10442

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 61 commits into
base: feature/programming-exercises/choose-preliminary-feedback-model
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
61 commits
Select commit Hold shift + click to select a range
539ab1d
improve integration of monaco code editor and preliminary feedback
dmytropolityka Nov 14, 2024
9452111
Merge branch 'feature/programming-exercises/choose-preliminary-feedba…
dmytropolityka Jan 5, 2025
6fa0d09
Merge branch 'feature/programming-exercises/choose-preliminary-feedba…
dmytropolityka Jan 5, 2025
3436aad
move reload feedback button
dmytropolityka Jan 12, 2025
24b5437
styling issues
dmytropolityka Jan 13, 2025
e764299
fix test
dmytropolityka Jan 13, 2025
d4e2569
add new tests
dmytropolityka Jan 16, 2025
0fca59f
eslint
dmytropolityka Jan 16, 2025
4122b29
eslint
dmytropolityka Jan 16, 2025
ef723dd
change component.item to partial
dmytropolityka Jan 16, 2025
61d4b38
change component.item to unknown
dmytropolityka Jan 16, 2025
34ccdd6
Merge remote-tracking branch 'origin/develop' into feature/programmin…
dmytropolityka Mar 3, 2025
27ba875
resolve merge conflict issues
dmytropolityka Mar 4, 2025
1c06eea
Merge branch 'feature/programming-exercises/choose-preliminary-feedba…
dmytropolityka Mar 4, 2025
e770a34
fix deployment issue
dmytropolityka Mar 4, 2025
b1d0035
Merge branch 'feature/programming-exercises/choose-preliminary-feedba…
dmytropolityka Mar 5, 2025
6fdf2b4
Merge branch 'feature/programming-exercises/choose-preliminary-feedba…
dmytropolityka Mar 5, 2025
e1ef392
Merge branch 'feature/programming-exercises/choose-preliminary-feedba…
dmytropolityka Mar 5, 2025
017c4f2
fix styling in tests
dmytropolityka Mar 5, 2025
5e42e1d
fix styling in tests
dmytropolityka Mar 5, 2025
1d25704
Update exercise.json
dmytropolityka Mar 5, 2025
e25d68f
increase test coverage and minor changes
dmytropolityka Mar 5, 2025
8c85f25
enable support for multiple existing feedback widgets at the same lin…
dmytropolityka Mar 7, 2025
a22cc0b
remove private method mock
dmytropolityka Mar 7, 2025
e2a8bd1
always set file badges to zero by default
dmytropolityka Mar 7, 2025
6fa70c9
remove console log
dmytropolityka Mar 7, 2025
c92380a
fix rendering of feedback suggestions
dmytropolityka Mar 8, 2025
ed2ebbd
reiterate the approach for multiple feedback boxes at the same line
dmytropolityka Mar 8, 2025
ed5096a
refactoring
dmytropolityka Mar 8, 2025
0345224
prettier
dmytropolityka Mar 8, 2025
fe7f759
Merge branch 'feature/programming-exercises/choose-preliminary-feedba…
dmytropolityka Mar 8, 2025
4200770
eslint
dmytropolityka Mar 8, 2025
6b67c04
Merge remote-tracking branch 'origin/feature/programming-exercises/at…
dmytropolityka Mar 8, 2025
9c6b334
fix query parameter name
dmytropolityka Mar 8, 2025
b4b20b5
don't show reopen feedback component anywhere except the student cont…
dmytropolityka Mar 9, 2025
88b591d
remove duplicating icon
dmytropolityka Mar 11, 2025
de0e836
self closing tag
dmytropolityka Mar 11, 2025
c9d010e
Merge branch 'feature/programming-exercises/choose-preliminary-feedba…
dmytropolityka Mar 13, 2025
91dec44
Merge branch 'feature/programming-exercises/choose-preliminary-feedba…
dmytropolityka Mar 19, 2025
039b05b
merge develop branch
dmytropolityka Mar 19, 2025
d9521ac
Merge remote-tracking branch 'origin/feature/programming-exercises/at…
dmytropolityka Mar 19, 2025
5bb324b
Merge branch 'feature/programming-exercises/choose-preliminary-feedba…
dmytropolityka Mar 19, 2025
74c1eff
Merge branch 'feature/programming-exercises/choose-preliminary-feedba…
dmytropolityka Mar 19, 2025
032b803
fix imports
dmytropolityka Mar 19, 2025
6bfd06d
Merge remote-tracking branch 'origin/feature/programming-exercises/at…
dmytropolityka Mar 19, 2025
8387065
prettier
dmytropolityka Mar 19, 2025
d25b85d
Merge branch 'feature/programming-exercises/choose-preliminary-feedba…
dmytropolityka Mar 23, 2025
42918b6
merge conflict
dmytropolityka Mar 23, 2025
1c62a9d
Merge branch 'feature/programming-exercises/choose-preliminary-feedba…
dmytropolityka Mar 24, 2025
f3e68b6
Merge base branch
dmytropolityka Apr 4, 2025
0c4d0ec
Merge remote-tracking branch 'origin/feature/programming-exercises/at…
dmytropolityka Apr 4, 2025
756018e
Merge branch 'feature/programming-exercises/choose-preliminary-feedba…
dmytropolityka Apr 4, 2025
e8b5644
fix merge conflict issues
dmytropolityka Apr 4, 2025
aa01e3f
Merge remote-tracking branch 'origin/feature/programming-exercises/at…
dmytropolityka Apr 4, 2025
b1697c6
fix another merge conflict issue
dmytropolityka Apr 4, 2025
65dcd57
Merge branch 'feature/programming-exercises/choose-preliminary-feedba…
dmytropolityka Apr 4, 2025
9f4985d
Merge branch 'feature/programming-exercises/choose-preliminary-feedba…
dmytropolityka Apr 4, 2025
ac6502d
increase test coverage
dmytropolityka Apr 7, 2025
0177640
Merge remote-tracking branch 'origin/feature/programming-exercises/at…
dmytropolityka Apr 7, 2025
84c6a2d
Merge branch 'feature/programming-exercises/athena-feedback-in-code-e…
dmytropolityka Apr 7, 2025
aabdf01
Create pre-commit
dmytropolityka Apr 7, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Empty file modified .husky/pre-commit
100755 → 100644
Empty file.
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@ public class ProgrammingExerciseCodeReviewFeedbackService {

private static final Logger log = LoggerFactory.getLogger(ProgrammingExerciseCodeReviewFeedbackService.class);

public static final String NON_GRADED_FEEDBACK_SUGGESTION = "NonGradedFeedbackSuggestion:";
// feedback.detailText prefix
public static final String PRELIMINARY_FEEDBACK_PREFIX = "NonGradedFeedbackSuggestion:";

private final GroupNotificationService groupNotificationService;

Expand Down Expand Up @@ -124,7 +125,7 @@ public void generateAutomaticNonGradedFeedback(ProgrammingExerciseStudentPartici
automaticResult.setScore(100.0);
automaticResult.setSuccessful(null);
automaticResult.setCompletionDate(ZonedDateTime.now().plusMinutes(5)); // we do not want to show dates without a completion date, but we want the students to know their
// feedback request is in work
// feedback request is in work
automaticResult = this.resultRepository.save(automaticResult);

try {
Expand All @@ -143,17 +144,17 @@ public void generateAutomaticNonGradedFeedback(ProgrammingExerciseStudentPartici
String feedbackText;
if (Objects.nonNull(individualFeedbackItem.lineStart())) {
if (Objects.nonNull(individualFeedbackItem.lineEnd()) && !individualFeedbackItem.lineStart().equals(individualFeedbackItem.lineEnd())) {
feedbackText = String.format(NON_GRADED_FEEDBACK_SUGGESTION + "File %s at lines %d-%d", individualFeedbackItem.filePath(),
individualFeedbackItem.lineStart(), individualFeedbackItem.lineEnd());
feedbackText = String.format(PRELIMINARY_FEEDBACK_PREFIX + "File %s at lines %d-%d", individualFeedbackItem.filePath(),
individualFeedbackItem.lineStart() + 1, individualFeedbackItem.lineEnd() + 1);
}
else {
feedbackText = String.format(NON_GRADED_FEEDBACK_SUGGESTION + "File %s at line %d", individualFeedbackItem.filePath(),
individualFeedbackItem.lineStart());
feedbackText = String.format(PRELIMINARY_FEEDBACK_PREFIX + "File %s at line %d", individualFeedbackItem.filePath(),
individualFeedbackItem.lineStart() + 1);
}
feedback.setReference(String.format("file:%s_line:%d", individualFeedbackItem.filePath(), individualFeedbackItem.lineStart()));
}
else {
feedbackText = String.format(NON_GRADED_FEEDBACK_SUGGESTION + "File %s", individualFeedbackItem.filePath());
feedbackText = String.format(PRELIMINARY_FEEDBACK_PREFIX + "File %s", individualFeedbackItem.filePath());
}
feedback.setText(feedbackText);
feedback.setDetailText(individualFeedbackItem.description());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import {
Feedback,
FeedbackSuggestionType,
FeedbackType,
NON_GRADED_FEEDBACK_SUGGESTION_IDENTIFIER,
PRELIMINARY_FEEDBACK_IDENTIFIER,
STATIC_CODE_ANALYSIS_FEEDBACK_IDENTIFIER,
SUBMISSION_POLICY_FEEDBACK_IDENTIFIER,
buildFeedbackTextForReview,
Expand Down Expand Up @@ -162,8 +162,37 @@ describe('Feedback', () => {
});

it('should correctly detect non graded automatically generated feedback', () => {
const feedback: Feedback = { type: FeedbackType.AUTOMATIC, text: NON_GRADED_FEEDBACK_SUGGESTION_IDENTIFIER + 'content' };
expect(Feedback.isNonGradedFeedbackSuggestion(feedback)).toBeTrue();
const feedback: Feedback = { type: FeedbackType.AUTOMATIC, text: PRELIMINARY_FEEDBACK_IDENTIFIER + 'content' };
expect(Feedback.isPreliminaryFeedback(feedback)).toBeTrue();
});
});

describe('Feedback with lines information', () => {
it('should include lines information in feedback text if text contains "lines"', () => {
const feedback = new Feedback();
feedback.detailText = 'This is a detailed feedback.';
feedback.text = 'NonGradedFeedbackSuggestion:File src/example/file.java at lines 10-20';

const expectedText = 'This is a detailed feedback. (lines 10-20)';
expect(buildFeedbackTextForReview(feedback)).toBe(expectedText);
});

it('should not include lines information if text does not contain "lines"', () => {
const feedback = new Feedback();
feedback.detailText = 'This is a detailed feedback.';
feedback.text = 'NonGradedFeedbackSuggestion:File src/example/file.java';

const expectedText = 'This is a detailed feedback.';
expect(buildFeedbackTextForReview(feedback)).toBe(expectedText);
});

it('should ignore feedback not starting with the expected prefix', () => {
const feedback = new Feedback();
feedback.detailText = 'This is another type of feedback.';
feedback.text = 'Some other feedback text unrelated to lines';

const expectedText = 'This is another type of feedback.';
expect(buildFeedbackTextForReview(feedback)).toBe(expectedText);
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export const SUBMISSION_POLICY_FEEDBACK_IDENTIFIER = 'SubPolFeedbackIdentifier:'
export const FEEDBACK_SUGGESTION_IDENTIFIER = 'FeedbackSuggestion:';
export const FEEDBACK_SUGGESTION_ACCEPTED_IDENTIFIER = 'FeedbackSuggestion:accepted:';
export const FEEDBACK_SUGGESTION_ADAPTED_IDENTIFIER = 'FeedbackSuggestion:adapted:';
export const NON_GRADED_FEEDBACK_SUGGESTION_IDENTIFIER = 'NonGradedFeedbackSuggestion:';
export const PRELIMINARY_FEEDBACK_IDENTIFIER = 'NonGradedFeedbackSuggestion:';

export interface DropInfo {
instruction: GradingInstruction;
Expand Down Expand Up @@ -123,11 +123,11 @@ export class Feedback implements BaseEntity {
return that.text.startsWith(FEEDBACK_SUGGESTION_IDENTIFIER);
}

public static isNonGradedFeedbackSuggestion(that: Feedback): boolean {
public static isPreliminaryFeedback(that: Feedback): boolean {
if (!that.text) {
return false;
}
return that.text.startsWith(NON_GRADED_FEEDBACK_SUGGESTION_IDENTIFIER);
return that.text.startsWith(PRELIMINARY_FEEDBACK_IDENTIFIER);
}

/**
Expand Down Expand Up @@ -297,6 +297,12 @@ export const buildFeedbackTextForReview = (feedback: Feedback, addFeedbackText =
}
} else if (feedback.detailText) {
feedbackText = feedback.detailText;
if (feedback.text && feedback.text.includes('lines')) {
const linesInfo = feedback.text.match(/lines .*/);
if (linesInfo) {
feedbackText += ` (${linesInfo[0]})`;
}
}
} else if (addFeedbackText && feedback.text) {
feedbackText = feedback.text;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import {
FEEDBACK_SUGGESTION_IDENTIFIER,
Feedback,
FeedbackType,
NON_GRADED_FEEDBACK_SUGGESTION_IDENTIFIER,
PRELIMINARY_FEEDBACK_IDENTIFIER,
STATIC_CODE_ANALYSIS_FEEDBACK_IDENTIFIER,
SUBMISSION_POLICY_FEEDBACK_IDENTIFIER,
} from 'app/assessment/shared/entities/feedback.model';
Expand Down Expand Up @@ -50,9 +50,9 @@ export class ProgrammingFeedbackItemService implements FeedbackItemService {
return this.createScaFeedbackItem(feedback, showTestDetails);
} else if (Feedback.isFeedbackSuggestion(feedback)) {
return this.createFeedbackSuggestionItem(feedback, showTestDetails);
} else if (feedback.type === FeedbackType.AUTOMATIC && !Feedback.isNonGradedFeedbackSuggestion(feedback)) {
} else if (feedback.type === FeedbackType.AUTOMATIC && !Feedback.isPreliminaryFeedback(feedback)) {
return this.createAutomaticFeedbackItem(feedback, showTestDetails);
} else if (feedback.type === FeedbackType.AUTOMATIC && Feedback.isNonGradedFeedbackSuggestion(feedback)) {
} else if (feedback.type === FeedbackType.AUTOMATIC && Feedback.isPreliminaryFeedback(feedback)) {
return this.createNonGradedFeedbackItem(feedback);
} else if ((feedback.type === FeedbackType.MANUAL || feedback.type === FeedbackType.MANUAL_UNREFERENCED) && feedback.gradingInstruction) {
return this.createGradingInstructionFeedbackItem(feedback, showTestDetails);
Expand Down Expand Up @@ -161,7 +161,7 @@ export class ProgrammingFeedbackItemService implements FeedbackItemService {
return {
type: 'Reviewer',
name: this.translateService.instant('artemisApp.result.detail.feedback'),
title: feedback.text?.slice(NON_GRADED_FEEDBACK_SUGGESTION_IDENTIFIER.length),
title: feedback.text?.slice(PRELIMINARY_FEEDBACK_IDENTIFIER.length),
text: feedback.detailText,
positive: feedback.positive,
credits: feedback.credits,
Expand Down
6 changes: 3 additions & 3 deletions src/main/webapp/app/exercise/result/result.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import { AssessmentType } from 'app/assessment/shared/entities/assessment-type.m
import { ProgrammingSubmission } from 'app/programming/shared/entities/programming-submission.model';
import {
FeedbackType,
NON_GRADED_FEEDBACK_SUGGESTION_IDENTIFIER,
PRELIMINARY_FEEDBACK_IDENTIFIER,
STATIC_CODE_ANALYSIS_FEEDBACK_IDENTIFIER,
SUBMISSION_POLICY_FEEDBACK_IDENTIFIER,
} from 'app/assessment/shared/entities/feedback.model';
Expand Down Expand Up @@ -90,14 +90,14 @@ describe('ResultService', () => {
score: 80,
};
const result6: Result = {
feedbacks: [{ text: NON_GRADED_FEEDBACK_SUGGESTION_IDENTIFIER + 'AI feedback', type: FeedbackType.AUTOMATIC }],
feedbacks: [{ text: PRELIMINARY_FEEDBACK_IDENTIFIER + 'AI feedback', type: FeedbackType.AUTOMATIC }],
participation: { type: ParticipationType.PROGRAMMING },
completionDate: dayjs().subtract(5, 'minutes'),
assessmentType: AssessmentType.AUTOMATIC_ATHENA,
successful: true,
};
const result7: Result = {
feedbacks: [{ text: NON_GRADED_FEEDBACK_SUGGESTION_IDENTIFIER + 'AI feedback', type: FeedbackType.AUTOMATIC }],
feedbacks: [{ text: PRELIMINARY_FEEDBACK_IDENTIFIER + 'AI feedback', type: FeedbackType.AUTOMATIC }],
participation: { type: ParticipationType.PROGRAMMING },
completionDate: dayjs().subtract(5, 'minutes'),
assessmentType: AssessmentType.AUTOMATIC_ATHENA,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ export class UpdatingResultComponent implements OnChanges, OnDestroy {
@Input() showCompletion = true;
@Input() showProgressBar = false;
@Input() showProgressBarBorder = false;
@Output() showResult = new EventEmitter<void>();
@Output() showResult = new EventEmitter<Result>();
/**
* @property personalParticipation Whether the participation belongs to the user (by being a student) or not (by being an instructor)
*/
Expand Down Expand Up @@ -80,7 +80,7 @@ export class UpdatingResultComponent implements OnChanges, OnDestroy {
}

if (this.result) {
this.showResult.emit();
this.showResult.emit(this.result);
}
}
}
Expand Down Expand Up @@ -125,7 +125,7 @@ export class UpdatingResultComponent implements OnChanges, OnDestroy {
}
this.onParticipationChange.emit();
if (result) {
this.showResult.emit();
this.showResult.emit(this.result);
}
}),
)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
.cross {
width: 20px;
height: 20px;
padding: 2px;
display: flex;
align-items: center;
justify-content: center;
border: 1px solid #000;

span {
font-size: 10px;
line-height: 1;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@
[translateValues]="{ text: this.feedback | feedbackContent | quoted: ' ' }"
deleteQuestion="artemisApp.feedback.delete.question"
type="submit"
(delete)="deleteFeedback()"
(delete)="deleteFeedback(false)"
[dialogError]="dialogError$"
class="btn btn-danger btn-sm me-1"
>
Expand All @@ -90,24 +90,31 @@
}
</div>
} @else {
<div class="p-1">
<div class="p-1 position-relative">
@if (Feedback.isPreliminaryFeedback(feedback)) {
<button
type="button"
class="btn-close cross position-absolute top-0 end-0 m-1 rounded-circle"
aria-label="Close"
(click)="deleteFeedback(true)"
[ngbTooltip]="'entity.action.close' | artemisTranslate"
></button>
}
<div class="row flex-nowrap align-items-top m-1">
<div class="col flex-grow-0 ps-0">
<h5 class="d-inline">
@if (!Feedback.isNonGradedFeedbackSuggestion(feedback)) {
<span
class="badge"
[class.bg-success]="feedback.credits! > 0 && feedback.isSubsequent === undefined"
[class.bg-danger]="feedback.credits! < 0 && feedback.isSubsequent === undefined"
[class.bg-warning]="feedback.credits === 0 && feedback.isSubsequent === undefined"
[class.bg-secondary]="readOnly && feedback.isSubsequent"
>{{ roundScoreSpecifiedByCourseSettings(feedback.credits, course) + 'P' }}</span
>
}
<span
class="badge"
[class.bg-success]="feedback.credits! > 0 && feedback.isSubsequent === undefined"
[class.bg-danger]="feedback.credits! < 0 && feedback.isSubsequent === undefined"
[class.bg-warning]="feedback.credits === 0 && feedback.isSubsequent === undefined"
[class.bg-secondary]="readOnly && feedback.isSubsequent"
>{{ roundScoreSpecifiedByCourseSettings(feedback.credits, course) + 'P' }}</span
>
</h5>
</div>
<div class="col-10 ps-0 flex-grow-1 flex-shrink-1">
@if (Feedback.isNonGradedFeedbackSuggestion(feedback)) {
@if (Feedback.isPreliminaryFeedback(feedback)) {
<h6 class="d-inline mb-1" jhiTranslate="artemisApp.assessment.detail.feedback"></h6>
<p [innerHTML]="buildFeedbackTextForCodeEditor(feedback)" class="mt-1 mb-0"></p>
} @else {
Expand Down
Loading