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

Text-exercises: Highlight selected result in result history timeline #9845

Open
wants to merge 14 commits into
base: develop
Choose a base branch
from
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<div [id]="'exercise-' + textExercise?.id">
@if (displayHeader) {
@if (displayHeader()) {
<jhi-header-participation-page class="d-block mb-4" [exercise]="textExercise" [participation]="participation">
<span pagetitle>
{{ 'artemisApp.textSubmission.textEditor' | artemisTranslate }}:
Expand Down Expand Up @@ -54,7 +54,7 @@
@if (isReadOnlyWithShowResult) {
@if (showHistory) {
<div id="result-history" class="row mb-2">
<jhi-result-history [results]="sortedHistoryResults" [exercise]="textExercise" />
<jhi-result-history [results]="sortedHistoryResults" [exercise]="textExercise" [selectedResultId]="submission.latestResult?.id!" />
</div>
}
}
Expand All @@ -64,7 +64,7 @@
@if (textExercise) {
<jhi-resizeable-container
[isExerciseParticipation]="true"
[expandProblemStatement]="expandProblemStatement && !isReadOnlyWithShowResult"
[expandProblemStatement]="expandProblemStatement() && !isReadOnlyWithShowResult"
[collapsed]="isReadOnlyWithShowResult"
>
<!--region Left Panel-->
Expand All @@ -79,15 +79,15 @@
<span
class="badge bg-primary mb-2"
id="word-count"
[hidden]="submission && !submission.submitted && isExamSummary"
[hidden]="submission && !submission.submitted && isExamSummary()"
jhiTranslate="artemisApp.textExercise.wordCount"
[translateValues]="{ count: wordCount }"
>
</span>
<span
class="badge bg-primary mb-2"
id="character-count"
[hidden]="submission && !submission.submitted && isExamSummary"
[hidden]="submission && !submission.submitted && isExamSummary()"
jhiTranslate="artemisApp.textExercise.characterCount"
[translateValues]="{ count: characterCount }"
>
Expand All @@ -104,7 +104,7 @@
[disabled]="!isActive || !submission || !isOwnerOfParticipation"
(keydown.tab)="onTextEditorTab(textEditor, $event)"
(input)="onTextEditorInput($event)"
[hidden]="submission && !submission.submitted && isExamSummary"
[hidden]="submission && !submission.submitted && isExamSummary()"
></textarea>
@if (textExercise?.teamMode) {
<jhi-team-submission-sync
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Component, Input, OnDestroy, OnInit, inject } from '@angular/core';
import { Component, OnDestroy, OnInit, inject, input } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { HttpErrorResponse } from '@angular/common/http';
import { AlertService } from 'app/core/util/alert.service';
Expand Down Expand Up @@ -58,14 +58,13 @@ export class TextEditorComponent implements OnInit, OnDestroy, ComponentCanDeact
readonly ChatServiceMode = ChatServiceMode;
protected readonly isAthenaAIResult = isAthenaAIResult;

@Input() participationId?: number;
@Input() displayHeader: boolean = true;
@Input() expandProblemStatement?: boolean = true;

@Input() inputExercise?: TextExercise;
@Input() inputSubmission?: TextSubmission;
@Input() inputParticipation?: StudentParticipation;
@Input() isExamSummary = false;
participationId = input<number>();
displayHeader = input<boolean>(true);
expandProblemStatement = input<boolean | undefined>(true);
inputExercise = input<TextExercise>();
inputSubmission = input<TextSubmission>();
inputParticipation = input<StudentParticipation>();
isExamSummary = input<boolean>(false);

textExercise: TextExercise;
participation: StudentParticipation;
Expand Down Expand Up @@ -109,7 +108,7 @@ export class TextEditorComponent implements OnInit, OnDestroy, ComponentCanDeact
if (this.inputValuesArePresent()) {
this.setupComponentWithInputValues();
} else {
const participationId = this.participationId !== undefined ? this.participationId : Number(this.route.snapshot.paramMap.get('participationId'));
const participationId = this.participationId() !== undefined ? this.participationId() : Number(this.route.snapshot.paramMap.get('participationId'));
this.submissionId = Number(this.route.snapshot.paramMap.get('submissionId')) || undefined;

if (Number.isNaN(participationId)) {
Expand All @@ -121,7 +120,7 @@ export class TextEditorComponent implements OnInit, OnDestroy, ComponentCanDeact
this.updateParticipation(this.participation, this.submissionId);
});

this.textService.get(participationId).subscribe({
this.textService.get(participationId!).subscribe({
next: (data: StudentParticipation) => this.updateParticipation(data, this.submissionId),
error: (error: HttpErrorResponse) => onError(this.alertService, error),
});
Expand Down Expand Up @@ -163,7 +162,7 @@ export class TextEditorComponent implements OnInit, OnDestroy, ComponentCanDeact
}

private inputValuesArePresent(): boolean {
return !!(this.inputExercise || this.inputSubmission || this.inputParticipation);
return !!(this.inputExercise() || this.inputSubmission() || this.inputParticipation());
}

/**
Expand All @@ -174,14 +173,14 @@ export class TextEditorComponent implements OnInit, OnDestroy, ComponentCanDeact
* @private
*/
private setupComponentWithInputValues() {
if (this.inputExercise) {
this.textExercise = this.inputExercise;
if (this.inputExercise() !== undefined) {
this.textExercise = this.inputExercise()!;
}
if (this.inputSubmission) {
this.submission = this.inputSubmission;
if (this.inputSubmission() !== undefined) {
this.submission = this.inputSubmission()!;
}
if (this.inputParticipation) {
this.participation = this.inputParticipation;
if (this.inputParticipation() !== undefined) {
this.participation = this.inputParticipation()!;
}

if (this.submission?.text) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,19 @@
@for (result of displayedResults; track result; let i = $index) {
<div class="result-history-element">
<div class="result-score">
<div class="result-score-icon" [ngClass]="getTextColorClass(result, evaluateTemplateStatus(exercise, result.participation, result, false, MissingResultInfo.NONE))">
<fa-icon [icon]="getResultIconClass(result, evaluateTemplateStatus(exercise, result.participation, result, false, MissingResultInfo.NONE))" size="xl" />
<div
class="result-score-icon"
[ngClass]="
result.id === selectedResultId()
? 'text-primary'
: getTextColorClass(result, evaluateTemplateStatus(exercise(), result.participation, result, false, MissingResultInfo.NONE))
"
>
<fa-icon [icon]="getResultIconClass(result, evaluateTemplateStatus(exercise(), result.participation, result, false, MissingResultInfo.NONE))" size="xl" />
</div>
<jhi-result
class="result-score-info text-center"
[exercise]="exercise"
[exercise]="exercise()"
[result]="result"
[participation]="result.participation!"
[showUngradedResults]="true"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Component, Input, OnChanges } from '@angular/core';
import { Component, OnChanges, input } from '@angular/core';
import { Result } from 'app/entities/result.model';
import { Exercise, ExerciseType } from 'app/entities/exercise.model';
import { MissingResultInformation, evaluateTemplateStatus, getResultIconClass, getTextColorClass } from 'app/exercises/shared/result/result.utils';
Expand All @@ -17,24 +17,27 @@ export class ResultHistoryComponent implements OnChanges {
readonly evaluateTemplateStatus = evaluateTemplateStatus;
readonly MissingResultInfo = MissingResultInformation;

@Input() results: Result[];
@Input() exercise: Exercise;
results = input.required<Result[]>();
exercise = input<Exercise>();
selectedResultId = input<number>();

showPreviousDivider = false;
displayedResults: Result[];
movedLastRatedResult: boolean;

ngOnChanges(): void {
this.showPreviousDivider = this.results.length > MAX_RESULT_HISTORY_LENGTH;
if (this.exercise?.type === ExerciseType.TEXT || this.exercise?.type === ExerciseType.MODELING) {
this.displayedResults = this.results.filter((result) => result.successful !== undefined);
this.showPreviousDivider = this.results().length > MAX_RESULT_HISTORY_LENGTH;
if (this.exercise()?.type === ExerciseType.TEXT || this.exercise()?.type === ExerciseType.MODELING) {
this.displayedResults = this.results().filter((result) => result.successful !== undefined);
} else {
this.displayedResults = this.results;
this.displayedResults = this.results();
}
const successfulResultsLength = this.displayedResults.length;
if (successfulResultsLength > MAX_RESULT_HISTORY_LENGTH) {
this.displayedResults = this.displayedResults.slice(successfulResultsLength - MAX_RESULT_HISTORY_LENGTH);
const lastRatedResult = this.results.filter((result) => result.rated).last();
const lastRatedResult = this.results()
.filter((result) => result.rated)
.last();
if (!this.displayedResults.first()?.rated && lastRatedResult) {
this.displayedResults[0] = lastRatedResult;
this.movedLastRatedResult = true;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { input, runInInjectionContext } from '@angular/core';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ResultHistoryComponent } from 'app/overview/result-history/result-history.component';
import { MockPipe } from 'ng-mocks';
import { ArtemisDatePipe } from 'app/shared/pipes/artemis-date.pipe';
import { ArtemisTestModule } from '../../../test.module';
import { Result } from 'app/entities/result.model';

describe('ResultHistoryComponent', () => {
let component: ResultHistoryComponent;
Expand All @@ -25,11 +27,13 @@ describe('ResultHistoryComponent', () => {
});

it('should initialize with same rated results', () => {
component.results = [
{ rated: true, id: 1 },
{ rated: true, id: 2 },
{ rated: true, id: 3 },
];
runInInjectionContext(TestBed, () => {
component.results = input<Result[]>([
{ rated: true, id: 1 },
{ rated: true, id: 2 },
{ rated: true, id: 3 },
]);
});
component.ngOnChanges();
expect(component.displayedResults).toEqual([
{ rated: true, id: 1 },
Expand All @@ -39,14 +43,16 @@ describe('ResultHistoryComponent', () => {
expect(component.showPreviousDivider).toBeFalse();
expect(component.movedLastRatedResult).toBeFalsy();

component.results = [
{ rated: false, id: 1 },
{ rated: false, id: 2 },
{ rated: false, id: 3 },
{ rated: false, id: 4 },
{ rated: false, id: 5 },
{ rated: false, id: 6 },
];
runInInjectionContext(TestBed, () => {
component.results = input<Result[]>([
{ rated: false, id: 1 },
{ rated: false, id: 2 },
{ rated: false, id: 3 },
{ rated: false, id: 4 },
{ rated: false, id: 5 },
{ rated: false, id: 6 },
]);
});
component.ngOnChanges();
expect(component.displayedResults).toEqual([
{ rated: false, id: 2 },
Expand All @@ -60,11 +66,13 @@ describe('ResultHistoryComponent', () => {
});

it('should initialize with mixed rated results', () => {
component.results = [
{ rated: true, id: 1 },
{ rated: false, id: 2 },
{ rated: false, id: 3 },
];
runInInjectionContext(TestBed, () => {
component.results = input<Result[]>([
{ rated: true, id: 1 },
{ rated: false, id: 2 },
{ rated: false, id: 3 },
]);
});
component.ngOnChanges();
expect(component.displayedResults).toEqual([
{ rated: true, id: 1 },
Expand All @@ -74,14 +82,16 @@ describe('ResultHistoryComponent', () => {
expect(component.showPreviousDivider).toBeFalse();
expect(component.movedLastRatedResult).toBeFalsy();

component.results = [
{ rated: true, id: 1 },
{ rated: false, id: 2 },
{ rated: false, id: 3 },
{ rated: false, id: 4 },
{ rated: false, id: 5 },
{ rated: false, id: 6 },
];
runInInjectionContext(TestBed, () => {
component.results = input<Result[]>([
{ rated: true, id: 1 },
{ rated: false, id: 2 },
{ rated: false, id: 3 },
{ rated: false, id: 4 },
{ rated: false, id: 5 },
{ rated: false, id: 6 },
]);
});
component.ngOnChanges();
expect(component.displayedResults).toEqual([
{ rated: true, id: 1 },
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { DebugElement } from '@angular/core';
import { DebugElement, input, runInInjectionContext } from '@angular/core';
import dayjs from 'dayjs/esm';
import { ActivatedRoute, RouterModule, convertToParamMap } from '@angular/router';
import { ComponentFixture, TestBed, fakeAsync, flush, tick } from '@angular/core/testing';
Expand Down Expand Up @@ -110,9 +110,11 @@ describe('TextEditorComponent', () => {
});

it('should use inputValues if present instead of loading new details', fakeAsync(() => {
comp.inputExercise = textExercise;
comp.inputParticipation = participation;
comp.inputSubmission = { id: 1, text: 'test' };
runInInjectionContext(TestBed, () => {
comp.inputExercise = input<TextExercise>(textExercise);
comp.inputParticipation = input<StudentParticipation>(participation);
comp.inputSubmission = input<TextSubmission>({ id: 1, text: 'test' });
});
// @ts-ignore updateParticipation is private
const updateParticipationSpy = jest.spyOn(comp, 'updateParticipation');
// @ts-ignore setupComponentWithInputValuesSpy is private
Expand Down
Loading