Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
41 changes: 27 additions & 14 deletions src/app/api/models/task-definition.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,20 @@
import { HttpClient } from '@angular/common/http';
import { Entity, EntityMapping } from 'ngx-entity-service';
import { Observable, tap } from 'rxjs';
import { AppInjector } from 'src/app/app-injector';
import { DoubtfireConstants } from 'src/app/config/constants/doubtfire-constants';
import { Grade, GroupSet, TutorialStream, Unit } from './doubtfire-model';
import { TaskDefinitionService } from '../services/task-definition.service';

export type UploadRequirement = { key: string; name: string; type: string; tiiCheck?: boolean; tiiPct?: number };
import {HttpClient} from '@angular/common/http';
import {Entity, EntityMapping} from 'ngx-entity-service';
import {Observable, tap} from 'rxjs';
import {AppInjector} from 'src/app/app-injector';
import {DoubtfireConstants} from 'src/app/config/constants/doubtfire-constants';
import {Grade, GroupSet, TutorialStream, Unit} from './doubtfire-model';
import {TaskDefinitionService} from '../services/task-definition.service';

export type UploadRequirement = {
key: string;
name: string;
type: string;
tiiCheck?: boolean;
tiiPct?: number;
};

export type SimilarityCheck = { key: string; type: string; pattern: string };
export type SimilarityCheck = {key: string; type: string; pattern: string};

export class TaskDefinition extends Entity {
id: number;
Expand Down Expand Up @@ -37,6 +43,8 @@ export class TaskDefinition extends Entity {
scormBypassTest: boolean;
scormTimeDelayEnabled: boolean;
scormAttemptLimit: number = 0;
tutorialSelfEnrolmentEnabled: boolean;
tutorialSelfEnrolmentStream: TutorialStream = null;
hasTaskAssessmentResources: boolean;
isGraded: boolean;
maxQualityPts: number;
Expand Down Expand Up @@ -73,15 +81,15 @@ export class TaskDefinition extends Entity {
entity: this,
cache: this.unit.taskDefinitionCache,
constructorParams: this.unit,
}
},
);
} else {
return svc.update(
{
unitId: this.unit.id,
id: this.id,
},
{ entity: this }
{entity: this},
);
}
}
Expand Down Expand Up @@ -128,7 +136,10 @@ export class TaskDefinition extends Entity {
}

public matches(text: string): boolean {
return this.abbreviation.toLowerCase().indexOf(text) !== -1 || this.name.toLowerCase().indexOf(text) !== -1;
return (
this.abbreviation.toLowerCase().indexOf(text) !== -1 ||
this.name.toLowerCase().indexOf(text) !== -1
);
}

/**
Expand Down Expand Up @@ -221,7 +232,9 @@ export class TaskDefinition extends Entity {

public deleteTaskResources(): Observable<any> {
const httpClient = AppInjector.get(HttpClient);
return httpClient.delete(this.taskResourcesUploadUrl).pipe(tap(() => (this.hasTaskResources = false)));
return httpClient
.delete(this.taskResourcesUploadUrl)
.pipe(tap(() => (this.hasTaskResources = false)));
}

public deleteScormData(): Observable<any> {
Expand Down
28 changes: 19 additions & 9 deletions src/app/api/services/task-definition.service.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { CachedEntityService } from 'ngx-entity-service';
import { TaskDefinition, Unit } from 'src/app/api/models/doubtfire-model';
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import {CachedEntityService} from 'ngx-entity-service';
import {TaskDefinition, Unit} from 'src/app/api/models/doubtfire-model';
import {Injectable} from '@angular/core';
import {HttpClient} from '@angular/common/http';
import API_URL from 'src/app/config/constants/apiURL';
import { MappingFunctions } from './mapping-fn';
import { AppInjector } from 'src/app/app-injector';
import { Observable } from 'rxjs';
import {MappingFunctions} from './mapping-fn';
import {AppInjector} from 'src/app/app-injector';
import {Observable} from 'rxjs';

@Injectable()
export class TaskDefinitionService extends CachedEntityService<TaskDefinition> {
Expand Down Expand Up @@ -81,6 +81,15 @@ export class TaskDefinitionService extends CachedEntityService<TaskDefinition> {
return taskDef.tutorialStream?.abbreviation;
},
},
{
keys: ['tutorialSelfEnrolmentStream', 'tutorial_self_enrolment_stream_abbr'],
toEntityFn: (data: object, key: string, taskDef: TaskDefinition, params?: any) => {
return taskDef.unit.tutorialStreamsCache.get(data[key]);
},
toJsonFn: (taskDef: TaskDefinition, key: string) => {
return taskDef.tutorialSelfEnrolmentStream?.abbreviation;
},
},
'plagiarismWarnPct',
'restrictStatusUpdates',
{
Expand All @@ -100,6 +109,7 @@ export class TaskDefinitionService extends CachedEntityService<TaskDefinition> {
'hasTaskResources',
'hasTaskAssessmentResources',
'scormEnabled',
'tutorialSelfEnrolmentEnabled',
'hasScormData',
'scormAllowReview',
'scormBypassTest',
Expand All @@ -108,15 +118,15 @@ export class TaskDefinitionService extends CachedEntityService<TaskDefinition> {
'isGraded',
'maxQualityPts',
'overseerImageId',
'assessmentEnabled'
'assessmentEnabled',
);

this.mapping.mapAllKeysToJsonExcept(
'id',
'hasTaskSheet',
'hasTaskResources',
'hasTaskAssessmentResources',
'hasScormData'
'hasScormData',
);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
<h3 mat-dialog-title>Tutorial Enrolment</h3>
<div mat-dialog-content>
<p class="w-full">
Please select from the dropdown below the tutorial you would like to enrol in.
</p>

<mat-form-field class="w-full">
<mat-label>Tutorials</mat-label>
<mat-select [formControl]="tutorialsFormControl">
@for (tutorial of getTutorialsForStream(); track tutorial) {
<mat-option [value]="tutorial">{{ tutorial.abbreviation }}</mat-option>
}
</mat-select>
</mat-form-field>
</div>

<div mat-dialog-actions>
<button mat-dialog-close mat-stroked-button color="warn">Cancel</button>
<button
mat-dialog-close
style="margin-right: 10px; float: right"
mat-stroked-button
color="primary"
[disabled]="!tutorialsFormControl.value"
mat-button
(click)="attemptTutorialEnrolment()"
>
Enrol
</button>
</div>
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import {Component, Inject, LOCALE_ID} from '@angular/core';
import {MatDialogRef, MAT_DIALOG_DATA} from '@angular/material/dialog';
import {
TaskComment,
TaskCommentService,
Task,
Unit,
Tutorial,
} from 'src/app/api/models/doubtfire-model';
import {AppInjector} from 'src/app/app-injector';
import {FormControl, Validators, FormGroup, FormGroupDirective, NgForm} from '@angular/forms';
import {ErrorStateMatcher} from '@angular/material/core';
import {AlertService} from '../../services/alert.service';

export interface TutorialEnrolmentModalData {
task: Task;
}

@Component({
selector: 'f-tutorial-enrolment-modal',
templateUrl: './tutorial-enrolment-modal.component.html',
})
export class TutorialEnrolmentModalComponent {
tutorialsFormControl = new FormControl<Tutorial | null>(null);

constructor(
public dialogRef: MatDialogRef<TutorialEnrolmentModalComponent>,
@Inject(MAT_DIALOG_DATA) public data: TutorialEnrolmentModalData,
private alerts: AlertService,
) {}

public getTutorialsForStream() {
const tutorialStreamAbbreviation =
this.data.task.definition.tutorialSelfEnrolmentStream.abbreviation;

const student = this.data.task.project;
return this.data.task.unit.tutorials.filter((tutorial) => {
// Filter workshops based on campus allocation
const result: boolean =
student.campus == null ||
tutorial.campus == null ||
student.campus.id === tutorial.campus.id;

if (!result) return result;
if (tutorial.tutorialStream) {
return tutorial.tutorialStream.abbreviation === tutorialStreamAbbreviation;
}
});
}

attemptTutorialEnrolment() {
const selectedTutorial = this.tutorialsFormControl.value;
this.data.task.project.switchToTutorial(selectedTutorial);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import {Injectable} from '@angular/core';
import {Task} from 'src/app/api/models/task';
import {MatDialogRef, MatDialog} from '@angular/material/dialog';
import {
TutorialEnrolmentModalComponent,
TutorialEnrolmentModalData,
} from './tutorial-enrolment-modal.component';

@Injectable({
providedIn: 'root',
})
export class TutorialEnrolmentModalService {
constructor(public dialog: MatDialog) {}

public show(data: TutorialEnrolmentModalData) {
let dialogRef: MatDialogRef<TutorialEnrolmentModalComponent, TutorialEnrolmentModalData>;

dialogRef = this.dialog.open(TutorialEnrolmentModalComponent, {data});

dialogRef.afterOpened().subscribe((result: any) => {});

dialogRef.afterClosed().subscribe((result: any) => {});
}
}
6 changes: 6 additions & 0 deletions src/app/doubtfire-angular.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,9 @@ import {ScormExtensionModalComponent} from './common/modals/scorm-extension-moda
import { GradeIconComponent } from './common/grade-icon/grade-icon.component';
import { GradeTaskModalComponent } from './tasks/modals/grade-task-modal/grade-task-modal.component';
import { PrivacyPolicy } from './config/privacy-policy/privacy-policy';
import { TaskDefinitionTutorialEnrolmentComponent } from './units/states/edit/directives/unit-tasks-editor/task-definition-editor/task-definition-tutorial-enrolment/task-definition-tutorial-enrolment.component';
import {TaskTutorialEnrolmentCardComponent} from './projects/states/dashboard/directives/task-dashboard/directives/task-tutorial-enrolment-card/task-tutorial-enrolment-card.component';
import { TutorialEnrolmentModalComponent } from './common/modals/tutorial-enrolment-modal/tutorial-enrolment-modal.component';

// See https://stackoverflow.com/questions/55721254/how-to-change-mat-datepicker-date-format-to-dd-mm-yyyy-in-simplest-way/58189036#58189036
const MY_DATE_FORMAT = {
Expand Down Expand Up @@ -389,6 +392,9 @@ import { UnitStudentEnrolmentModalComponent } from './units/modals/unit-student-
TaskScormCardComponent,
ScormExtensionCommentComponent,
ScormExtensionModalComponent,
TaskDefinitionTutorialEnrolmentComponent,
TaskTutorialEnrolmentCardComponent,
TutorialEnrolmentModalComponent,
],
// Services we provide
providers: [
Expand Down
6 changes: 6 additions & 0 deletions src/app/doubtfire-angularjs.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,7 @@ import {GradeService} from './common/services/grade.service';
import {TaskScormCardComponent} from './projects/states/dashboard/directives/task-dashboard/directives/task-scorm-card/task-scorm-card.component';
import { UnitStudentEnrolmentModalService } from './units/modals/unit-student-enrolment-modal/unit-student-enrolment-modal.service';
import { PrivacyPolicy } from './config/privacy-policy/privacy-policy';
import {TaskTutorialEnrolmentCardComponent} from './projects/states/dashboard/directives/task-dashboard/directives/task-tutorial-enrolment-card/task-tutorial-enrolment-card.component';

export const DoubtfireAngularJSModule = angular.module('doubtfire', [
'doubtfire.config',
Expand Down Expand Up @@ -495,3 +496,8 @@ DoubtfireAngularJSModule.directive(
'fTaskVisualisation',
downgradeComponent({ component: TaskVisualisationComponent })
);

DoubtfireAngularJSModule.directive(
'fTaskTutorialEnrolmentCard',
downgradeComponent({component: TaskTutorialEnrolmentCardComponent}),
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<mat-card appearance="outlined" [hidden]="!this.task.definition.tutorialSelfEnrolmentEnabled">
<mat-card-header>
<mat-card-title>Tutorial Enrolment</mat-card-title>
</mat-card-header>
<mat-card-content>
<p class="pt-2">
This task lets you choose a tutorial to enrol in for the teaching period. Your selected
tutorial will determine which tutor is assigned to mark your work throughout the teaching
period.
</p>
@if (!isStudentEnrolledInTutorialStream()) {
<p>To complete this task, you must enrol in a tutorial from the provided list.</p>
}
@if (isStudentEnrolledInTutorialStream()) {
<p class="text-green-600 font-bold">You're enrolled! You can now proceed to the next task.</p>
<p>
You can confirm your enrolment in the
<a uiSref="projects/tutorials" [uiParams]="{projectId: this.task.project.id, taskAbbr: ''}"
>Tutorials List</a
>.
</p>
}
</mat-card-content>
@if (!isStudentEnrolledInTutorialStream()) {
<mat-card-actions class="space-x-2 px-5 pt-2">
<button mat-stroked-button (click)="launchTutorialEnrolment()" [disabled]="isNotStudent()">
<mat-icon style="width: 20px; height: 20px; font-size: 20px" aria-label="Launch icon">
launch
</mat-icon>
Begin enrolment
</button>
</mat-card-actions>
}
</mat-card>
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import {Component, Input} from '@angular/core';
import {Task, User, UserService} from 'src/app/api/models/doubtfire-model';
import {TutorialEnrolmentModalService} from 'src/app/common/modals/tutorial-enrolment-modal/tutorial-enrolment-modal.service';

@Component({
selector: 'f-task-tutorial-enrolment-card',
templateUrl: './task-tutorial-enrolment-card.component.html',
styleUrls: ['./task-tutorial-enrolment-card.component.scss'],
})
export class TaskTutorialEnrolmentCardComponent {
@Input() task: Task;
user: User;

constructor(
private userService: UserService,
private tutorialEnrolmentModalService: TutorialEnrolmentModalService,
) {
this.user = this.userService.currentUser;
}

isStudentEnrolledInTutorialStream(): boolean {
const tutorialStreamAbbreviation =
this.task.definition.tutorialSelfEnrolmentStream.abbreviation;
let isEnrolledInTutorialStream = false;

const studentTutorialEnrolments = this.task.project.tutorials;
for (const tutorial of studentTutorialEnrolments) {
if (tutorial.tutorialStream.abbreviation === tutorialStreamAbbreviation) {
isEnrolledInTutorialStream = true;
}
}
return isEnrolledInTutorialStream;
}

isNotStudent(): boolean {
return this.user !== this.task.project.student;
}

launchTutorialEnrolment(): void {
this.tutorialEnrolmentModalService.show({task: this.task});
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
<!-- <div class="col-sm-12 col-xl-6"> -->
<!-- migrated -->
<f-task-scorm-card [task]="task"></f-task-scorm-card>
<f-task-tutorial-enrolment-card [task]="task"></f-task-scorm-card>
<f-task-status-card [task]="task"></f-task-status-card>
<f-task-due-card [task]="task"></f-task-due-card>
<task-description-card [task-def]="task.definition" [task]="task" [unit]="task.unit"></task-description-card>
Expand Down
Loading