Skip to content

Commit

Permalink
Merge pull request #114 from Clean-CaDET/fix/authoring-error-handling
Browse files Browse the repository at this point in the history
Fix/authoring error handling
  • Loading branch information
Luburic authored Sep 3, 2024
2 parents 502437e + 4a2cf95 commit 6cfd715
Show file tree
Hide file tree
Showing 16 changed files with 106 additions and 37 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,10 @@
{{subactivity.order}}. {{subactivity.code}}: {{subactivity.name}}
</small>
<span class="flex-row">
<button [disabled]="i === 0" mat-icon-button (click)="reorder(activity, i, true)">
<button [disabled]="i === 0" mat-icon-button (click)="reorder(activity, i, true); $event.stopPropagation();">
<mat-icon>keyboard_arrow_up</mat-icon>
</button>
<button [disabled]="i === activity.subactivities.length - 1" mat-icon-button (click)="reorder(activity, i, false)">
<button [disabled]="i === activity.subactivities.length - 1" mat-icon-button (click)="reorder(activity, i, false); $event.stopPropagation();">
<mat-icon>keyboard_arrow_down</mat-icon>
</button>
</span>
Expand All @@ -43,6 +43,6 @@
<mat-divider vertical="true"></mat-divider>
<div *ngIf="activeActivity" style="width: 70%;">
<!-- Warning: There is a bug for client-side code uniquness validation as the component receives only the step subactivites (and not all task activities). -->
<cc-activity-details [activity]="activeActivity" (activitySaved)="save($event)" [activities]="activities"></cc-activity-details>
<cc-activity-details [activity]="activeActivity" (activitySaved)="save($event)" [activities]="activities" [updateStatus]="updateStatus"></cc-activity-details>
</div>
</div>
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import { Component, EventEmitter, Input, OnChanges, Output } from '@angular/core';
import { Component, EventEmitter, Input, OnChanges, Output, SimpleChanges } from '@angular/core';
import { MatDialog } from '@angular/material/dialog';
import { DeleteFormComponent } from 'src/app/shared/generics/delete-form/delete-form.component';
import { Activity } from '../../model/activity';
import { isRequestStartedOrError, RequestStatus } from 'src/app/shared/generics/model/request-status';

@Component({
selector: 'cc-activities',
Expand All @@ -10,6 +11,7 @@ import { Activity } from '../../model/activity';
})
export class ActivitiesComponent implements OnChanges {
@Input() activities: Activity[];
@Input() updateStatus: RequestStatus;

activeActivity: Activity;

Expand All @@ -18,7 +20,8 @@ export class ActivitiesComponent implements OnChanges {

constructor(private dialog: MatDialog) { }

ngOnChanges(): void {
ngOnChanges(changes: SimpleChanges): void {
if(isRequestStartedOrError(changes.updateStatus)) return; // Triggers from parent when HTTP request starts
if(this.activeActivity) {
let correspondingActivity = this.activities.find(a => a.id === this.activeActivity.id || a.code === this.activeActivity.code);
if(correspondingActivity) {
Expand Down Expand Up @@ -60,7 +63,6 @@ export class ActivitiesComponent implements OnChanges {

save(activity: Activity) {
this.activitySaved.emit(activity);
this.activeActivity = activity;
}

delete(activity: Activity) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,7 @@
<mat-form-field style="width: 100px;" [appearance]="editMode ? 'fill' : 'outline'">
<mat-label>Kod</mat-label>
<input formControlName="code" matInput [readonly]="!editMode">
<mat-error *ngIf="activityForm.get('code').hasError('notUnique') && activityForm.get('code').touched">
Mora biti jedinstven za zadatak
</mat-error>
<mat-error *ngIf="activityForm.get('code').hasError('notUnique') && activityForm.get('code').touched">Jedinstven za zadatak</mat-error>
</mat-form-field>
<mat-form-field style="flex-grow: 1;" [appearance]="editMode ? 'fill' : 'outline'">
<mat-label>Naziv</mat-label>
Expand All @@ -19,11 +17,11 @@
<mat-icon>edit</mat-icon>
</button>
<button mat-mini-fab *ngIf="editMode" color="primary" matTooltip="Sačuvaj"
[disabled]="!activityForm.valid" (click)="submit()">
[disabled]="!activityForm.valid || updateStatus === 1" (click)="submit()">
<mat-icon>check</mat-icon>
</button>
<button mat-mini-fab *ngIf="editMode" color="warn"
matTooltip="Odbaci" (click)="discardChanges()">
<button mat-mini-fab *ngIf="editMode" color="warn" matTooltip="Odbaci"
[disabled]="updateStatus === 1" (click)="discardChanges()">
<mat-icon>clear</mat-icon>
</button>
</section>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Component, EventEmitter, Input, OnChanges, Output } from '@angular/core';
import { Component, EventEmitter, Input, OnChanges, Output, SimpleChanges } from '@angular/core';
import { AbstractControl, FormArray, FormBuilder, FormControl, FormGroup, ValidationErrors, ValidatorFn, Validators } from '@angular/forms';
import { Activity } from '../../model/activity';
import { isRequestStartedOrError, RequestStatus } from 'src/app/shared/generics/model/request-status';

@Component({
selector: 'cc-activity-details',
Expand All @@ -10,14 +11,16 @@ import { Activity } from '../../model/activity';
export class ActivityDetailsComponent implements OnChanges {
@Input() activity: Activity;
@Input() activities: Activity[];
@Input() updateStatus: RequestStatus;
@Output() activitySaved = new EventEmitter<Activity>();

activityForm: FormGroup;
editMode: boolean = false;

constructor(private builder: FormBuilder) { }

ngOnChanges(): void {
ngOnChanges(changes: SimpleChanges): void {
if(isRequestStartedOrError(changes.updateStatus)) return; // Triggers from parent when HTTP request starts
this.createForm();
if (this.activity.id || this.activity.name) {
this.view();
Expand Down Expand Up @@ -145,7 +148,6 @@ export class ActivityDetailsComponent implements OnChanges {
changedActivity.parentId = this.activity.parentId;
changedActivity.order = this.activity.order;
this.activitySaved.emit(changedActivity);
this.view();
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,10 +67,11 @@
</div>
<mat-divider vertical="true" style="border-width: 3px;"></mat-divider>
<div style="width: 75%">
<cc-task-details *ngIf="mode === 'task'" [task]="task" (taskSaved)="updateTask($event)"></cc-task-details>
<cc-activity-details *ngIf="mode === 'guidance' && selectedStep" [activity]="selectedStep" [activities]="task.steps"
<mat-progress-bar mode="indeterminate" style="position: fixed" *ngIf="updateStatus === 1"></mat-progress-bar>
<cc-task-details *ngIf="mode === 'task'" [task]="task" (taskSaved)="updateTask($event)" [updateStatus]="updateStatus"></cc-task-details>
<cc-activity-details *ngIf="mode === 'guidance' && selectedStep" [activity]="selectedStep" [activities]="task.steps" [updateStatus]="updateStatus"
(activitySaved)="createOrUpdateStep($event)"></cc-activity-details>
<cc-activities *ngIf="mode === 'subactivities' && selectedStep" [activities]="subactivities"
<cc-activities *ngIf="mode === 'subactivities' && selectedStep" [activities]="subactivities" [updateStatus]="updateStatus"
(activitySaved)="createOrUpdateStep($event)" (activityDeleted)="deleteActivity($event)"></cc-activities>
</div>
</div>
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
.viewport {
height: calc(100vh - 210px);
width: 100%;
}
}

.mat-mdc-progress-bar {
--mdc-linear-progress-track-height: 5px;
--mdc-linear-progress-active-indicator-height: 5px;
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { DeleteFormComponent } from 'src/app/shared/generics/delete-form/delete-
import { MatSnackBar } from '@angular/material/snack-bar';
import { Title } from '@angular/platform-browser';
import { MarkdownPanelComponent } from 'src/app/shared/markdown/markdown-panel/markdown-panel.component';
import { RequestStatus } from 'src/app/shared/generics/model/request-status';

@Component({
selector: 'cc-learning-task',
Expand All @@ -20,6 +21,7 @@ export class LearningTaskComponent implements OnInit, OnDestroy {

courseId: number;
unitId: number;
updateStatus: RequestStatus = RequestStatus.None;

task: LearningTask;
mode: string = 'task';
Expand Down Expand Up @@ -70,6 +72,7 @@ export class LearningTaskComponent implements OnInit, OnDestroy {
}

private selectStep(step: Activity, mode: string) {
this.updateStatus = RequestStatus.None;
this.selectedStep = step;
this.mode = mode;
if (this.mode === 'subactivities') {
Expand Down Expand Up @@ -181,17 +184,25 @@ export class LearningTaskComponent implements OnInit, OnDestroy {
}

updateTask(task: LearningTask) {
this.updateStatus = RequestStatus.Started;
this.taskService.update(this.unitId, task)
.subscribe({
next: updatedTask => {
this.updateStatus = RequestStatus.Completed;
this.setupTaskAndActivities(updatedTask);
if (this.selectedStep) {
this.setParams(this.task.steps.find(s => s.id === this.selectedStep.id || s.code === this.selectedStep.code), this.mode);
}
},
error: (error) => {
if (error.error.status === 409)
this.updateStatus = RequestStatus.Error;
if (error.error.status === 409) {
this.errorsBar.open('Greška: Aktivnost sa datim kodom već postoji u zadatku. Izmeni kod.', "OK", { horizontalPosition: 'right', verticalPosition: 'top' });
} else if(error.status === 0) {
this.errorsBar.open('Greška: Server nije prihvatio zahtev. Probaj da ponoviš operaciju.', "OK", { horizontalPosition: 'right', verticalPosition: 'top' });
} else {
this.errorsBar.open('Greška: Zahtev nije obrađen. Kod greške je: ' + error.error.status + ' Probaj da ponoviš operaciju.', "OK", { horizontalPosition: 'right', verticalPosition: 'top' });
}
}
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,11 @@
matTooltip="Izmeni" [color]="task?.description ? '' : 'primary'">
<mat-icon>edit</mat-icon>
</button>
<button mat-mini-fab *ngIf="editMode" [disabled]="!taskForm.valid"
<button mat-mini-fab *ngIf="editMode" [disabled]="!taskForm.valid || updateStatus === 1"
(click)="submitForm()" color="primary" matTooltip="Sačuvaj">
<mat-icon>check</mat-icon>
</button>
<button mat-mini-fab *ngIf="editMode" matTooltip="Odbaci"
<button mat-mini-fab *ngIf="editMode" matTooltip="Odbaci" [disabled]="updateStatus === 1"
(click)="discardChanges()" color="warn">
<mat-icon>clear</mat-icon>
</button>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Component, EventEmitter, Input, OnChanges, Output } from '@angular/core';
import { Component, EventEmitter, Input, OnChanges, Output, SimpleChanges } from '@angular/core';
import { LearningTask } from '../../model/learning-task';
import { FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms';
import { isRequestStartedOrError, RequestStatus } from 'src/app/shared/generics/model/request-status';

@Component({
selector: 'cc-task-details',
Expand All @@ -10,14 +11,16 @@ import { FormBuilder, FormControl, FormGroup, Validators } from '@angular/forms'
export class TaskDetailsComponent implements OnChanges {

@Input() task: LearningTask;
@Input() updateStatus: RequestStatus;
@Output() taskSaved = new EventEmitter<LearningTask>();

taskForm: FormGroup;
editMode: boolean = false;

constructor(private builder: FormBuilder) { }

ngOnChanges(): void {
ngOnChanges(changes: SimpleChanges): void {
if(isRequestStartedOrError(changes.updateStatus)) return; // Triggers from parent when HTTP request starts
this.createForm();
if (this.task) {
this.setInitialValues(this.task);
Expand All @@ -43,7 +46,6 @@ export class TaskDetailsComponent implements OnChanges {

submitForm() {
if (this.taskForm.valid) {
this.editMode = false;
this.task.name = this.taskForm.value.name;
this.task.description = this.taskForm.value.description;
this.task.isTemplate = this.taskForm.value.isTemplate;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,15 @@ export class MultipleChoiceQuestionComponent implements OnInit, OnDestroy, Learn
reattemptCount: this.submissionReattemptCount
};
this.submissionIsProcessing = true;
this.submissionService.submit(this.learningObject.id, submission).subscribe((feedback) => {
this.submissionReattemptCount++;
this.feedbackConnector.sendToFeedback(feedback);
this.submissionService.submit(this.learningObject.id, submission).subscribe({
next: feedback => {
this.submissionReattemptCount++;
this.feedbackConnector.sendToFeedback(feedback);
},
error: error => {
console.log(error);
this.feedbackConnector.sendToFeedback({type: feedbackTypes.error});
}
});
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,9 +65,15 @@ export class MultipleResponseQuestionComponent implements OnInit, OnDestroy, Lea
reattemptCount: this.submissionReattemptCount
};
this.submissionIsProcessing = true;
this.submissionService.submit(this.learningObject.id, submission).subscribe((feedback) => {
this.submissionReattemptCount++;
this.feedbackConnector.sendToFeedback(feedback);
this.submissionService.submit(this.learningObject.id, submission).subscribe({
next: feedback => {
this.submissionReattemptCount++;
this.feedbackConnector.sendToFeedback(feedback);
},
error: error => {
console.log(error);
this.feedbackConnector.sendToFeedback({type: feedbackTypes.error});
}
});
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,15 @@ export class ShortAnswerQuestionComponent implements LearningObjectComponent {
reattemptCount: this.submissionReattemptCount
};
this.submissionIsProcessing = true;
this.submissionService.submit(this.learningObject.id, submission).subscribe(feedback => {
this.submissionReattemptCount++;
this.feedbackConnector.sendToFeedback(feedback);
this.submissionService.submit(this.learningObject.id, submission).subscribe({
next: feedback => {
this.submissionReattemptCount++;
this.feedbackConnector.sendToFeedback(feedback);
},
error: error => {
console.log(error);
this.feedbackConnector.sendToFeedback({type: feedbackTypes.error});
}
});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { Output, EventEmitter } from '@angular/core';
import { Subscription } from 'rxjs';
import { ActivatedRoute, Params } from '@angular/router';
import { KnowledgeComponentService } from '../knowledge-component.service';
import { Feedback } from '../../model/learning-objects/feedback.model';
import { Feedback, feedbackTypes } from '../../model/learning-objects/feedback.model';
import { KnowledgeComponentStatistics } from '../../model/knowledge-component-statistics.model';
import { createResponse, welcomeMessage } from './instructional-feedback/feedback-message-creator';

Expand Down Expand Up @@ -48,6 +48,14 @@ export class SubmissionResultComponent implements OnInit, OnDestroy {
getKnowledgeComponentStatistics(feedback: Feedback): void {
this.feedbackProcessed = false;
this.feedbackMessage = "";
if(feedback?.type === feedbackTypes.error) {
this.messageTimeout = setTimeout(() => {
this.feedbackMessage = "🤕 Nešto sam se pokvario i nisam uspeo da obradim tvoj odgovor.\nAko ti radi internet probaj ponovo.\nAko se problem nastavi javi se mentoru."
this.feedbackProcessed = true;
this.assessmentConnector.sendToAssessment(feedback);
}, 400);
return;
}
this.kcService.getKnowledgeComponentStatistics(this.kcId).subscribe(result => {
if(feedback) {
let isFirstSatisfaction = this.statistics.isSatisfied !== result.isSatisfied
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@ import { Evaluation } from "./evaluation.model";

export interface Feedback {
type: string;
hint: string;
evaluation: Evaluation;
hint?: string;
evaluation?: Evaluation;
}

export const feedbackTypes = {
pump: 'Pump',
hint: 'Hint',
correctness: 'Correctness',
solution: 'Solution',
error: "Error"
};
12 changes: 12 additions & 0 deletions src/app/shared/generics/model/request-status.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
export enum RequestStatus {
None = 0,
Started = 1,
Completed = 2,
Error = 3
}

export function isRequestStartedOrError(changes: {currentValue: RequestStatus, previousValue: RequestStatus}) {
if(!changes) return false;
if(changes.currentValue === changes.previousValue) return false; // Change was triggered by another field
return changes.currentValue === RequestStatus.Started || changes.currentValue === RequestStatus.Error;
}
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,15 @@ export class MarkdownEditorComponent implements OnChanges, AfterViewInit {
selectionStart,
selectionStart + tagText.length
);

// Scroll the textarea to make the selected text visible
const textarea = this.textArea.nativeElement;
const lineHeight = parseInt(window.getComputedStyle(textarea).lineHeight, 10);
const scrollHeight = textarea.scrollHeight;
const textBeforeSelection = textarea.value.slice(0, selectionStart);
const linesBeforeSelection = (textBeforeSelection.match(/\n/g) || []).length;
const scrollPosition = linesBeforeSelection * lineHeight;
textarea.scrollTop = Math.min(scrollPosition, scrollHeight - textarea.clientHeight);
});
}

Expand Down

0 comments on commit 6cfd715

Please sign in to comment.