Skip to content
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

Development: Add client support for programming exercise feedback suggestions #7099

Merged
merged 74 commits into from
Oct 4, 2023
Merged
Show file tree
Hide file tree
Changes from 39 commits
Commits
Show all changes
74 commits
Select commit Hold shift + click to select a range
9e6a7b9
Basic programming feedback suggestion card layout
pal03377 Aug 25, 2023
7b37b58
Better Suggestion badge for feedback suggestion cards
pal03377 Aug 25, 2023
9ea12ef
Add Accept and Discard buttons
pal03377 Aug 26, 2023
cbd820d
Add file browser badges for suggestions
pal03377 Aug 26, 2023
081b104
Fixing issues with saving inline feedback
pal03377 Aug 26, 2023
9f2e4c6
Make the programming feedback assessment editor more solid
pal03377 Aug 27, 2023
86c2fa9
Merge branch 'develop' into feature/programming-exercises/feedback-su…
pal03377 Aug 27, 2023
afd07b2
Add suggestion badge to accepted and modified feedback suggestions
pal03377 Aug 27, 2023
deb5678
Use suggestion badge component for text feedback
pal03377 Aug 27, 2023
5c64eab
Update file badges less often
pal03377 Aug 27, 2023
8b09917
Add file badge tooltips
pal03377 Aug 27, 2023
d0ef16c
Show unreferenced feedback suggestions
pal03377 Aug 27, 2023
2c2899e
Change ID for unreferenced feedback
pal03377 Aug 27, 2023
3a49e76
Improvements from self-code-review ✨
pal03377 Aug 28, 2023
f81f3d3
Write client tests: part 1
pal03377 Aug 28, 2023
944b153
Write client tests: part 2
pal03377 Aug 28, 2023
c5bb462
Text fix by reordering
pal03377 Aug 28, 2023
c4d004b
Fix Codacy issue
pal03377 Aug 28, 2023
16cf83a
Dark file badge in dark mode
pal03377 Aug 28, 2023
2025583
More feedback suggestion examples for debugging
pal03377 Aug 28, 2023
eb8bc8d
Hide feedback suggestion badges to students
pal03377 Aug 28, 2023
0462d0c
Increase test coverage
pal03377 Aug 28, 2023
3dee732
Test expectation fix
pal03377 Aug 28, 2023
5125ea9
Improve suggestion badge texts
pal03377 Aug 28, 2023
299c9e4
Fix line widget update on feedbackS changes
pal03377 Aug 28, 2023
56a0b35
Don't show feedback suggestions that are in the same file & line as e…
pal03377 Aug 28, 2023
8049cd6
Treat suggestions like normal feedback from the TA in the student fee…
pal03377 Aug 28, 2023
423c669
Prevent double-feedback when adding suggestions where there already e…
pal03377 Aug 28, 2023
aeca0cd
Change feedback height on resize
pal03377 Aug 29, 2023
6cb50cf
Change condition for de-duplication
pal03377 Aug 29, 2023
a0cf5d1
Adjust accept/discard button sizes
pal03377 Aug 29, 2023
c55af90
Merge branch 'develop' into feature/programming-exercises/feedback-su…
pal03377 Aug 29, 2023
494f65c
Remove default IDs to prevent problems with testing
pal03377 Aug 30, 2023
9f05d12
Merge branch 'develop' into feature/programming-exercises/feedback-su…
pal03377 Aug 30, 2023
cac3bba
Adjust feedback suggestions background in light more slightly
pal03377 Aug 30, 2023
6400b6a
Merge remote-tracking branch 'origin/feature/programming-exercises/fe…
pal03377 Aug 30, 2023
79e4cd9
Merge branch 'develop' into feature/programming-exercises/feedback-su…
pal03377 Aug 30, 2023
5e98ece
Fix removeSuggestion for feedback without an ID
pal03377 Aug 30, 2023
ab101ef
Fix test should correctly remove feedback suggestions
pal03377 Aug 30, 2023
b286d9f
Merge & resolve athena.service.ts conflicts
pal03377 Aug 30, 2023
d029258
Small fixes
pal03377 Aug 30, 2023
5245472
Bugfix: Make feedback work for file names containing underscore
pal03377 Aug 31, 2023
800605e
Add tests for filenames including an underscore
pal03377 Aug 31, 2023
62e1223
Review: Replace deprecated function
pal03377 Aug 31, 2023
93f90d0
Merge branch 'develop' into feature/programming-exercises/feedback-su…
pal03377 Aug 31, 2023
deb80bd
Fix code-editor-ace.component tests by adding a mock
pal03377 Aug 31, 2023
14af999
Mock ResizeObserver in other place where necessary
pal03377 Aug 31, 2023
4d97e42
Add some more simple tests to increase the coverage
pal03377 Sep 1, 2023
9d0adfd
Fix Athena test mocks
pal03377 Sep 1, 2023
989de14
Review: Implement suggestions by Benedikt
pal03377 Sep 1, 2023
b4533ac
Merge branch 'develop' into feature/programming-exercises/feedback-su…
pal03377 Sep 1, 2023
b27c030
Merge branch 'develop' into feature/programming-exercises/feedback-su…
pal03377 Sep 2, 2023
2461262
Merge branch 'develop' into feature/programming-exercises/feedback-su…
pal03377 Sep 3, 2023
38638d1
Merge + Solve conflicts with #6925
pal03377 Sep 3, 2023
1709f5a
Review: Small formatting change
pal03377 Sep 3, 2023
bfe4f40
Merge branch 'develop' into feature/programming-exercises/feedback-su…
pal03377 Sep 7, 2023
3e74575
Merge branch 'develop' into feature/programming-exercises/feedback-su…
pal03377 Sep 9, 2023
505beb7
Merge branch 'develop' into feature/programming-exercises/feedback-su…
pal03377 Sep 9, 2023
c13d62f
Merge branch 'develop' into feature/programming-exercises/feedback-su…
pal03377 Sep 11, 2023
c3fa067
Merge branch 'develop' into feature/programming-exercises/feedback-su…
pal03377 Sep 12, 2023
2f09ee8
Implement review suggestions by Markus
pal03377 Sep 14, 2023
94f32f3
Merge branch 'develop' into feature/programming-exercises/feedback-su…
pal03377 Sep 16, 2023
39d185a
Merge branch 'develop' into feature/programming-exercises/feedback-su…
MarkusPaulsen Sep 21, 2023
c1c0def
Merge branch 'develop' into feature/programming-exercises/feedback-su…
pal03377 Sep 27, 2023
b4932cc
Remove mock data for merging
pal03377 Sep 28, 2023
8fb21bf
Implement Patrick's suggestions
pal03377 Sep 28, 2023
8d97684
Fix cypress test
pal03377 Sep 28, 2023
ff380d5
Merge branch 'develop' into feature/programming-exercises/feedback-su…
bassner Sep 29, 2023
d476108
Merge branch 'develop' into feature/programming-exercises/feedback-su…
pal03377 Sep 29, 2023
34dcaaa
Merge branch 'develop' into feature/programming-exercises/feedback-su…
pal03377 Sep 30, 2023
27654e1
Merge branch 'develop' into feature/programming-exercises/feedback-su…
pal03377 Oct 1, 2023
0e5f8bb
Merge branch 'develop' into feature/programming-exercises/feedback-su…
maximiliansoelch Oct 2, 2023
09d6b39
Merge branch 'develop' into feature/programming-exercises/feedback-su…
Oct 3, 2023
b45e160
Merge branch 'develop' into feature/programming-exercises/feedback-su…
pal03377 Oct 4, 2023
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

This file was deleted.

This file was deleted.

This file was deleted.

10 changes: 6 additions & 4 deletions src/main/webapp/app/assessment/assessment-shared.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,17 @@ import { AssessmentHeaderComponent } from './assessment-header/assessment-header
import { AssessmentLayoutComponent } from './assessment-layout/assessment-layout.component';
import { AssessmentComplaintAlertComponent } from './assessment-complaint-alert/assessment-complaint-alert.component';
import { ScoreDisplayComponent } from '../shared/score-display/score-display.component';
import { AssessmentDetailComponent } from './assessment-detail/assessment-detail.component';
import { UnreferencedFeedbackDetailComponent } from 'app/assessment/unreferenced-feedback-detail/unreferenced-feedback-detail.component';
import { ArtemisSharedComponentModule } from 'app/shared/components/shared-component.module';
import { ArtemisComplaintsForTutorModule } from 'app/complaints/complaints-for-tutor/complaints-for-tutor.module';
import { AssessmentLocksComponent } from 'app/assessment/assessment-locks/assessment-locks.component';
import { RouterModule } from '@angular/router';
import { assessmentLocksRoute } from 'app/assessment/assessment-locks/assessment-locks.route';
import { UnreferencedFeedbackComponent } from 'app/exercises/shared/unreferenced-feedback/unreferenced-feedback.component';
import { ArtemisMarkdownModule } from 'app/shared/markdown.module';
import { AssessmentCorrectionRoundBadgeComponent } from 'app/assessment/assessment-detail/assessment-correction-round-badge/assessment-correction-round-badge.component';
import { AssessmentCorrectionRoundBadgeComponent } from 'app/assessment/unreferenced-feedback-detail/assessment-correction-round-badge/assessment-correction-round-badge.component';
import { ArtemisGradingInstructionLinkIconModule } from 'app/shared/grading-instruction-link-icon/grading-instruction-link-icon.module';
import { ArtemisFeedbackModule } from 'app/exercises/shared/feedback/feedback.module';

const ENTITY_STATES = [...assessmentLocksRoute];

Expand All @@ -25,21 +26,22 @@ const ENTITY_STATES = [...assessmentLocksRoute];
RouterModule.forChild(ENTITY_STATES),
ArtemisMarkdownModule,
ArtemisGradingInstructionLinkIconModule,
ArtemisFeedbackModule,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note for code reviewers: This is needed to get the new feedback-suggestion-badge component.

],
declarations: [
AssessmentHeaderComponent,
AssessmentLayoutComponent,
AssessmentComplaintAlertComponent,
ScoreDisplayComponent,
AssessmentDetailComponent,
UnreferencedFeedbackDetailComponent,
AssessmentLocksComponent,
UnreferencedFeedbackComponent,
AssessmentCorrectionRoundBadgeComponent,
],
exports: [
AssessmentLayoutComponent,
ScoreDisplayComponent,
AssessmentDetailComponent,
UnreferencedFeedbackDetailComponent,
AssessmentLocksComponent,
UnreferencedFeedbackComponent,
AssessmentCorrectionRoundBadgeComponent,
Expand Down
83 changes: 83 additions & 0 deletions src/main/webapp/app/assessment/athena.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import { Injectable } from '@angular/core';
import { HttpClient, HttpResponse } from '@angular/common/http';
import { Observable, of, switchMap } from 'rxjs';
import { ProfileService } from 'app/shared/layouts/profiles/profile.service';
import { Feedback, FeedbackType } from 'app/entities/feedback.model';

@Injectable({ providedIn: 'root' })
export class AthenaService {
public resourceUrl = 'api/athena';

constructor(
protected http: HttpClient,
private profileService: ProfileService,
) {}

/**
* Get feedback suggestions for the given submission from Athena
*
* @param exerciseId the id of the exercise
* @param submissionId the id of the submission
*/
getFeedbackSuggestions(exerciseId: number, submissionId: number): Observable<Feedback[]> {
// For debugging: Return basic feedback suggestions for BubbleSort.java
const referencedFeedbackSuggestion1 = new Feedback();
referencedFeedbackSuggestion1.credits = 1;
referencedFeedbackSuggestion1.text = 'FeedbackSuggestion:';
referencedFeedbackSuggestion1.detailText = 'This is a referenced feedback suggestion - test test';
referencedFeedbackSuggestion1.gradingInstruction = undefined;
referencedFeedbackSuggestion1.reference = 'file:src/de/athena/BubbleSort.java_line:9';
referencedFeedbackSuggestion1.type = FeedbackType.AUTOMATIC;
const referencedFeedbackSuggestion2 = new Feedback();
referencedFeedbackSuggestion2.credits = -1;
referencedFeedbackSuggestion2.text = 'FeedbackSuggestion:';
referencedFeedbackSuggestion2.detailText = 'Look at that TODO....';
referencedFeedbackSuggestion2.gradingInstruction = undefined;
referencedFeedbackSuggestion2.reference = 'file:src/de/athena/BubbleSort.java_line:13';
referencedFeedbackSuggestion2.type = FeedbackType.AUTOMATIC;
const referencedFeedbackSuggestion3 = new Feedback();
referencedFeedbackSuggestion3.credits = -4;
referencedFeedbackSuggestion3.text = 'FeedbackSuggestion:';
referencedFeedbackSuggestion3.detailText = 'You did not implement it correctly';
referencedFeedbackSuggestion3.gradingInstruction = undefined;
referencedFeedbackSuggestion3.reference = 'file:src/de/athena/MergeSort.java_line:13';
referencedFeedbackSuggestion3.type = FeedbackType.AUTOMATIC;
const unreferencedFeedbackSuggestion = new Feedback();
unreferencedFeedbackSuggestion.credits = -2;
unreferencedFeedbackSuggestion.text = 'FeedbackSuggestion:';
unreferencedFeedbackSuggestion.detailText = 'You did not implement it correctly';
unreferencedFeedbackSuggestion.gradingInstruction = undefined;
unreferencedFeedbackSuggestion.reference = undefined;
unreferencedFeedbackSuggestion.type = FeedbackType.AUTOMATIC;
return of([referencedFeedbackSuggestion1, referencedFeedbackSuggestion2, referencedFeedbackSuggestion3, unreferencedFeedbackSuggestion]);

return this.profileService.getProfileInfo().pipe(
switchMap((profileInfo) => {
if (!profileInfo.activeProfiles.includes('athena')) {
return of([] as Feedback[]);
}
return this.http
.get<Feedback[]>(`${this.resourceUrl}/exercises/${exerciseId}/submissions/${submissionId}/feedback-suggestions`, { observe: 'response' })
.pipe(switchMap((res: HttpResponse<Feedback[]>) => of(res.body!)))
.pipe(
switchMap((feedbackSuggestions) => {
// Make real Feedback objects out of the plain objects
return of(
feedbackSuggestions.map((feedbackSuggestion) => {
const suggestion = new Feedback();
suggestion.id = feedbackSuggestion.id;
suggestion.credits = feedbackSuggestion.credits;
suggestion.text = feedbackSuggestion.text;
suggestion.detailText = feedbackSuggestion.detailText;
suggestion.gradingInstruction = feedbackSuggestion.gradingInstruction;
suggestion.reference = feedbackSuggestion.reference;
suggestion.type = feedbackSuggestion.type;
return suggestion;
}),
);
}),
);
}),
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
<div (drop)="updateFeedbackOnDrop($event)" (dragover)="$event.preventDefault()" class="card mb-3" [class.is-suggestion]="isSuggestion" id="unreferenced-feedback-detail">
pal03377 marked this conversation as resolved.
Show resolved Hide resolved
<div class="card-header">
<jhi-feedback-suggestion-badge *ngIf="isSuggestion || Feedback.isFeedbackSuggestion(feedback)" [feedback]="feedback"></jhi-feedback-suggestion-badge>
<jhi-grading-instruction-link-icon *ngIf="feedback.gradingInstruction" [feedback]="feedback"></jhi-grading-instruction-link-icon>
<fa-icon [icon]="faTrashAlt" class="float-end" (click)="delete()" *ngIf="!readOnly"></fa-icon>
<!-- Accept/Discard for feedback suggestions -->
<div class="row float-end suggestion-action-buttons" *ngIf="isSuggestion">
<button class="btn btn-success m-1 btn-sm" (click)="onAcceptSuggestion.emit(feedback)">
<fa-icon [icon]="faCheck"></fa-icon>
<span jhiTranslate="artemisApp.assessment.detail.accept">Accept</span>
</button>
<button class="btn btn-danger m-1 btn-sm" (click)="onDiscardSuggestion.emit(feedback)">
<fa-icon [icon]="faTrash"></fa-icon>
<span jhiTranslate="artemisApp.assessment.detail.discard">Discard</span>
</button>
</div>
</div>
<div class="card-body">
<div class="form-group row">
<label for="feedback-points" class="col-4 feedback-label" jhiTranslate="artemisApp.exercise.score"></label>
<input
id="feedback-points"
class="col form-control"
type="number"
step="0.5"
[(ngModel)]="feedback.credits"
(ngModelChange)="emitChanges()"
[readOnly]="feedback.gradingInstruction || readOnly"
[disabled]="readOnly"
[required]="!feedback.gradingInstruction"
/>
</div>
<div class="form-group row">
<div class="col-4 assessment-label">
<label class="pe-0" style="float: left" jhiTranslate="artemisApp.assessment.detail.feedback"></label>
pal03377 marked this conversation as resolved.
Show resolved Hide resolved
<div *ngIf="feedback.gradingInstruction">
<fa-icon [icon]="faQuestionCircle" class="text-secondary ps-1" [ngbTooltip]="'artemisApp.assessment.feedbackHint' | artemisTranslate"></fa-icon>
</div>
</div>
<div class="col p-0">
<div *ngIf="feedback.gradingInstruction">
<span>{{ feedback.gradingInstruction!.feedback }}</span>
</div>
<textarea
id="feedback-textarea"
class="form-control"
rows="2"
[(ngModel)]="feedback.detailText"
(ngModelChange)="emitChanges()"
[readOnly]="readOnly"
[disabled]="readOnly"
[placeholder]="
feedback.gradingInstruction?.feedback
? ('artemisApp.assessment.additionalFeedbackCommentPlaceholder' | artemisTranslate)
: ('artemisApp.assessment.feedbackCommentPlaceholder' | artemisTranslate)
"
[required]="!feedback.gradingInstruction?.feedback"
></textarea>
</div>
</div>
<!-- Text showing whether the tutor feedback is correct or not (upon validation on the server) -->
<div *ngIf="feedback.correctionStatus !== undefined">
<span *ngIf="feedback.correctionStatus === 'CORRECT'" class="text-success"
>{{ 'artemisApp.exampleSubmission.feedback.' + feedback.correctionStatus! | artemisTranslate }}
</span>
<span *ngIf="feedback.correctionStatus !== 'CORRECT'" class="text-danger"
>{{ 'artemisApp.exampleSubmission.feedback.' + feedback.correctionStatus! | artemisTranslate }}
</span>

<!-- :warning: emoji was rendered as a black-white glyph, hence the solution with the fa-icon -->
<fa-layers *ngIf="feedback.correctionStatus !== 'CORRECT'">
<fa-icon class="text-warning" [icon]="faExclamationTriangle"></fa-icon>
<fa-icon [icon]="faExclamation" size="2x" [styles]="{ width: '16px', 'margin-top': '-6px' }" [classes]="['text-dark']" transform="shrink-10"></fa-icon>
pal03377 marked this conversation as resolved.
Show resolved Hide resolved
</fa-layers>
</div>

<jhi-assessment-correction-round-badge [feedback]="feedback" [highlightDifferences]="highlightDifferences"></jhi-assessment-correction-round-badge>
</div>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
.card-body {
padding-top: 5px;
padding-bottom: 5px;
}

.is-suggestion .card-header,
.is-suggestion .card-body {
background-color: var(--feedback-suggestions-background);
}

.is-suggestion input,
.is-suggestion textarea {
background-color: var(--feedback-suggestions-input-background);
}

/* Slight opacity for suggestions until they are hovered */
.is-suggestion .card-body > * {
opacity: 0.6; /* grey out the suggestion content to indicate that it is not saved yet */
transition: opacity 0.2s ease-in-out;
}

.is-suggestion:hover .card-body > * {
opacity: 1; /* make the suggestion more readable on hover */
}

.suggestion-action-buttons button {
width: fit-content;
}

.assessment-label {
padding-left: 5px;
padding-right: 5px;
}
Loading