From 42fd7774df9301c863c56091c99b277ccbb43dc2 Mon Sep 17 00:00:00 2001 From: Geoffrey Kwan Date: Fri, 18 Nov 2022 11:54:01 -0500 Subject: [PATCH] chore(Export): Convert export step visits (#906) --- package-lock.json | 25 +- package.json | 1 + src/app/teacher/classroom-monitor.module.ts | 2 + .../dataExport/data-export-module.ts | 12 +- .../export-step-visits.component.html | 88 ++ .../export-step-visits.component.scss | 48 + .../export-step-visits.component.spec.ts | 117 ++ .../export-step-visits.component.ts} | 312 ++--- .../dataExport/exportController.ts | 29 +- .../dataExport/exportVisits.html | 108 -- .../dataExport/exportVisitsController.spec.js | 1042 ----------------- src/messages.xlf | 278 +++++ 12 files changed, 738 insertions(+), 1324 deletions(-) create mode 100644 src/assets/wise5/classroomMonitor/dataExport/export-step-visits/export-step-visits.component.html create mode 100644 src/assets/wise5/classroomMonitor/dataExport/export-step-visits/export-step-visits.component.scss create mode 100644 src/assets/wise5/classroomMonitor/dataExport/export-step-visits/export-step-visits.component.spec.ts rename src/assets/wise5/classroomMonitor/dataExport/{exportVisitsController.ts => export-step-visits/export-step-visits.component.ts} (54%) delete mode 100644 src/assets/wise5/classroomMonitor/dataExport/exportVisits.html delete mode 100644 src/assets/wise5/test-unit/controllers/dataExport/exportVisitsController.spec.js diff --git a/package-lock.json b/package-lock.json index 771fb19270c..021ac98c4e3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -46,6 +46,7 @@ "drawing-tool": "^2.1.2", "eventemitter2": "^5.0.1", "fabric": "3.6.3", + "file-saver": "^2.0.5", "highcharts": "^9.3.3", "highcharts-angular": "^2.10.0", "html2canvas": "^0.5.0-beta4", @@ -4790,6 +4791,11 @@ "node": ">=0.12" } }, + "node_modules/angular-file-saver/node_modules/file-saver": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/file-saver/-/file-saver-1.3.8.tgz", + "integrity": "sha512-spKHSBQIxxS81N/O21WmuXA2F6wppUCsutpzenOeZzOCCJ5gEfcbqJP983IrpLXzYmXnMUa6J03SubcNPdKrlg==" + }, "node_modules/angular-inview": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/angular-inview/-/angular-inview-2.3.1.tgz", @@ -10365,9 +10371,9 @@ } }, "node_modules/file-saver": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/file-saver/-/file-saver-1.3.8.tgz", - "integrity": "sha512-spKHSBQIxxS81N/O21WmuXA2F6wppUCsutpzenOeZzOCCJ5gEfcbqJP983IrpLXzYmXnMUa6J03SubcNPdKrlg==" + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/file-saver/-/file-saver-2.0.5.tgz", + "integrity": "sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA==" }, "node_modules/file-uri-to-path": { "version": "1.0.0", @@ -30956,6 +30962,13 @@ "requires": { "blob-tmp": "^1.0.0", "file-saver": "^1.3.3" + }, + "dependencies": { + "file-saver": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/file-saver/-/file-saver-1.3.8.tgz", + "integrity": "sha512-spKHSBQIxxS81N/O21WmuXA2F6wppUCsutpzenOeZzOCCJ5gEfcbqJP983IrpLXzYmXnMUa6J03SubcNPdKrlg==" + } } }, "angular-inview": { @@ -35183,9 +35196,9 @@ } }, "file-saver": { - "version": "1.3.8", - "resolved": "https://registry.npmjs.org/file-saver/-/file-saver-1.3.8.tgz", - "integrity": "sha512-spKHSBQIxxS81N/O21WmuXA2F6wppUCsutpzenOeZzOCCJ5gEfcbqJP983IrpLXzYmXnMUa6J03SubcNPdKrlg==" + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/file-saver/-/file-saver-2.0.5.tgz", + "integrity": "sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA==" }, "file-uri-to-path": { "version": "1.0.0", diff --git a/package.json b/package.json index d585c0a6202..bb210c57bfb 100644 --- a/package.json +++ b/package.json @@ -47,6 +47,7 @@ "drawing-tool": "^2.1.2", "eventemitter2": "^5.0.1", "fabric": "3.6.3", + "file-saver": "^2.0.5", "highcharts": "^9.3.3", "highcharts-angular": "^2.10.0", "html2canvas": "^0.5.0-beta4", diff --git a/src/app/teacher/classroom-monitor.module.ts b/src/app/teacher/classroom-monitor.module.ts index 3f30df2b4f3..024d02591ff 100644 --- a/src/app/teacher/classroom-monitor.module.ts +++ b/src/app/teacher/classroom-monitor.module.ts @@ -59,6 +59,7 @@ import { NodeProgressViewComponent } from '../../assets/wise5/classroomMonitor/c import { TopBarComponent } from '../../assets/wise5/classroomMonitor/classroomMonitorComponents/shared/top-bar/top-bar.component'; import { NotebookGradingComponent } from '../../assets/wise5/classroomMonitor/notebook-grading/notebook-grading.component'; import { StudentGradingComponent } from '../../assets/wise5/classroomMonitor/student-grading/student-grading.component'; +import { ExportStepVisitsComponent } from '../../assets/wise5/classroomMonitor/dataExport/export-step-visits/export-step-visits.component'; @NgModule({ declarations: [ @@ -72,6 +73,7 @@ import { StudentGradingComponent } from '../../assets/wise5/classroomMonitor/stu EditComponentAnnotationsComponent, EditComponentCommentComponent, EditComponentScoreComponent, + ExportStepVisitsComponent, GradingEditComponentMaxScoreComponent, ManagePeriodComponent, ManageShowStudentInfoComponent, diff --git a/src/assets/wise5/classroomMonitor/dataExport/data-export-module.ts b/src/assets/wise5/classroomMonitor/dataExport/data-export-module.ts index 1b290d41b94..b6565b54ba6 100644 --- a/src/assets/wise5/classroomMonitor/dataExport/data-export-module.ts +++ b/src/assets/wise5/classroomMonitor/dataExport/data-export-module.ts @@ -1,18 +1,16 @@ -import { downgradeInjectable } from '@angular/upgrade/static'; +import { downgradeComponent, downgradeInjectable } from '@angular/upgrade/static'; import * as angular from 'angular'; import { ComponentServiceLookupService } from '../../services/componentServiceLookupService'; import { DataExportService } from '../../services/dataExportService'; import DataExportController from './dataExportController'; -import ExportController from './exportController'; -import ExportVisitsController from './exportVisitsController'; +import { ExportStepVisitsComponent } from './export-step-visits/export-step-visits.component'; export default angular .module('dataExport', ['ngFileSaver']) .factory('DataExportService', downgradeInjectable(DataExportService)) .factory('ComponentServiceLookupService', downgradeInjectable(ComponentServiceLookupService)) .controller('DataExportController', DataExportController) - .controller('ExportController', ExportController) - .controller('ExportVisitsController', ExportVisitsController) + .directive('exportStepVisits', downgradeComponent({ component: ExportStepVisitsComponent })) .config([ '$stateProvider', ($stateProvider) => { @@ -25,9 +23,7 @@ export default angular }) .state('root.cm.exportVisits', { url: '/export/visits', - templateUrl: '/assets/wise5/classroomMonitor/dataExport/exportVisits.html', - controller: 'ExportVisitsController', - controllerAs: 'exportVisitsController' + component: 'exportStepVisits' }); } ]); diff --git a/src/assets/wise5/classroomMonitor/dataExport/export-step-visits/export-step-visits.component.html b/src/assets/wise5/classroomMonitor/dataExport/export-step-visits/export-step-visits.component.html new file mode 100644 index 00000000000..df8ed68ae74 --- /dev/null +++ b/src/assets/wise5/classroomMonitor/dataExport/export-step-visits/export-step-visits.component.html @@ -0,0 +1,88 @@ +
+
+
+ +
Export Step Visit Events
+
+ +
+
+ + + + + + + + + +
Column NameExplanation
{{ columnExplanation.name }}{{ columnExplanation.explanation }}
+
+ +
+
+ +
Include Student Names
+
+
Choose Steps
+
+ + + +
+ +
+ +
{{ idToStepNumberAndTitle[value.node.id] }}
+
+
+
+
+ +
Include Deleted Steps (If Any)
+
+
+
+ +
+ +
+
+
+
diff --git a/src/assets/wise5/classroomMonitor/dataExport/export-step-visits/export-step-visits.component.scss b/src/assets/wise5/classroomMonitor/dataExport/export-step-visits/export-step-visits.component.scss new file mode 100644 index 00000000000..eb4a7966bbd --- /dev/null +++ b/src/assets/wise5/classroomMonitor/dataExport/export-step-visits/export-step-visits.component.scss @@ -0,0 +1,48 @@ +.main-div { + margin-top: 20px; + margin-left: 20px; +} + +.group-header { + margin-left: 20px; + margin-bottom: 16px; +} + +.step-header { + margin-left: 40px; + margin-bottom: 16px; +} + +table, tr, th, td { + border: 1px solid black; +} + +th, td { + padding: 10px; +} + +.top-explanations-buttons { + margin-bottom: 10px; +} + +.bottom-explanations-buttons { + margin-top: 10px; +} + +.explanation-column-name { + width: 200px; +} + +.include-student-names { + margin-left: 10px; + margin-top: 10px; +} + +.select-all-buttons { + margin-bottom: 20px; +} + +.checkbox-label { + cursor: pointer; + display: inline; +} \ No newline at end of file diff --git a/src/assets/wise5/classroomMonitor/dataExport/export-step-visits/export-step-visits.component.spec.ts b/src/assets/wise5/classroomMonitor/dataExport/export-step-visits/export-step-visits.component.spec.ts new file mode 100644 index 00000000000..54792e9e92d --- /dev/null +++ b/src/assets/wise5/classroomMonitor/dataExport/export-step-visits/export-step-visits.component.spec.ts @@ -0,0 +1,117 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { FormsModule } from '@angular/forms'; +import { MatCheckboxModule } from '@angular/material/checkbox'; +import { MatIconModule } from '@angular/material/icon'; +import { ConfigService } from '../../../services/configService'; +import { DataExportService } from '../../../services/dataExportService'; +import { TeacherProjectService } from '../../../services/teacherProjectService'; +import { ClassroomMonitorTestingModule } from '../../classroom-monitor-testing.module'; +import { ExportStepVisitsComponent } from './export-step-visits.component'; + +let component: ExportStepVisitsComponent; +let fixture: ComponentFixture; +const group0Id = 'group0'; +const group1Id = 'group1'; +const node1Id = 'node1'; +const node2Id = 'node2'; +const node3Id = 'node3'; +const allIds = [group0Id, group1Id, node1Id, node2Id, node3Id]; + +const group0 = createGroupNode(group0Id, [group1Id]); +const group1 = createGroupNode(group1Id, [node1Id, node2Id, node3Id]); +const node1 = createStepNode(node1Id); +const node2 = createStepNode(node2Id); +const node3 = createStepNode(node3Id); + +describe('ExportStepVisitsComponent', () => { + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ExportStepVisitsComponent], + imports: [ClassroomMonitorTestingModule, FormsModule, MatCheckboxModule, MatIconModule], + providers: [DataExportService] + }).compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(ExportStepVisitsComponent); + component = fixture.componentInstance; + spyOn(TestBed.inject(ConfigService), 'getPermissions').and.returnValue({ + canAuthorProject: true, + canGradeStudentWork: true, + canViewStudentNames: true + }); + spyOn(TestBed.inject(TeacherProjectService), 'getNodeOrderOfProject').and.returnValue({ + nodes: [{ node: group0 }, { node: group1 }, { node: node1 }, { node: node2 }, { node: node3 }] + }); + fixture.detectChanges(); + }); + + selectAll(); + deselectAll(); + nodeChecked(); +}); + +function createGroupNode(id: string, ids: string[]) { + return { id: id, type: 'group', ids: ids }; +} + +function createStepNode(id: string) { + return { id: id, type: 'node' }; +} + +function selectAll() { + describe('selectAll', () => { + it('should select all', () => { + setIdToCheckedForAll(component.idToChecked, false); + component.selectAll(); + expectIdToCheckedForAll(component.idToChecked, true); + }); + }); +} + +function setIdToCheckedForAll(idToChecked: any, value: boolean) { + for (const id of allIds) { + idToChecked[id] = value; + } +} + +function expectIdToCheckedForAll(idToChecked: any, value: boolean) { + for (const id of allIds) { + expect(idToChecked[id]).toEqual(value); + } +} + +function deselectAll() { + describe('deselectAll', () => { + it('should deselect all', () => { + setIdToCheckedForAll(component.idToChecked, true); + component.deselectAll(); + expectIdToCheckedForAll(component.idToChecked, false); + }); + }); +} + +function nodeChecked() { + describe('nodeChecked', () => { + it('should check a group node', () => { + setIdToCheckedForAll(component.idToChecked, false); + component.idToChecked[group1Id] = true; + component.nodeChecked(group1); + expect(component.idToChecked[group0Id]).toEqual(false); + expect(component.idToChecked[group1Id]).toEqual(true); + expect(component.idToChecked[node1Id]).toEqual(true); + expect(component.idToChecked[node2Id]).toEqual(true); + expect(component.idToChecked[node3Id]).toEqual(true); + }); + it('should check a step node', () => { + setIdToCheckedForAll(component.idToChecked, false); + component.idToChecked[node1Id] = true; + component.nodeChecked(node1); + expect(component.idToChecked[group0Id]).toEqual(false); + expect(component.idToChecked[group1Id]).toEqual(false); + expect(component.idToChecked[node1Id]).toEqual(true); + expect(component.idToChecked[node2Id]).toEqual(false); + expect(component.idToChecked[node3Id]).toEqual(false); + }); + }); +} diff --git a/src/assets/wise5/classroomMonitor/dataExport/exportVisitsController.ts b/src/assets/wise5/classroomMonitor/dataExport/export-step-visits/export-step-visits.component.ts similarity index 54% rename from src/assets/wise5/classroomMonitor/dataExport/exportVisitsController.ts rename to src/assets/wise5/classroomMonitor/dataExport/export-step-visits/export-step-visits.component.ts index bf6e6a48883..57301a11a8a 100644 --- a/src/assets/wise5/classroomMonitor/dataExport/exportVisitsController.ts +++ b/src/assets/wise5/classroomMonitor/dataExport/export-step-visits/export-step-visits.component.ts @@ -1,17 +1,21 @@ -'use strict'; - -import { TeacherDataService } from '../../services/teacherDataService'; -import ExportController from './exportController'; -import { ConfigService } from '../../services/configService'; -import { UtilService } from '../../services/utilService'; -import { TeacherProjectService } from '../../services/teacherProjectService'; -import { DataExportService } from '../../services/dataExportService'; - -class ExportVisitsController extends ExportController { +import { Component } from '@angular/core'; +import { ConfigService } from '../../../services/configService'; +import { DataExportService } from '../../../services/dataExportService'; +import { TeacherProjectService } from '../../../services/teacherProjectService'; +import { UtilService } from '../../../services/utilService'; +import ExportController from '../exportController'; +import { UpgradeModule } from '@angular/upgrade/static'; + +@Component({ + selector: 'export-step-visits', + templateUrl: './export-step-visits.component.html', + styleUrls: ['./export-step-visits.component.scss'] +}) +export class ExportStepVisitsComponent extends ExportController { project: any; nodes: any[]; checkedItems: string[] = []; - columnNames: any[]; + columnNames: string[]; columnNameToColumnNumber: any = {}; idToChecked: any = {}; idToNode: any = {}; @@ -27,31 +31,20 @@ class ExportVisitsController extends ExportController { includeDeletedSteps: boolean = true; deletedSteps: any = {}; - static $inject = [ - '$filter', - '$state', - 'ConfigService', - 'DataExportService', - 'ProjectService', - 'FileSaver', - 'TeacherDataService', - 'UtilService' - ]; - constructor( - private $filter: any, - private $state: any, - private ConfigService: ConfigService, - private DataExportService: DataExportService, - private ProjectService: TeacherProjectService, - FileSaver: any, - private TeacherDataService: TeacherDataService, - private UtilService: UtilService + private configService: ConfigService, + private dataExportService: DataExportService, + private projectService: TeacherProjectService, + private upgrade: UpgradeModule, + private utilService: UtilService ) { - super($filter, FileSaver); - this.project = this.ProjectService.project; - this.canViewStudentNames = this.ConfigService.getPermissions().canViewStudentNames; - let nodeOrderOfProject = this.ProjectService.getNodeOrderOfProject(this.project); + super(); + } + + ngOnInit(): void { + this.project = this.projectService.project; + this.canViewStudentNames = this.configService.getPermissions().canViewStudentNames; + const nodeOrderOfProject = this.projectService.getNodeOrderOfProject(this.project); this.nodes = nodeOrderOfProject.nodes; this.initializeIdToChecked(this.nodes); this.initializeIdToNode(this.nodes); @@ -61,24 +54,24 @@ class ExportVisitsController extends ExportController { this.initializeIdToUserInfo(); } - initializeIdToChecked(nodes: any[]) { + private initializeIdToChecked(nodes: any[]): void { for (const node of nodes) { this.idToChecked[node.node.id] = true; } this.includeDeletedSteps = true; } - initializeIdToNode(nodes: any[]) { + private initializeIdToNode(nodes: any[]): void { for (const node of nodes) { const nodeId = node.node.id; this.idToNode[nodeId] = node; - this.idToStepNumber[nodeId] = this.ProjectService.getNodePositionById(nodeId); - this.idToStepNumberAndTitle[nodeId] = this.ProjectService.getNodePositionAndTitle(nodeId); + this.idToStepNumber[nodeId] = this.projectService.getNodePositionById(nodeId); + this.idToStepNumberAndTitle[nodeId] = this.projectService.getNodePositionAndTitle(nodeId); } } - initializeWorkgroupIdNodeIdToVisitCounter(nodes: any[]) { - const workgroupIds = this.ConfigService.getClassmateWorkgroupIds(); + private initializeWorkgroupIdNodeIdToVisitCounter(nodes: any[]): void { + const workgroupIds = this.configService.getClassmateWorkgroupIds(); for (const workgroupId of workgroupIds) { for (const node of nodes) { const key = this.getWorkgroupIdNodeIdKey(workgroupId, node.node.id); @@ -91,7 +84,7 @@ class ExportVisitsController extends ExportController { } } - initializeColumnNames() { + private initializeColumnNames(): void { this.columnNames = [ '#', 'Workgroup ID', @@ -122,85 +115,124 @@ class ExportVisitsController extends ExportController { ]; } - initializeColumnExplanations() { + private initializeColumnExplanations(): void { this.columnExplanations = [ - { name: '#', explanation: this.$translate('columnExplanationRowNumber') }, - { name: 'Workgroup ID', explanation: this.$translate('columnExplanationWorkgroupID') }, - { name: 'User ID 1', explanation: this.$translate('columnExplanationUserID1') }, - { name: 'Student Name 1', explanation: this.$translate('columnExplanationStudentName1') }, - { name: 'User ID 2', explanation: this.$translate('columnExplanationUserID2') }, - { name: 'Student Name 2', explanation: this.$translate('columnExplanationStudentName2') }, - { name: 'User ID 3', explanation: this.$translate('columnExplanationUserID3') }, - { name: 'Student Name 3', explanation: this.$translate('columnExplanationStudentName3') }, - { name: 'Run ID', explanation: this.$translate('columnExplanationRunID') }, - { name: 'Project ID', explanation: this.$translate('columnExplanationProjectID') }, - { name: 'Project Name', explanation: this.$translate('columnExplanationProjectName') }, - { name: 'Period ID', explanation: this.$translate('columnExplanationPeriodID') }, - { name: 'Period Name', explanation: this.$translate('columnExplanationPeriodName') }, - { name: 'Start Date', explanation: this.$translate('columnExplanationStartDate') }, - { name: 'End Date', explanation: this.$translate('columnExplanationEndDate') }, - { name: 'Node ID', explanation: this.$translate('columnExplanationNodeID') }, - { name: 'Step Title', explanation: this.$translate('columnExplanationStepTitle') }, - { name: 'Enter Time', explanation: this.$translate('columnExplanationEnterTime') }, - { name: 'Exit Time', explanation: this.$translate('columnExplanationExitTime') }, + { name: '#', explanation: $localize`The row number.` }, + { name: 'Workgroup ID', explanation: $localize`The ID of the group.` }, + { + name: 'User ID 1', + explanation: $localize`The User ID of the first student in the group. This ID follows the student for all runs.` + }, + { + name: 'Student Name 1', + explanation: $localize`The name of the first student. This only shows up if you have permission to view the student names and you enabled the 'Include Student Names' checkbox.` + }, + { + name: 'User ID 2', + explanation: $localize`The UserID of the second student in the group. This ID follows the student for all runs.` + }, + { + name: 'Student Name 2', + explanation: $localize`The name of the second student. This only shows up if you have permission to view the student names and you enabled the 'Include Student Names' checkbox.` + }, + { + name: 'User ID 3', + explanation: $localize`The User ID of the third student in the group. This ID follows the student for all runs.` + }, + { + name: 'Student Name 3', + explanation: $localize`The name of the third student. This only shows up if you have permission to view the student names and you enabled the 'Include Student Names' checkbox.` + }, + { name: 'Run ID', explanation: $localize`The ID of the run.` }, + { name: 'Project ID', explanation: $localize`The ID of the project.` }, + { name: 'Project Name', explanation: $localize`The name of the project.` }, + { name: 'Period ID', explanation: $localize`The ID of the period that this student is in.` }, + { + name: 'Period Name', + explanation: $localize`The period name that this student is in. This name is chosen by the teacher that created the run.` + }, + { name: 'Start Date', explanation: $localize`The start date of the run.` }, + { name: 'End Date', explanation: $localize`The end date of the run.` }, + { + name: 'Node ID', + explanation: $localize`The ID of the step. Each step in a unit has a unique Node ID.` + }, + { name: 'Step Title', explanation: $localize`The title of the step.` }, + { + name: 'Enter Time', + explanation: $localize`The timestamp when the student entered the step.` + }, + { + name: 'Exit Time', + explanation: $localize`The timestamp when the student exited the step. This value can be empty if WISE did not get the chance to save a step exit event. This can happen if the student closes their laptop without signing out of WISE or if they refresh the WISE page.` + }, { name: 'Visit Duration (Seconds)', - explanation: this.$translate('columnExplanationVisitDurationSeconds') + explanation: $localize`The amount of time the student spent on the step during this visit measured in seconds.` + }, + { + name: 'Visit Counter', + explanation: $localize`The number of times the student has visited this step so far.` + }, + { + name: 'Revisit Counter', + explanation: $localize`The number of times the student has revisited this step so far. This will always be 1 less than the 'Visit Counter' for a given visit.` + }, + { + name: 'Previous Node ID', + explanation: $localize`The Node ID of the step the student was on before visiting this step.` }, - { name: 'Visit Counter', explanation: this.$translate('columnExplanationVisitCounter') }, - { name: 'Revisit Counter', explanation: this.$translate('columnExplanationRevisitCounter') }, - { name: 'Previous Node ID', explanation: this.$translate('columnExplanationPreviousNodeID') }, { name: 'Previous Step Title', - explanation: this.$translate('columnExplanationPreviousStepTitle') + explanation: $localize`The step title of the step the student was on before visiting this step.` }, { name: 'Node IDs Since Last Visit', - explanation: this.$translate('columnExplanationNodeIDsSinceLastVisit') + explanation: $localize`A list of Node IDs that contain the steps the student visited before revisiting this step. This cell will only contain values if they revisit a step. For example if the student navigated to node1, then node2, then node3, then node1. For the second visit to node1, the 'Node IDs Since Last Visit' will show node2, node3.` }, { name: 'Steps Since Last Visit', - explanation: this.$translate('columnExplanationStepsSinceLastVisit') + explanation: $localize`A list of step numbers that contain the steps the student visited before revisiting this step. This cell will only contain values if they revisit a step. For example if the student navigated to 1.1, then 1.2, then 1.3, then 1.1. For the second visit to 1.1, the 'Steps Since Last Visit' will show 1.2, 1.3.` } ]; } - initializeIdToUserInfo() { - const workgroupIds = this.ConfigService.getClassmateWorkgroupIds(); + private initializeIdToUserInfo(): void { + const workgroupIds = this.configService.getClassmateWorkgroupIds(); for (const workgroupId of workgroupIds) { - this.idToUserInfo[workgroupId] = this.ConfigService.getUserInfoByWorkgroupId(workgroupId); + this.idToUserInfo[workgroupId] = this.configService.getUserInfoByWorkgroupId(workgroupId); } } - isActiveWorkgroup(workgroupId) { + private isActiveWorkgroup(workgroupId: any): boolean { return this.idToUserInfo[workgroupId] != null; } - getHeaderRow() { + private getHeaderRow(): string[] { return this.columnNames; } - initializeColumnNameToColumnNumber() { + private initializeColumnNameToColumnNumber(): void { for (let c = 0; c < this.columnNames.length; c++) { this.columnNameToColumnNumber[this.columnNames[c]] = c; } } - selectAll() { + selectAll(): void { for (const node of this.nodes) { this.idToChecked[node.node.id] = true; } this.includeDeletedSteps = true; } - deselectAll() { + deselectAll(): void { for (const node of this.nodes) { this.idToChecked[node.node.id] = false; } this.includeDeletedSteps = false; } - nodeChecked(node: any) { + nodeChecked(node: any): void { if (node.type === 'group') { const isGroupChecked = this.idToChecked[node.id]; for (const childId of node.ids) { @@ -209,25 +241,23 @@ class ExportVisitsController extends ExportController { } } - goBack() { - this.$state.go('root.cm.export'); + goBack(): void { + this.upgrade.$injector.get('$state').go('root.cm.export'); } - export() { + export(): void { this.rowCounter = 1; this.checkedItems = this.getCheckedItems(); const includeStudentEvents = true; const includeTeacherEvents = false; - this.DataExportService.retrieveEventsExport( - includeStudentEvents, - includeTeacherEvents, - this.includeStudentNames - ).then((events: any) => { - this.handleExportCallback(events); - }); + this.dataExportService + .retrieveEventsExport(includeStudentEvents, includeTeacherEvents, this.includeStudentNames) + .then((events: any) => { + this.handleExportCallback(events); + }); } - getCheckedItems() { + private getCheckedItems(): string[] { const checkedItems = []; for (const node of this.nodes) { if (this.idToChecked[node.node.id]) { @@ -237,7 +267,7 @@ class ExportVisitsController extends ExportController { return checkedItems; } - handleExportCallback(events: any[]) { + private handleExportCallback(events: any[]): void { let sortedEvents = this.sortEvents(events); this.deletedSteps = this.getDeletedSteps(sortedEvents); sortedEvents = this.cleanEvents(sortedEvents); @@ -262,11 +292,11 @@ class ExportVisitsController extends ExportController { } rows = this.filterRows(rows); rows.unshift(this.getHeaderRow()); - const fileName = `${this.ConfigService.getRunId()}_visits.csv`; + const fileName = `${this.configService.getRunId()}_visits.csv`; this.generateCSVFile(rows, fileName); } - cleanEvents(events: any[]) { + private cleanEvents(events: any[]): any[] { let cleanedEvents = []; cleanedEvents = this.getNodeEnteredAndExitedEvents(events); cleanedEvents = this.getEventsWithActiveWorkgroups(cleanedEvents); @@ -274,7 +304,7 @@ class ExportVisitsController extends ExportController { return cleanedEvents; } - getNodeEnteredAndExitedEvents(events: any[]) { + private getNodeEnteredAndExitedEvents(events: any[]): any[] { const cleanedEvents = []; for (const event of events) { if (this.isStepEnteredEvent(event) || this.isStepExitedEvent(event)) { @@ -284,7 +314,7 @@ class ExportVisitsController extends ExportController { return cleanedEvents; } - getEventsWithActiveWorkgroups(events: any[]) { + private getEventsWithActiveWorkgroups(events: any[]): any[] { const cleanedEvents = []; for (const event of events) { if (this.isActiveWorkgroup(event.workgroupId)) { @@ -294,7 +324,7 @@ class ExportVisitsController extends ExportController { return cleanedEvents; } - getEventsThatAreNotErroneous(events: any[]) { + private getEventsThatAreNotErroneous(events: any[]): any[] { const cleanedEvents = []; events.forEach((event, index) => { if (events[index + 1] == null || !this.isErroneousExitedEvent(event, events[index + 1])) { @@ -304,7 +334,7 @@ class ExportVisitsController extends ExportController { return cleanedEvents; } - isErroneousExitedEvent(event: any, nextEvent: any) { + private isErroneousExitedEvent(event: any, nextEvent: any): any { return ( this.isStepExitedEvent(event) && this.isStepExitedEvent(nextEvent) && @@ -312,13 +342,13 @@ class ExportVisitsController extends ExportController { ); } - getDeletedSteps(events: any[]) { + private getDeletedSteps(events: any[]): any { const deletedSteps = {}; for (const event of events) { const nodeId = event.nodeId; if ( nodeId != null && - this.ProjectService.getNodeById(nodeId) == null && + this.projectService.getNodeById(nodeId) == null && nodeId.startsWith('node') ) { deletedSteps[event.nodeId] = true; @@ -327,11 +357,11 @@ class ExportVisitsController extends ExportController { return deletedSteps; } - isDeletedStep(nodeId) { + private isDeletedStep(nodeId: string): boolean { return this.deletedSteps[nodeId] != null; } - filterRows(rows: any[]) { + private filterRows(rows: any[]): any[] { return rows.filter((row) => { const nodeId = this.getCellInRow(row, 'Node ID'); return ( @@ -341,11 +371,11 @@ class ExportVisitsController extends ExportController { }); } - sortEvents(events: any[]) { + private sortEvents(events: any[]): any[] { return events.sort(this.sortEventsByWorkgroupIdAndClientSaveTime); } - sortEventsByWorkgroupIdAndClientSaveTime(a: any, b: any) { + private sortEventsByWorkgroupIdAndClientSaveTime(a: any, b: any): number { if (a.workgroupId < b.workgroupId) { return -1; } else if (a.workgroupId > b.workgroupId) { @@ -359,23 +389,19 @@ class ExportVisitsController extends ExportController { } } - isStepEnteredEvent(event: any) { + private isStepEnteredEvent(event: any): boolean { return event.event === 'nodeEntered' && event.nodeId.startsWith('node'); } - isStepExitedEvent(event: any) { + private isStepExitedEvent(event: any): boolean { return event.event === 'nodeExited' && event.nodeId.startsWith('node'); } - isMatchingWorkgroupId(nodeEnteredEvent: any, nodeExitedEvent: any) { - return nodeEnteredEvent.workgroupId === nodeExitedEvent.workgroupId; - } - - isMatchingNodeId(nodeEnteredEvent: any, nodeExitedEvent: any) { + private isMatchingNodeId(nodeEnteredEvent: any, nodeExitedEvent: any): boolean { return nodeEnteredEvent.nodeId === nodeExitedEvent.nodeId; } - createVisit(nodeEnteredEvent: any, nodeExitedEvent: any, previousVisits: any[]) { + private createVisit(nodeEnteredEvent: any, nodeExitedEvent: any, previousVisits: any[]): any { const visit = this.createRowWithEmptyCells(); const workgroupId = nodeEnteredEvent.workgroupId; const nodeId = nodeEnteredEvent.nodeId; @@ -383,19 +409,19 @@ class ExportVisitsController extends ExportController { this.setCellInRow(visit, '#', this.getNextRowNumber()); this.setCellInRow(visit, 'Workgroup ID', workgroupId); this.addUserCells(visit, workgroupId); - this.setCellInRow(visit, 'Project ID', this.ConfigService.getProjectId()); - this.setCellInRow(visit, 'Run ID', this.ConfigService.getRunId()); - this.setCellInRow(visit, 'Project Name', this.ConfigService.getRunName()); + this.setCellInRow(visit, 'Project ID', this.configService.getProjectId()); + this.setCellInRow(visit, 'Run ID', this.configService.getRunId()); + this.setCellInRow(visit, 'Project Name', this.configService.getRunName()); this.setCellInRow(visit, 'Period ID', this.getPeriodId(workgroupId)); this.setCellInRow(visit, 'Period Name', this.getPeriodName(workgroupId)); - this.setCellInRow(visit, 'Start Date', this.ConfigService.getFormattedStartDate()); - this.setCellInRow(visit, 'End Date', this.ConfigService.getFormattedEndDate()); + this.setCellInRow(visit, 'Start Date', this.configService.getFormattedStartDate()); + this.setCellInRow(visit, 'End Date', this.configService.getFormattedEndDate()); this.setCellInRow(visit, 'Node ID', nodeId); this.setCellInRow(visit, 'Step Title', this.getStepNumberAndTitle(nodeId)); this.setCellInRow( visit, 'Enter Time', - this.UtilService.convertMillisecondsToFormattedDateTime(nodeEnteredEvent.clientSaveTime) + this.utilService.convertMillisecondsToFormattedDateTime(nodeEnteredEvent.clientSaveTime) ); if (nodeExitedEvent == null) { this.setCellInRow(visit, 'Exit Time', '(Unknown Exit Time)'); @@ -404,7 +430,7 @@ class ExportVisitsController extends ExportController { this.setCellInRow( visit, 'Exit Time', - this.UtilService.convertMillisecondsToFormattedDateTime(nodeExitedEvent.clientSaveTime) + this.utilService.convertMillisecondsToFormattedDateTime(nodeExitedEvent.clientSaveTime) ); this.setCellInRow( visit, @@ -440,11 +466,11 @@ class ExportVisitsController extends ExportController { return visit; } - createRowWithEmptyCells() { + private createRowWithEmptyCells(): any { return new Array(this.columnNames.length); } - getPreviousVisit(previousVisits: any[], workgroupId: number) { + private getPreviousVisit(previousVisits: any[], workgroupId: number): any { if (previousVisits.length > 0) { const previousVisit = previousVisits[previousVisits.length - 1]; if (this.getCellInRow(previousVisit, 'Workgroup ID') == workgroupId) { @@ -454,15 +480,15 @@ class ExportVisitsController extends ExportController { return null; } - getNodeIdsBetweenLastVisit(nodeId: string, previousVisits: any[]) { + private getNodeIdsBetweenLastVisit(nodeId: string, previousVisits: any[]): string { return this.getStepsBetweenLastVisit(nodeId, previousVisits, 'nodeId'); } - getStepNumbersBetweenLastVisit(nodeId: string, previousVisits: any[]) { + private getStepNumbersBetweenLastVisit(nodeId: string, previousVisits: any[]): string { return this.getStepsBetweenLastVisit(nodeId, previousVisits, 'stepNumber'); } - getStepsBetweenLastVisit(nodeId: string, previousVisits: any[], output: string) { + private getStepsBetweenLastVisit(nodeId: string, previousVisits: any[], output: string): string { const steps = []; for (let v = previousVisits.length - 1; v > 0; v--) { const previousNodeId = this.getCellInRow(previousVisits[v], 'Node ID'); @@ -479,45 +505,45 @@ class ExportVisitsController extends ExportController { return steps.join(', '); } - addUserCells(row: any[], workgroupId: number) { - const userInfo = this.ConfigService.getUserInfoByWorkgroupId(workgroupId); + private addUserCells(row: any[], workgroupId: number): void { + const userInfo = this.configService.getUserInfoByWorkgroupId(workgroupId); for (let u = 0; u < userInfo.users.length; u++) { this.addSingleUserCells(row, u + 1, userInfo.users[u]); } } - addSingleUserCells(row: any[], studentNumber: number, user: any) { + private addSingleUserCells(row: any[], studentNumber: number, user: any): void { this.setCellInRow(row, `User ID ${studentNumber}`, user.id); if (this.includeStudentNames) { this.setCellInRow(row, `Student Name ${studentNumber}`, user.name); } } - getPeriodName(workgroupId: number) { - return this.ConfigService.getUserInfoByWorkgroupId(workgroupId).periodName; + private getPeriodName(workgroupId: number): string { + return this.configService.getUserInfoByWorkgroupId(workgroupId).periodName; } - getPeriodId(workgroupId: number) { - return this.ConfigService.getUserInfoByWorkgroupId(workgroupId).periodId; + private getPeriodId(workgroupId: number): number { + return this.configService.getUserInfoByWorkgroupId(workgroupId).periodId; } - getVisitDuration(nodeEnteredEvent: any, nodeExitedEvent: any) { + private getVisitDuration(nodeEnteredEvent: any, nodeExitedEvent: any): number { return (nodeExitedEvent.clientSaveTime - nodeEnteredEvent.clientSaveTime) / 1000; } - getColumnNumber(columnName: string) { + private getColumnNumber(columnName: string): number { return this.columnNameToColumnNumber[columnName]; } - getNextRowNumber() { + private getNextRowNumber(): number { return this.rowCounter; } - incrementRowCounter() { + private incrementRowCounter(): void { this.rowCounter++; } - getStepNumber(nodeId: string) { + private getStepNumber(nodeId: string): any { if (this.isDeletedStep(nodeId)) { return '(Deleted Step)'; } else { @@ -525,7 +551,7 @@ class ExportVisitsController extends ExportController { } } - getStepNumberAndTitle(nodeId: string) { + private getStepNumberAndTitle(nodeId: string): string { if (this.isDeletedStep(nodeId)) { return '(Deleted Step)'; } else { @@ -533,38 +559,36 @@ class ExportVisitsController extends ExportController { } } - getWorkgroupIdNodeIdKey(workgroupId: number, nodeId: string) { + private getWorkgroupIdNodeIdKey(workgroupId: number, nodeId: string): string { return `${workgroupId}-${nodeId}`; } - incrementVisitCounter(workgroupId: number, nodeId: string) { + private incrementVisitCounter(workgroupId: number, nodeId: string): void { this.workgroupIdNodeIdToVisitCounter[this.getWorkgroupIdNodeIdKey(workgroupId, nodeId)]++; } - getVisitCounter(workgroupId: number, nodeId: string) { + private getVisitCounter(workgroupId: number, nodeId: string): number { return this.workgroupIdNodeIdToVisitCounter[this.getWorkgroupIdNodeIdKey(workgroupId, nodeId)]; } - getRevisitCounter(workgroupId: number, nodeId: string) { + private getRevisitCounter(workgroupId: number, nodeId: string): number { const key = this.getWorkgroupIdNodeIdKey(workgroupId, nodeId); return this.workgroupIdNodeIdToVisitCounter[key] - 1; } - setCellInRow(row: any[], columnName: string, value: any) { + private setCellInRow(row: any[], columnName: string, value: any): void { row[this.getColumnNumber(columnName)] = value; } - getCellInRow(row: any[], columnName: string) { + private getCellInRow(row: any[], columnName: string): any { return row[this.getColumnNumber(columnName)]; } - toggleColumnExplanations() { + toggleColumnExplanations(): void { this.isShowColumnExplanations = !this.isShowColumnExplanations; } - backToTop() { + backToTop(): void { window.document.querySelector('.top-content').scrollIntoView(); } } - -export default ExportVisitsController; diff --git a/src/assets/wise5/classroomMonitor/dataExport/exportController.ts b/src/assets/wise5/classroomMonitor/dataExport/exportController.ts index eccf6c506c3..c1a1795ace8 100644 --- a/src/assets/wise5/classroomMonitor/dataExport/exportController.ts +++ b/src/assets/wise5/classroomMonitor/dataExport/exportController.ts @@ -1,14 +1,11 @@ 'use strict'; +import * as FileSaver from 'file-saver'; + class ExportController { - $translate: any; maxExcelCellSize: number = 32767; - static $inject = ['$filter', 'FileSaver']; - - constructor($filter, private FileSaver: any) { - this.$translate = $filter('translate'); - } + constructor() {} /** * Generate the csv file and have the client download it @@ -16,13 +13,13 @@ class ExportController { * inner array contains strings or numbers which represent the cell values in the export. * @param fileName the name of the file that will be generated */ - generateCSVFile(rows: any[], fileName: string) { + generateCSVFile(rows: any[], fileName: string): void { const csvString = this.generateCSVString(rows); const csvBlob = new Blob([csvString], { type: 'text/csv' }); - this.FileSaver.saveAs(csvBlob, fileName); + FileSaver.saveAs(csvBlob, fileName); } - generateCSVString(rows: any[]) { + private generateCSVString(rows: any[]): string { let csvString = ''; for (const row of rows) { csvString += this.createCSVRow(row); @@ -30,7 +27,7 @@ class ExportController { return csvString; } - createCSVRow(row: any[]) { + private createCSVRow(row: any[]): string { let csvString = ''; for (const cell of row) { csvString += `${this.createCSVCell(cell)},`; @@ -39,7 +36,7 @@ class ExportController { return csvString; } - createCSVCell(cell: any) { + private createCSVCell(cell: any): string { let csvString = ''; if (this.isEmpty(cell)) { csvString = ' '; @@ -58,23 +55,23 @@ class ExportController { return csvString; } - isEmpty(data: any) { + private isEmpty(data: any): boolean { return data == null || data === '' || typeof data === 'undefined'; } - isObject(data: any) { + private isObject(data: any): boolean { return typeof data === 'object'; } - isString(data: any) { + private isString(data: any): boolean { return typeof data === 'string'; } - wrapInDoubleQuotes(str: string) { + private wrapInDoubleQuotes(str: string): string { return `"${str}"`; } - isStringTooLarge(str: string) { + private isStringTooLarge(str: string): boolean { return str.length >= this.maxExcelCellSize; } } diff --git a/src/assets/wise5/classroomMonitor/dataExport/exportVisits.html b/src/assets/wise5/classroomMonitor/dataExport/exportVisits.html deleted file mode 100644 index bbf080111c1..00000000000 --- a/src/assets/wise5/classroomMonitor/dataExport/exportVisits.html +++ /dev/null @@ -1,108 +0,0 @@ - -
-
-
-
- - arrow_back - - {{ ::'back' | translate }} - - -
-
{{ ::'exportStepVisitEvents' | translate }}
-
- - {{exportVisitsController.isShowColumnExplanations ? ('hideColumnExplanations' | translate) : ('showColumnExplanations' | translate)}} - -
-
- - - - - - - - - -
{{ ::'columnName' | translate }}{{ ::'explanation' | translate }}
{{columnExplanation.name}}{{columnExplanation.explanation}}
-
- - {{exportVisitsController.isShowColumnExplanations ? ('hideColumnExplanations' | translate) : ('showColumnExplanations' | translate)}} - -
-
- - {{ ::'includeStudentNames' | translate }} - -
{{ ::'chooseSteps' | translate }}
-
- {{ ::'selectAll' | translate }} - {{ ::'deselectAll' | translate }} - - file_download - - {{ ::'download' | translate }} - - -
-
- -
{{exportVisitsController.idToStepNumberAndTitle[value.node.id]}}
-
-
-
- -
{{ ::'includeDeletedStepsIfAny' | translate }}
-
-
-
-
- - file_download - - {{ ::'download' | translate }} - - - - arrow_upward - - {{ ::'backToTop' | translate }} - - -
-
-
-
-
\ No newline at end of file diff --git a/src/assets/wise5/test-unit/controllers/dataExport/exportVisitsController.spec.js b/src/assets/wise5/test-unit/controllers/dataExport/exportVisitsController.spec.js deleted file mode 100644 index 581fe5c8275..00000000000 --- a/src/assets/wise5/test-unit/controllers/dataExport/exportVisitsController.spec.js +++ /dev/null @@ -1,1042 +0,0 @@ -import classroomMonitorModule from '../../../classroomMonitor/classroomMonitor'; - -let $controller; -let $q; -let $rootScope; -let $scope; -let exportVisitsController; -let ConfigService; -let ProjectService; -let TeacherDataService; -let demoProjectJSON; -const demoProjectJSONOriginal = window.mocks['test-unit/sampleData/curriculum/DemoProject/project']; - -describe('ExportVisitsController', () => { - beforeEach(angular.mock.module(classroomMonitorModule.name)); - beforeEach(inject(( - _ConfigService_, - _ProjectService_, - _TeacherDataService_, - _$controller_, - _$q_, - _$rootScope_ - ) => { - $controller = _$controller_; - $q = _$q_; - $rootScope = _$rootScope_; - $scope = $rootScope.$new(); - ConfigService = _ConfigService_; - ProjectService = _ProjectService_; - TeacherDataService = _TeacherDataService_; - spyOn(ConfigService, 'getPermissions').and.returnValue({ canViewStudentNames: true }); - spyOn(ConfigService, 'getClassmateWorkgroupIds').and.returnValue([100, 101]); - spyOn(ConfigService, 'getProjectId').and.returnValue(1000); - spyOn(ConfigService, 'getRunId').and.returnValue(2000); - spyOn(ConfigService, 'getRunName').and.returnValue('Demo Project'); - spyOn(ConfigService, 'getFormattedStartDate').and.returnValue('Fri Apr 17 2020 6:22:14 PM'); - spyOn(ConfigService, 'getFormattedEndDate').and.returnValue('Thu May 21 2020 11:59:59 PM'); - const nodeOrderOfProject = createNodeOrderOfProject(); - spyOn(ProjectService, 'getNodeOrderOfProject').and.returnValue(nodeOrderOfProject); - demoProjectJSON = JSON.parse(JSON.stringify(demoProjectJSONOriginal)); - ProjectService.setProject(demoProjectJSON); - exportVisitsController = $controller('ExportVisitsController', { $scope: $scope }); - exportVisitsController.initializeWorkgroupIdNodeIdToVisitCounter(nodeOrderOfProject.nodes); - })); - initializeIdToChecked_ShouldSetMappingsToFalse(); - initializeIdToNode_ShouldSetMappings(); - initializeWorkgroupIdNodeIdToVisitCounter_ShouldSetCounters(); - getHeaderRow_ShouldGetArrayOfColumnNames(); - initializeColumnNameToColumnNumber_ShouldSetMappings(); - initializeIdToUserInfo_ShouldPopulateIdToUserInfo(); - selectAll_ShouldSetAllCheckedToTrue(); - selectAll_ShouldSetAllCheckedToFalse(); - nodeChecked_WhenSettingAGroupToTrue_ShouldSetAllChildrenToTrue(); - nodeChecked_WhenSettingAGroupToFalse_ShouldSetAllChildrenToFalse(); - getCheckedItems_ShouldGetAnArrayOfIds(); - export_ShouldRetrieveEvents(); - export_WhileIncludingStudentNames_ShouldRetrieveEventsWithStudentNames(); - handleExportCallback_WithMatchingEnterAndExitEvents_ShouldCreateRows(); - handleExportCallback_WithMissingExitEventAtBeginning_ShouldCreateRows(); - handleExportCallback_WithMissingExitEventAtEnd_ShouldCreateRows(); - getNodeEnteredAndExitedEvents_ShouldRemoveOtherEvents(); - getEventsWithActiveWorkgroups_ShouldRemoveEvents(); - getEventsThatAreNotErroneous_ShouldRemoveEvents(); - isErroneousExitedEvent_ShouldReturnTrue(); - isErroneousExitedEvent_ShouldReturnFalse(); - getDeletedSteps_ShouldReturnStepsNoLongerInProject(); - filterRows_ShouldTakeOutRowsThatAreNotSelected(); - filterRows_ShouldTakeOutRowsForDeletedSteps(); - filterRows_ShouldKeepRowsForDeletedSteps(); - sortEvents_ShouldOrderEventsByWorkgroupIdAndClientSaveTime(); - isStepEnteredEvent_WithNodeExitedEvent_ShouldReturnFalse(); - isStepEnteredEvent_WithGroupNode_ShouldReturnFalse(); - isStepEnteredEvent_WithStepNode_ShouldReturnTrue(); - isStepExitedEvent_WithNodeEnteredEvent_ShouldReturnFalse(); - isStepExitedEvent_WithAGroupNode_ShouldReturnFalse(); - isStepExitedEvent_WithAStepNode_ShouldReturnTrue(); - isMatchingWorkgroupId_WithNonMatchingWorkgroupIds_ShouldReturnFalse(); - isMatchingWorkgroupId_WithMatchingWorkgroupIds_ShouldReturnTrue(); - isMatchingNodeId_WithNonMatchingNodeIds_ShouldReturnFalse(); - isMatchingNodeId_WithMatchingNodeIds_ShouldReturnTrue(); - createVisit_WithEnterAndExitEvent_ShouldCreateAVisit(); - createVisit_WithNoPreviousVisits_ShouldCreateAVisit(); - createVisit_WithOnlyEnterEvent_ShouldCreateAVisit(); - getPreviousVisit_ShouldGetPreviousVisit(); - getPreviousVisit_ShouldReturnNullIfWorkgroupIdIsDifferent(); - createRowWithEmptyCells_ShouldReturnArrayWithEmptyValues(); - getNodeIdsBetweenLastVisit_ShouldReturnAStringOfStepNumbers(); - getStepNumbersBetweenLastVisit_ShouldReturnAStringOfStepNumbers(); - addUserCells_WithOneStudentNotIncludingNames_ShouldSetTheUserID(); - addUserCells_WithOneStudentIncludingNames_ShouldSetTheUserIDAndStudentName(); - addUserCells_WithMultipleStudentsIncludingNames_ShouldSetTheUserIDAndStudentNames(); - getVisitDuration_ShouldGetTheTimeDifferenceBetweenEventsInSeconds(); - getColumnNumber_ShouldReturnTheColumnNumber(); - incrementRowCounter_ShouldIncrementRowCounterBy1(); - getStepNumber_ShouldGetAStringContainingStepNumber(); - getStepNumber_ShouldGetAStringForADeletedStep(); - getStepNumberAndTitle_ShouldGetAStringContainingStepNumberAndTitle(); - getStepNumberAndTitle_ShouldGetAStringForADeletedStep(); - getWorkgroupIdNodeIdKey_ShouldGetAStringContainingWorkgroupIdAndNodeId(); - getStepNumber_ShouldGetTheStepNumberString(); - incrementVisitCounter_ShouldIncreaseTheCounterBy1(); - getVisitCounter_ShouldGetTheVisitCount(); - getRevisitCounter_ShouldGetTheRevisitCount(); - setCellInRow_ShouldSetTheValueInTheCell(); - getCellInRow_ShouldGetTheValueInTheCell(); -}); - -function createNodeOrderOfProject() { - return { - nodes: createNodes() - }; -} - -function createNodes() { - return [ - createNode('group0', 'group', 'Master', ''), - createNode('group1', 'group', 'Master', '1', [ - 'node1', - 'node2', - 'node3', - 'node4', - 'node5', - 'node6', - 'node7' - ]), - createNode('node1', 'node', 'HTML Step', '1.1'), - createNode('node2', 'node', 'Open Response Step', '1.2'), - createNode('node3', 'node', 'Graph Step', '1.3'), - createNode('node4', 'node', 'Table Step', '1.4'), - createNode('node5', 'node', 'Match Step', '1.5'), - createNode('node6', 'node', 'Multiple Choice Step', '1.6'), - createNode('node7', 'node', 'Draw Step', '1.7') - ]; -} - -function createNode(id, type, title, stepNumber, childIds) { - return { - node: { - id: id, - title: title, - type: type, - ids: childIds - }, - stepNumber: stepNumber - }; -} - -function createRow(nodeId, stepTitle = '', workgroupId) { - const row = exportVisitsController.createRowWithEmptyCells(); - exportVisitsController.setCellInRow(row, 'Node ID', nodeId); - exportVisitsController.setCellInRow(row, 'Step Title', stepTitle); - exportVisitsController.setCellInRow(row, 'Workgroup ID', workgroupId); - return row; -} - -function createEvent(type, nodeId, workgroupId, clientSaveTime) { - return { - event: type, - nodeId: nodeId, - workgroupId: workgroupId, - clientSaveTime: clientSaveTime - }; -} - -function expectIdsToCheckedToEqualValue(ids, value) { - for (const id of ids) { - expect(exportVisitsController.idToChecked[id]).toEqual(value); - } -} - -function expectVisitCountersToEqualValue(keys, value) { - for (const key of keys) { - expect(exportVisitsController.workgroupIdNodeIdToVisitCounter[key]).toEqual(value); - } -} - -function getColumnNameToNumberValue(columnName) { - return exportVisitsController.columnNameToColumnNumber[columnName]; -} - -function createResolvedPromise() { - const deferred = $q.defer(); - deferred.resolve({}); - return deferred.promise; -} - -function initializeIdToChecked_ShouldSetMappingsToFalse() { - it('initializeIdToChecked should set mappings to false', () => { - exportVisitsController.initializeIdToChecked(exportVisitsController.nodes); - expectIdsToCheckedToEqualValue( - ['group0', 'group1', 'node1', 'node2', 'node3', 'node4', 'node5', 'node6', 'node7'], - true - ); - }); -} - -function initializeIdToNode_ShouldSetMappings() { - it('initializeIdToNode should set mappings', () => { - exportVisitsController.initializeIdToNode(exportVisitsController.nodes); - expect(exportVisitsController.idToNode['group0']).toEqual( - createNode('group0', 'group', 'Master', '') - ); - expect(exportVisitsController.idToNode['group1']).toEqual( - createNode('group1', 'group', 'Master', '1', [ - 'node1', - 'node2', - 'node3', - 'node4', - 'node5', - 'node6', - 'node7' - ]) - ); - expect(exportVisitsController.idToNode['node1']).toEqual( - createNode('node1', 'node', 'HTML Step', '1.1') - ); - expect(exportVisitsController.idToNode['node2']).toEqual( - createNode('node2', 'node', 'Open Response Step', '1.2') - ); - expect(exportVisitsController.idToNode['node3']).toEqual( - createNode('node3', 'node', 'Graph Step', '1.3') - ); - expect(exportVisitsController.idToNode['node4']).toEqual( - createNode('node4', 'node', 'Table Step', '1.4') - ); - expect(exportVisitsController.idToNode['node5']).toEqual( - createNode('node5', 'node', 'Match Step', '1.5') - ); - expect(exportVisitsController.idToNode['node6']).toEqual( - createNode('node6', 'node', 'Multiple Choice Step', '1.6') - ); - expect(exportVisitsController.idToNode['node7']).toEqual( - createNode('node7', 'node', 'Draw Step', '1.7') - ); - }); -} - -function initializeWorkgroupIdNodeIdToVisitCounter_ShouldSetCounters() { - it('initializeWorkgroupIdNodeIdToVisitCounter should set counters', () => { - exportVisitsController.initializeWorkgroupIdNodeIdToVisitCounter(exportVisitsController.nodes); - expectVisitCountersToEqualValue( - [ - '100-group0', - '100-group1', - '100-node1', - '100-node2', - '100-node3', - '100-node4', - '100-node5', - '100-node6', - '100-node7', - '101-group0', - '101-group1', - '101-node1', - '101-node2', - '101-node3', - '101-node4', - '101-node5', - '101-node6', - '101-node7' - ], - 0 - ); - }); -} - -function getHeaderRow_ShouldGetArrayOfColumnNames() { - it('getHeaderRow should get array of column names', () => { - const headerRow = exportVisitsController.getHeaderRow(); - expect(headerRow[0]).toEqual('#'); - expect(headerRow[1]).toEqual('Workgroup ID'); - expect(headerRow[2]).toEqual('WISE ID 1'); - expect(headerRow[3]).toEqual('Student Name 1'); - expect(headerRow[4]).toEqual('WISE ID 2'); - expect(headerRow[5]).toEqual('Student Name 2'); - expect(headerRow[6]).toEqual('WISE ID 3'); - expect(headerRow[7]).toEqual('Student Name 3'); - expect(headerRow[8]).toEqual('Run ID'); - expect(headerRow[9]).toEqual('Project ID'); - expect(headerRow[10]).toEqual('Project Name'); - expect(headerRow[11]).toEqual('Period ID'); - expect(headerRow[12]).toEqual('Period Name'); - expect(headerRow[13]).toEqual('Start Date'); - expect(headerRow[14]).toEqual('End Date'); - expect(headerRow[15]).toEqual('Node ID'); - expect(headerRow[16]).toEqual('Step Title'); - expect(headerRow[17]).toEqual('Enter Time'); - expect(headerRow[18]).toEqual('Exit Time'); - expect(headerRow[19]).toEqual('Visit Duration (Seconds)'); - expect(headerRow[20]).toEqual('Visit Counter'); - expect(headerRow[21]).toEqual('Revisit Counter'); - expect(headerRow[22]).toEqual('Previous Node ID'); - expect(headerRow[23]).toEqual('Previous Step Title'); - expect(headerRow[24]).toEqual('Node IDs Since Last Visit'); - expect(headerRow[25]).toEqual('Steps Since Last Visit'); - }); -} - -function initializeColumnNameToColumnNumber_ShouldSetMappings() { - it('initializeColumnNameToColumnNumber should set mappings', () => { - exportVisitsController.initializeColumnNameToColumnNumber(); - expect(getColumnNameToNumberValue('#')).toEqual(0); - expect(getColumnNameToNumberValue('Workgroup ID')).toEqual(1); - expect(getColumnNameToNumberValue('WISE ID 1')).toEqual(2); - expect(getColumnNameToNumberValue('Student Name 1')).toEqual(3); - expect(getColumnNameToNumberValue('WISE ID 2')).toEqual(4); - expect(getColumnNameToNumberValue('Student Name 2')).toEqual(5); - expect(getColumnNameToNumberValue('WISE ID 3')).toEqual(6); - expect(getColumnNameToNumberValue('Student Name 3')).toEqual(7); - expect(getColumnNameToNumberValue('Run ID')).toEqual(8); - expect(getColumnNameToNumberValue('Project ID')).toEqual(9); - expect(getColumnNameToNumberValue('Project Name')).toEqual(10); - expect(getColumnNameToNumberValue('Period ID')).toEqual(11); - expect(getColumnNameToNumberValue('Period Name')).toEqual(12); - expect(getColumnNameToNumberValue('Start Date')).toEqual(13); - expect(getColumnNameToNumberValue('End Date')).toEqual(14); - expect(getColumnNameToNumberValue('Node ID')).toEqual(15); - expect(getColumnNameToNumberValue('Step Title')).toEqual(16); - expect(getColumnNameToNumberValue('Enter Time')).toEqual(17); - expect(getColumnNameToNumberValue('Exit Time')).toEqual(18); - expect(getColumnNameToNumberValue('Visit Duration (Seconds)')).toEqual(19); - expect(getColumnNameToNumberValue('Visit Counter')).toEqual(20); - expect(getColumnNameToNumberValue('Revisit Counter')).toEqual(21); - expect(getColumnNameToNumberValue('Previous Node ID')).toEqual(22); - expect(getColumnNameToNumberValue('Previous Step Title')).toEqual(23); - expect(getColumnNameToNumberValue('Node IDs Since Last Visit')).toEqual(24); - expect(getColumnNameToNumberValue('Steps Since Last Visit')).toEqual(25); - }); -} - -function initializeIdToUserInfo_ShouldPopulateIdToUserInfo() { - it('initializeIdToUserInfo should populate workgroupIdToUserInfo', () => { - const userInfo1 = { users: [{ id: 100, name: 'Spongebob Squarepants' }] }; - const userInfo2 = { users: [{ id: 101, name: 'Patrick Star' }] }; - spyOn(ConfigService, 'getUserInfoByWorkgroupId').and.callFake(workgroupId => { - if (workgroupId === 100) { - return userInfo1; - } else if (workgroupId === 101) { - return userInfo2; - } - }); - exportVisitsController.initializeIdToUserInfo(); - expect(exportVisitsController.idToUserInfo[100]).toEqual(userInfo1); - expect(exportVisitsController.idToUserInfo[101]).toEqual(userInfo2); - }); -} - -function selectAll_ShouldSetAllCheckedToTrue() { - it('selectAll should set all checked to true', () => { - exportVisitsController.selectAll(); - expectIdsToCheckedToEqualValue( - ['group0', 'group1', 'node1', 'node2', 'node3', 'node4', 'node5', 'node6', 'node7'], - true - ); - }); -} - -function selectAll_ShouldSetAllCheckedToFalse() { - it('selectAll should set all checked to false', () => { - exportVisitsController.deselectAll(); - expectIdsToCheckedToEqualValue( - ['group0', 'group1', 'node1', 'node2', 'node3', 'node4', 'node5', 'node6', 'node7'], - false - ); - }); -} - -function nodeChecked_WhenSettingAGroupToTrue_ShouldSetAllChildrenToTrue() { - it('nodeChecked when setting a group to true should set all children to true', () => { - exportVisitsController.deselectAll(); - exportVisitsController.idToChecked['group1'] = true; - exportVisitsController.nodeChecked(exportVisitsController.idToNode['group1'].node); - expect(exportVisitsController.idToChecked['group0']).toEqual(false); - expectIdsToCheckedToEqualValue( - ['group1', 'node1', 'node2', 'node3', 'node4', 'node5', 'node6', 'node7'], - true - ); - }); -} - -function nodeChecked_WhenSettingAGroupToFalse_ShouldSetAllChildrenToFalse() { - it('nodeChecked when setting a group to false should set all children to false', () => { - exportVisitsController.deselectAll(); - exportVisitsController.idToChecked['node1'] = true; - exportVisitsController.idToChecked['node2'] = true; - exportVisitsController.idToChecked['node3'] = true; - exportVisitsController.idToChecked['group1'] = false; - exportVisitsController.nodeChecked(exportVisitsController.idToNode['group1'].node); - expectIdsToCheckedToEqualValue( - ['group0', 'group1', 'node1', 'node2', 'node3', 'node4', 'node5', 'node6', 'node7'], - false - ); - }); -} - -function getCheckedItems_ShouldGetAnArrayOfIds() { - it('getCheckedItems should get an array of ids', () => { - exportVisitsController.deselectAll(); - exportVisitsController.idToChecked['node1'] = true; - exportVisitsController.idToChecked['node3'] = true; - exportVisitsController.idToChecked['node5'] = true; - const checkedItems = exportVisitsController.getCheckedItems(); - expect(checkedItems.length).toEqual(3); - expect(checkedItems[0]).toEqual('node1'); - expect(checkedItems[1]).toEqual('node3'); - expect(checkedItems[2]).toEqual('node5'); - }); -} - -function export_ShouldRetrieveEvents() { - it('export should retrieve events', () => { - spyOn(TeacherDataService, 'retrieveEventsExport').and.callFake(() => { - return createResolvedPromise(); - }); - exportVisitsController.export(); - const includeStudentEvents = true; - const includeTeacherEvents = false; - expect(TeacherDataService.retrieveEventsExport).toHaveBeenCalledWith( - includeStudentEvents, - includeTeacherEvents, - exportVisitsController.includeStudentNames - ); - }); -} - -function export_WhileIncludingStudentNames_ShouldRetrieveEventsWithStudentNames() { - it('export should retrieve events', () => { - spyOn(TeacherDataService, 'retrieveEventsExport').and.callFake(() => { - return createResolvedPromise(); - }); - exportVisitsController.includeStudentNames = true; - exportVisitsController.export(); - const includeStudentEvents = true; - const includeTeacherEvents = false; - expect(TeacherDataService.retrieveEventsExport).toHaveBeenCalledWith( - includeStudentEvents, - includeTeacherEvents, - exportVisitsController.includeStudentNames - ); - }); -} - -function handleExportCallback_WithMatchingEnterAndExitEvents_ShouldCreateRows() { - it('handleExportCallback with matching enter and exit events should create rows', () => { - spyOn(exportVisitsController, 'generateCSVFile').and.callFake(() => {}); - const userInfo = { users: [{ id: 100, name: 'Spongebob Squarepants' }] }; - spyOn(ConfigService, 'getUserInfoByWorkgroupId').and.returnValue(userInfo); - exportVisitsController.idToUserInfo = { 100: userInfo }; - const enterEvent = createEvent('nodeEntered', 'node1', 100, 10000); - const exitEvent = createEvent('nodeExited', 'node1', 100, 30000); - const response = { events: [enterEvent, exitEvent] }; - exportVisitsController.checkedItems = ['node1']; - exportVisitsController.handleExportCallback(response); - exportVisitsController.rowCounter = 1; - exportVisitsController.initializeWorkgroupIdNodeIdToVisitCounter(exportVisitsController.nodes); - const rows = [ - exportVisitsController.getHeaderRow(), - exportVisitsController.createVisit(enterEvent, exitEvent, []) - ]; - const fileName = '2000_visits.csv'; - expect(exportVisitsController.generateCSVFile).toHaveBeenCalledWith(rows, fileName); - }); -} - -function handleExportCallback_WithMissingExitEventAtBeginning_ShouldCreateRows() { - it('handleExportCallback with missing exit event at beginning should create rows', () => { - spyOn(exportVisitsController, 'generateCSVFile').and.callFake(() => {}); - const userInfo = { users: [{ id: 100, name: 'Spongebob Squarepants' }] }; - spyOn(ConfigService, 'getUserInfoByWorkgroupId').and.returnValue(userInfo); - exportVisitsController.idToUserInfo = { 100: userInfo }; - const enterEvent1 = createEvent('nodeEntered', 'node1', 100, 10000); - const enterEvent2 = createEvent('nodeEntered', 'node1', 100, 20000); - const exitEvent = createEvent('nodeExited', 'node1', 100, 30000); - const response = { events: [enterEvent1, enterEvent2, exitEvent] }; - exportVisitsController.checkedItems = ['node1']; - exportVisitsController.handleExportCallback(response); - exportVisitsController.rowCounter = 1; - exportVisitsController.initializeWorkgroupIdNodeIdToVisitCounter(exportVisitsController.nodes); - const visit1 = exportVisitsController.createVisit(enterEvent1, null, []); - const visit2 = exportVisitsController.createVisit(enterEvent2, exitEvent, [visit1]); - const rows = [exportVisitsController.getHeaderRow(), visit1, visit2]; - const fileName = '2000_visits.csv'; - expect(exportVisitsController.generateCSVFile).toHaveBeenCalledWith(rows, fileName); - }); -} - -function handleExportCallback_WithMissingExitEventAtEnd_ShouldCreateRows() { - it('handleExportCallback with missing exit event at end should create rows', () => { - spyOn(exportVisitsController, 'generateCSVFile').and.callFake(() => {}); - const userInfo = { users: [{ id: 100, name: 'Spongebob Squarepants' }] }; - spyOn(ConfigService, 'getUserInfoByWorkgroupId').and.returnValue(userInfo); - exportVisitsController.idToUserInfo = { 100: userInfo }; - const enterEvent1 = createEvent('nodeEntered', 'node1', 100, 10000); - const exitEvent = createEvent('nodeExited', 'node1', 100, 20000); - const enterEvent2 = createEvent('nodeEntered', 'node1', 100, 30000); - const response = { events: [enterEvent1, exitEvent, enterEvent2] }; - exportVisitsController.checkedItems = ['node1']; - exportVisitsController.handleExportCallback(response); - exportVisitsController.rowCounter = 1; - exportVisitsController.initializeWorkgroupIdNodeIdToVisitCounter(exportVisitsController.nodes); - const visit1 = exportVisitsController.createVisit(enterEvent1, exitEvent, []); - const visit2 = exportVisitsController.createVisit(enterEvent2, null, [visit1]); - const rows = [exportVisitsController.getHeaderRow(), visit1, visit2]; - const fileName = '2000_visits.csv'; - expect(exportVisitsController.generateCSVFile).toHaveBeenCalledWith(rows, fileName); - }); -} - -function getNodeEnteredAndExitedEvents_ShouldRemoveOtherEvents() { - it('getNodeEnteredAndExitedEvents should remove other events', () => { - const event1 = createEvent('nodeEntered', 'node1', 100, 1000); - const event2 = createEvent('buttonClicked', 'node1', 100, 2000); - const event3 = createEvent('nodeEntered', 'node1', 100, 3000); - const events = [event1, event2, event3]; - const cleanedEvents = exportVisitsController.getNodeEnteredAndExitedEvents(events); - expect(cleanedEvents.length).toEqual(2); - expect(cleanedEvents[0]).toEqual(event1); - expect(cleanedEvents[1]).toEqual(event3); - }); -} - -function getEventsWithActiveWorkgroups_ShouldRemoveEvents() { - it('getEventsWithActiveWorkgroups should remove events', () => { - const event1 = createEvent('nodeEntered', 'node1', 100, 1000); - const event2 = createEvent('nodeEntered', 'node1', 101, 2000); - const event3 = createEvent('nodeEntered', 'node1', 102, 3000); - const events = [event1, event2, event3]; - spyOn(exportVisitsController, 'isActiveWorkgroup').and.callFake(workgroupId => { - return workgroupId === 100 || workgroupId === 101; - }); - const cleanedEvents = exportVisitsController.getEventsWithActiveWorkgroups(events); - expect(cleanedEvents.length).toEqual(2); - expect(cleanedEvents[0]).toEqual(event1); - expect(cleanedEvents[1]).toEqual(event2); - }); -} - -function getEventsThatAreNotErroneous_ShouldRemoveEvents() { - it('getEventsThatAreNotErroneous should remove events', () => { - const event1 = createEvent('nodeEntered', 'node1', 100, 1000); - const event2 = createEvent('nodeExited', 'node1', 100, 1000); - const event3 = createEvent('nodeExited', 'node1', 100, 3000); - const events = [event1, event2, event3]; - const cleanedEvents = exportVisitsController.getEventsThatAreNotErroneous(events); - expect(cleanedEvents.length).toEqual(2); - expect(cleanedEvents[0]).toEqual(event1); - expect(cleanedEvents[1]).toEqual(event3); - }); -} - -function isErroneousExitedEvent_ShouldReturnTrue() { - it('isErroneousExitedEvent should return true', () => { - const event1 = createEvent('nodeExited', 'node1', 100, 1000); - const event2 = createEvent('nodeExited', 'node1', 100, 1000); - expect(exportVisitsController.isErroneousExitedEvent(event1, event2)).toBe(true); - }); -} - -function isErroneousExitedEvent_ShouldReturnFalse() { - it('isErroneousExitedEvent should return false', () => { - const event1 = createEvent('nodeExited', 'node1', 100, 1000); - const event2 = createEvent('nodeEntered', 'node1', 100, 1000); - expect(exportVisitsController.isErroneousExitedEvent(event1, event2)).toBe(false); - }); -} - -function getDeletedSteps_ShouldReturnStepsNoLongerInProject() { - it('getDeletedSteps should return steps no longer in project', () => { - const event1 = createEvent('nodeEntered', 'node1', 100, 1000); - const event2 = createEvent('nodeEntered', 'node10', 100, 2000); - const events = [event1, event2]; - spyOn(ProjectService, 'getNodeById').and.callFake(nodeId => { - if (nodeId === 'node1') { - return { id: 'node1' }; - } else if (nodeId === 'node10') { - return null; - } - }); - const deletedSteps = exportVisitsController.getDeletedSteps(events); - expect(Object.keys(deletedSteps).length).toEqual(1); - expect(deletedSteps['node1']).toBeUndefined(); - expect(deletedSteps['node10']).toEqual(true); - }); -} - -function filterRows_ShouldTakeOutRowsThatAreNotSelected() { - it('filterRows should take out rows that are not selected', () => { - const row1 = createRow('node1'); - const row2 = createRow('node2'); - const row3 = createRow('node3'); - const row4 = createRow('node1'); - const row5 = createRow('node2'); - const rows = [row1, row2, row3, row4, row5]; - exportVisitsController.checkedItems = ['node1', 'node3']; - const filteredRows = exportVisitsController.filterRows(rows); - expect(filteredRows.length).toEqual(3); - expect(filteredRows[0]).toEqual(row1); - expect(filteredRows[1]).toEqual(row3); - expect(filteredRows[2]).toEqual(row1); - }); -} - -function filterRows_ShouldTakeOutRowsForDeletedSteps() { - it('filterRows should take out rows for deleted steps', () => { - exportVisitsController.includeDeletedSteps = false; - exportVisitsController.deletedSteps['node2'] = true; - const row1 = createRow('node1'); - const row2 = createRow('node2'); - const row3 = createRow('node3'); - const rows = [row1, row2, row3]; - exportVisitsController.checkedItems = ['node1', 'node3']; - const filteredRows = exportVisitsController.filterRows(rows); - expect(filteredRows.length).toEqual(2); - expect(filteredRows[0]).toEqual(row1); - expect(filteredRows[1]).toEqual(row3); - }); -} - -function filterRows_ShouldKeepRowsForDeletedSteps() { - it('filterRows should keep rows for deleted steps', () => { - exportVisitsController.includeDeletedSteps = true; - exportVisitsController.deletedSteps['node2'] = true; - const row1 = createRow('node1'); - const row2 = createRow('node2'); - const row3 = createRow('node3'); - const rows = [row1, row2, row3]; - exportVisitsController.checkedItems = ['node1', 'node3']; - const filteredRows = exportVisitsController.filterRows(rows); - expect(filteredRows.length).toEqual(3); - expect(filteredRows[0]).toEqual(row1); - expect(filteredRows[1]).toEqual(row2); - expect(filteredRows[2]).toEqual(row3); - }); -} - -function sortEvents_ShouldOrderEventsByWorkgroupIdAndClientSaveTime() { - it('sortEvents should order events by workgroup id and client save time', () => { - const event1 = createEvent(null, null, 100, 1000); - const event2 = createEvent(null, null, 100, 2000); - const event3 = createEvent(null, null, 101, 4000); - const event4 = createEvent(null, null, 100, 3000); - const event5 = createEvent(null, null, 101, 5000); - let events = [event1, event2, event3, event4, event5]; - events = exportVisitsController.sortEvents(events); - expect(events.length).toEqual(5); - expect(events[0]).toEqual(event1); - expect(events[1]).toEqual(event2); - expect(events[2]).toEqual(event4); - expect(events[3]).toEqual(event3); - expect(events[4]).toEqual(event5); - }); -} - -function isStepEnteredEvent_WithNodeExitedEvent_ShouldReturnFalse() { - it('isStepEnteredEvent with node exited event should return false', () => { - const event = createEvent('nodeExited', 'node1'); - expect(exportVisitsController.isStepEnteredEvent(event)).toEqual(false); - }); -} - -function isStepEnteredEvent_WithGroupNode_ShouldReturnFalse() { - it('isStepEnteredEvent with group node should return false', () => { - const event = createEvent('nodeEntered', 'group1'); - expect(exportVisitsController.isStepEnteredEvent(event)).toEqual(false); - }); -} - -function isStepEnteredEvent_WithStepNode_ShouldReturnTrue() { - it('isStepEnteredEvent with step node should return true', () => { - const event = createEvent('nodeEntered', 'node1'); - expect(exportVisitsController.isStepEnteredEvent(event)).toEqual(true); - }); -} - -function isStepExitedEvent_WithNodeEnteredEvent_ShouldReturnFalse() { - it('isStepExitedEvent with node entered event should return false', () => { - const event = createEvent('nodeEntered', 'node1'); - expect(exportVisitsController.isStepExitedEvent(event)).toEqual(false); - }); -} - -function isStepExitedEvent_WithAGroupNode_ShouldReturnFalse() { - it('isStepExitedEvent with a group node should return false', () => { - const event = createEvent('nodeExited', 'group1'); - expect(exportVisitsController.isStepExitedEvent(event)).toEqual(false); - }); -} - -function isStepExitedEvent_WithAStepNode_ShouldReturnTrue() { - it('isStepExitedEvent with a step node should return true', () => { - const event = createEvent('nodeExited', 'node1'); - expect(exportVisitsController.isStepExitedEvent(event)).toEqual(true); - }); -} - -function isMatchingWorkgroupId_WithNonMatchingWorkgroupIds_ShouldReturnFalse() { - it('isMatchingWorkgroupId with non matching workgroup ids should return false', () => { - const event1 = createEvent('nodeEntered', null, 100); - const event2 = createEvent('nodeEntered', null, 101); - expect(exportVisitsController.isMatchingWorkgroupId(event1, event2)).toEqual(false); - }); -} - -function isMatchingWorkgroupId_WithMatchingWorkgroupIds_ShouldReturnTrue() { - it('isMatchingWorkgroupId with matching workgroup ids should return true', () => { - const event1 = createEvent('nodeEntered', null, 100); - const event2 = createEvent('nodeEntered', null, 100); - expect(exportVisitsController.isMatchingWorkgroupId(event1, event2)).toEqual(true); - }); -} - -function isMatchingNodeId_WithNonMatchingNodeIds_ShouldReturnFalse() { - it('isMatchingNodeId with non matching node ids should return false', () => { - const event1 = createEvent('nodeEntered', 'node1'); - const event2 = createEvent('nodeEntered', 'node2'); - expect(exportVisitsController.isMatchingNodeId(event1, event2)).toEqual(false); - }); -} - -function isMatchingNodeId_WithMatchingNodeIds_ShouldReturnTrue() { - it('isMatchingNodeI with matching node ids should return true', () => { - const event1 = createEvent('nodeEntered', 'node1'); - const event2 = createEvent('nodeEntered', 'node1'); - expect(exportVisitsController.isMatchingNodeId(event1, event2)).toEqual(true); - }); -} - -function createVisit_WithEnterAndExitEvent_ShouldCreateAVisit() { - it('createVisit with enter and exit events should create visit', () => { - const userInfo = { - users: [{ id: 100, name: 'Spongebob Squarepants' }] - }; - spyOn(ConfigService, 'getUserInfoByWorkgroupId').and.returnValue(userInfo); - const enterEvent = createEvent('nodeEntered', 'node1', 100, 10000); - const exitEvent = createEvent('nodeExited', 'node1', 100, 30000); - const previousVisits = [ - createRow('node1', '1.1: HTML Step', 100), - createRow('node2', '1.2: Open Response Step', 100), - createRow('node3', '1.3: Graph Step', 100) - ]; - exportVisitsController.incrementVisitCounter(100, 'node1'); - const visit = exportVisitsController.createVisit(enterEvent, exitEvent, previousVisits); - expect(exportVisitsController.getCellInRow(visit, '#')).toEqual(1); - expect(exportVisitsController.getCellInRow(visit, 'Node ID')).toEqual('node1'); - expect(exportVisitsController.getCellInRow(visit, 'Step Title')).toEqual('1.1: HTML Step'); - expect(exportVisitsController.getCellInRow(visit, 'Visit Duration (Seconds)')).toEqual(20); - expect(exportVisitsController.getCellInRow(visit, 'Visit Counter')).toEqual(2); - expect(exportVisitsController.getCellInRow(visit, 'Revisit Counter')).toEqual(1); - expect(exportVisitsController.getCellInRow(visit, 'Previous Node ID')).toEqual('node3'); - expect(exportVisitsController.getCellInRow(visit, 'Previous Step Title')).toEqual( - '1.3: Graph Step' - ); - expect(exportVisitsController.getCellInRow(visit, 'Steps Since Last Visit')).toEqual( - '1.2, 1.3' - ); - }); -} - -function createVisit_WithNoPreviousVisits_ShouldCreateAVisit() { - it('createVisit with enter and exit events should create visit', () => { - const userInfo = { users: [{ id: 100, name: 'Spongebob Squarepants' }] }; - spyOn(ConfigService, 'getUserInfoByWorkgroupId').and.returnValue(userInfo); - const enterEvent = createEvent('nodeEntered', 'node1', 100, 10000); - const exitEvent = createEvent('nodeExited', 'node1', 100, 30000); - const previousVisits = []; - const visit = exportVisitsController.createVisit(enterEvent, exitEvent, previousVisits); - expect(exportVisitsController.getCellInRow(visit, '#')).toEqual(1); - expect(exportVisitsController.getCellInRow(visit, 'Node ID')).toEqual('node1'); - expect(exportVisitsController.getCellInRow(visit, 'Step Title')).toEqual('1.1: HTML Step'); - expect(exportVisitsController.getCellInRow(visit, 'Visit Duration (Seconds)')).toEqual(20); - expect(exportVisitsController.getCellInRow(visit, 'Visit Counter')).toEqual(1); - expect(exportVisitsController.getCellInRow(visit, 'Revisit Counter')).toEqual(0); - expect(exportVisitsController.getCellInRow(visit, 'Previous Node ID')).toBeUndefined(); - expect(exportVisitsController.getCellInRow(visit, 'Previous Step Title')).toBeUndefined(); - expect(exportVisitsController.getCellInRow(visit, 'Steps Since Last Visit')).toBeUndefined(); - }); -} - -function createVisit_WithOnlyEnterEvent_ShouldCreateAVisit() { - it('createVisit with only enter should create visit', () => { - const userInfo = { - users: [{ id: 100, name: 'Spongebob Squarepants' }] - }; - spyOn(ConfigService, 'getUserInfoByWorkgroupId').and.returnValue(userInfo); - const enterEvent = createEvent('nodeEntered', 'node1', 100, 10000); - const exitEvent = null; - const previousVisits = [ - createRow('node1', '1.1: HTML Step', 100), - createRow('node2', '1.2: Open Response Step', 100), - createRow('node3', '1.3: Graph Step', 100) - ]; - exportVisitsController.incrementVisitCounter(100, 'node1'); - const visit = exportVisitsController.createVisit(enterEvent, exitEvent, previousVisits); - expect(exportVisitsController.getCellInRow(visit, '#')).toEqual(1); - expect(exportVisitsController.getCellInRow(visit, 'Node ID')).toEqual('node1'); - expect(exportVisitsController.getCellInRow(visit, 'Step Title')).toEqual('1.1: HTML Step'); - expect(exportVisitsController.getCellInRow(visit, 'Exit Time')).toEqual('(Unknown Exit Time)'); - expect(exportVisitsController.getCellInRow(visit, 'Visit Duration (Seconds)')).toEqual( - '(Unknown Visit Duration)' - ); - expect(exportVisitsController.getCellInRow(visit, 'Visit Counter')).toEqual(2); - expect(exportVisitsController.getCellInRow(visit, 'Revisit Counter')).toEqual(1); - expect(exportVisitsController.getCellInRow(visit, 'Previous Node ID')).toEqual('node3'); - expect(exportVisitsController.getCellInRow(visit, 'Previous Step Title')).toEqual( - '1.3: Graph Step' - ); - expect(exportVisitsController.getCellInRow(visit, 'Steps Since Last Visit')).toEqual( - '1.2, 1.3' - ); - }); -} - -function getPreviousVisit_ShouldGetPreviousVisit() { - it('createRowWithEmptyCells should get previous visit', () => { - const visit = exportVisitsController.createRowWithEmptyCells(); - const workgroupId = 100; - exportVisitsController.setCellInRow(visit, 'Workgroup ID', workgroupId); - const previousVisits = [visit]; - const previousVisit = exportVisitsController.getPreviousVisit(previousVisits, workgroupId); - expect(previousVisit).toEqual(visit); - }); -} - -function getPreviousVisit_ShouldReturnNullIfWorkgroupIdIsDifferent() { - it('createRowWithEmptyCells should return null if workgroup id is different', () => { - const visit = exportVisitsController.createRowWithEmptyCells(); - const workgroupId = 100; - exportVisitsController.setCellInRow(visit, 'Workgroup ID', workgroupId); - const previousVisits = [visit]; - const workgroupId2 = 101; - const previousVisit = exportVisitsController.getPreviousVisit(previousVisits, workgroupId2); - expect(previousVisit).toEqual(null); - }); -} - -function createRowWithEmptyCells_ShouldReturnArrayWithEmptyValues() { - it('createRowWithEmptyCells should return array with empty values', () => { - const row = exportVisitsController.createRowWithEmptyCells(); - expect(row.length).toEqual(26); - expect(row[0]).toBeUndefined(); - expect(row[1]).toBeUndefined(); - expect(row[24]).toBeUndefined(); - expect(row[25]).toBeUndefined(); - }); -} - -function getNodeIdsBetweenLastVisit_ShouldReturnAStringOfStepNumbers() { - it('getNodeIdsBetweenLastVisit should return a string of step numbers', () => { - const previousVisits = [ - createRow('node1'), - createRow('node2'), - createRow('node3'), - createRow('node4'), - createRow('node5'), - createRow('node1'), - createRow('node2'), - createRow('node3'), - createRow('node6'), - createRow('node7') - ]; - expect(exportVisitsController.getNodeIdsBetweenLastVisit('node3', previousVisits)).toEqual( - 'node6, node7' - ); - }); -} - -function getStepNumbersBetweenLastVisit_ShouldReturnAStringOfStepNumbers() { - it('getStepNumbersBetweenLastVisit should return a string of step numbers', () => { - const previousVisits = [ - createRow('node1'), - createRow('node2'), - createRow('node3'), - createRow('node4'), - createRow('node5'), - createRow('node1'), - createRow('node2'), - createRow('node3'), - createRow('node6'), - createRow('node7') - ]; - expect(exportVisitsController.getStepNumbersBetweenLastVisit('node3', previousVisits)).toEqual( - '1.6, 1.7' - ); - }); -} - -function addUserCells_WithOneStudentNotIncludingNames_ShouldSetTheUserID() { - it('addUserCells with one student should set the wise id', () => { - const userInfo = { users: [{ id: 100, name: 'Spongebob Squarepants' }] }; - spyOn(ConfigService, 'getUserInfoByWorkgroupId').and.returnValue(userInfo); - exportVisitsController.includeStudentNames = false; - const row = createRow('node1'); - exportVisitsController.addUserCells(row, 100); - expect(exportVisitsController.getCellInRow(row, 'WISE ID 1')).toEqual(100); - expect(exportVisitsController.getCellInRow(row, 'Student Name 1')).toBeUndefined(); - }); -} - -function addUserCells_WithOneStudentIncludingNames_ShouldSetTheUserIDAndStudentName() { - it('addUserCells with one student including names should set the wise id and student name', () => { - const userInfo = { users: [{ id: 1000, name: 'Spongebob Squarepants' }] }; - spyOn(ConfigService, 'getUserInfoByWorkgroupId').and.returnValue(userInfo); - exportVisitsController.includeStudentNames = true; - const row = createRow('node1'); - exportVisitsController.addUserCells(row, 100); - expect(exportVisitsController.getCellInRow(row, 'WISE ID 1')).toEqual(1000); - expect(exportVisitsController.getCellInRow(row, 'Student Name 1')).toEqual( - 'Spongebob Squarepants' - ); - }); -} - -function addUserCells_WithMultipleStudentsIncludingNames_ShouldSetTheUserIDAndStudentNames() { - it('addUserCells with multiple students including names should set the wise id and student names', () => { - const userInfo = { - users: [ - { id: 1000, name: 'Spongebob Squarepants' }, - { id: 1001, name: 'Patrick Star' } - ] - }; - spyOn(ConfigService, 'getUserInfoByWorkgroupId').and.returnValue(userInfo); - exportVisitsController.includeStudentNames = true; - const row = createRow('node1'); - exportVisitsController.addUserCells(row, 100); - expect(exportVisitsController.getCellInRow(row, 'WISE ID 1')).toEqual(1000); - expect(exportVisitsController.getCellInRow(row, 'Student Name 1')).toEqual( - 'Spongebob Squarepants' - ); - expect(exportVisitsController.getCellInRow(row, 'WISE ID 2')).toEqual(1001); - expect(exportVisitsController.getCellInRow(row, 'Student Name 2')).toEqual('Patrick Star'); - }); -} - -function getVisitDuration_ShouldGetTheTimeDifferenceBetweenEventsInSeconds() { - it('getVisitDuration should get the time difference between events in seconds', () => { - const event1 = createEvent(null, null, null, 1000); - const event2 = createEvent(null, null, null, 3000); - expect(exportVisitsController.getVisitDuration(event1, event2)).toEqual(2); - }); -} - -function getColumnNumber_ShouldReturnTheColumnNumber() { - it('getColumnNumber should return the column number', () => { - expect(exportVisitsController.getColumnNumber('Node ID')).toEqual(15); - }); -} - -function incrementRowCounter_ShouldIncrementRowCounterBy1() { - it('incrementRowCounter should increment row counter by 1', () => { - exportVisitsController.rowCounter = 0; - exportVisitsController.incrementRowCounter(); - expect(exportVisitsController.rowCounter).toEqual(1); - }); -} - -function getStepNumber_ShouldGetAStringContainingStepNumber() { - it('getStepNumberAndTitle should get a string containing step number', () => { - expect(exportVisitsController.getStepNumber('node1')).toEqual('1.1'); - }); -} - -function getStepNumber_ShouldGetAStringForADeletedStep() { - it('getStepNumber should get a string for a deleted step', () => { - exportVisitsController.deletedSteps['node10'] = true; - expect(exportVisitsController.getStepNumber('node10')).toEqual('(Deleted Step)'); - }); -} - -function getStepNumberAndTitle_ShouldGetAStringContainingStepNumberAndTitle() { - it('getStepNumberAndTitle should get a string containing step number and title', () => { - expect(exportVisitsController.getStepNumberAndTitle('node1')).toEqual('1.1: HTML Step'); - }); -} - -function getStepNumberAndTitle_ShouldGetAStringForADeletedStep() { - it('getStepNumberAndTitle should get a string for a deleted step', () => { - exportVisitsController.deletedSteps['node10'] = true; - expect(exportVisitsController.getStepNumberAndTitle('node10')).toEqual('(Deleted Step)'); - }); -} - -function getWorkgroupIdNodeIdKey_ShouldGetAStringContainingWorkgroupIdAndNodeId() { - it('getWorkgroupIdNodeIdKey should get a string containing workgroup id and node id', () => { - const workgroupId = 100; - const nodeId = 'node1'; - expect(exportVisitsController.getWorkgroupIdNodeIdKey(workgroupId, nodeId)).toEqual( - '100-node1' - ); - }); -} - -function getStepNumber_ShouldGetTheStepNumberString() { - it('getStepNumber should get the step number string', () => { - expect(exportVisitsController.getStepNumber('node1')).toEqual('1.1'); - }); -} - -function incrementVisitCounter_ShouldIncreaseTheCounterBy1() { - it('incrementVisitCounter should increase the counter by 1', () => { - const workgroupId = 100; - const nodeId = 'node1'; - const key = exportVisitsController.getWorkgroupIdNodeIdKey(workgroupId, nodeId); - exportVisitsController.workgroupIdNodeIdToVisitCounter[key] = 0; - exportVisitsController.incrementVisitCounter(workgroupId, nodeId); - expect(exportVisitsController.workgroupIdNodeIdToVisitCounter[key]).toEqual(1); - }); -} - -function getVisitCounter_ShouldGetTheVisitCount() { - it('getVisitCounter should get the visit count', () => { - const workgroupId = 100; - const nodeId = 'node1'; - const key = exportVisitsController.getWorkgroupIdNodeIdKey(workgroupId, nodeId); - exportVisitsController.workgroupIdNodeIdToVisitCounter[key] = 2; - expect(exportVisitsController.getVisitCounter(workgroupId, nodeId)).toEqual(2); - }); -} - -function getRevisitCounter_ShouldGetTheRevisitCount() { - it('getRevisitCounter should get the revisit count', () => { - const workgroupId = 100; - const nodeId = 'node1'; - const key = exportVisitsController.getWorkgroupIdNodeIdKey(workgroupId, nodeId); - exportVisitsController.workgroupIdNodeIdToVisitCounter[key] = 2; - expect(exportVisitsController.getRevisitCounter(workgroupId, nodeId)).toEqual(1); - }); -} - -function setCellInRow_ShouldSetTheValueInTheCell() { - it('setCellInRow should set the value in the cell', () => { - const row = createRow('node1'); - exportVisitsController.setCellInRow(row, 'Workgroup ID', 100); - expect(row[1]).toEqual(100); - }); -} - -function getCellInRow_ShouldGetTheValueInTheCell() { - it('getCellInRow should get the value in the cell', () => { - const row = createRow('node1'); - expect(exportVisitsController.getCellInRow(row, 'Node ID')).toEqual('node1'); - }); -} diff --git a/src/messages.xlf b/src/messages.xlf index 2fb7ff2c39c..68db1914175 100644 --- a/src/messages.xlf +++ b/src/messages.xlf @@ -395,6 +395,10 @@ src/assets/wise5/authoringTool/addNode/add-your-own-node/add-your-own-node.component.html 38 + + src/assets/wise5/classroomMonitor/dataExport/export-step-visits/export-step-visits.component.html + 7 + Cancel @@ -11112,6 +11116,280 @@ Are you sure you want to proceed? 129 + + Export Step Visit Events + + src/assets/wise5/classroomMonitor/dataExport/export-step-visits/export-step-visits.component.html + 12 + + + + Hide Column Explanations + + src/assets/wise5/classroomMonitor/dataExport/export-step-visits/export-step-visits.component.html + 17,19 + + + src/assets/wise5/classroomMonitor/dataExport/export-step-visits/export-step-visits.component.html + 40,42 + + + + Show Column Explanations + + src/assets/wise5/classroomMonitor/dataExport/export-step-visits/export-step-visits.component.html + 20,22 + + + + Column Name + + src/assets/wise5/classroomMonitor/dataExport/export-step-visits/export-step-visits.component.html + 28 + + + + Explanation + + src/assets/wise5/classroomMonitor/dataExport/export-step-visits/export-step-visits.component.html + 29 + + + + Include Student Names + + src/assets/wise5/classroomMonitor/dataExport/export-step-visits/export-step-visits.component.html + 49 + + + + Choose Steps + + src/assets/wise5/classroomMonitor/dataExport/export-step-visits/export-step-visits.component.html + 51 + + + + Select All + + src/assets/wise5/classroomMonitor/dataExport/export-step-visits/export-step-visits.component.html + 53 + + + + Deselect All + + src/assets/wise5/classroomMonitor/dataExport/export-step-visits/export-step-visits.component.html + 54 + + + + Export + + src/assets/wise5/classroomMonitor/dataExport/export-step-visits/export-step-visits.component.html + 55 + + + src/assets/wise5/classroomMonitor/dataExport/export-step-visits/export-step-visits.component.html + 75 + + + + Include Deleted Steps (If Any) + + src/assets/wise5/classroomMonitor/dataExport/export-step-visits/export-step-visits.component.html + 71 + + + + Back To Top + + src/assets/wise5/classroomMonitor/dataExport/export-step-visits/export-step-visits.component.html + 80 + + + + The row number. + + src/assets/wise5/classroomMonitor/dataExport/export-step-visits/export-step-visits.component.ts + 120 + + + + The ID of the group. + + src/assets/wise5/classroomMonitor/dataExport/export-step-visits/export-step-visits.component.ts + 121 + + + + The User ID of the first student in the group. This ID follows the student for all runs. + + src/assets/wise5/classroomMonitor/dataExport/export-step-visits/export-step-visits.component.ts + 124 + + + + The name of the first student. This only shows up if you have permission to view the student names and you enabled the 'Include Student Names' checkbox. + + src/assets/wise5/classroomMonitor/dataExport/export-step-visits/export-step-visits.component.ts + 128 + + + + The UserID of the second student in the group. This ID follows the student for all runs. + + src/assets/wise5/classroomMonitor/dataExport/export-step-visits/export-step-visits.component.ts + 132 + + + + The name of the second student. This only shows up if you have permission to view the student names and you enabled the 'Include Student Names' checkbox. + + src/assets/wise5/classroomMonitor/dataExport/export-step-visits/export-step-visits.component.ts + 136 + + + + The User ID of the third student in the group. This ID follows the student for all runs. + + src/assets/wise5/classroomMonitor/dataExport/export-step-visits/export-step-visits.component.ts + 140 + + + + The name of the third student. This only shows up if you have permission to view the student names and you enabled the 'Include Student Names' checkbox. + + src/assets/wise5/classroomMonitor/dataExport/export-step-visits/export-step-visits.component.ts + 144 + + + + The ID of the run. + + src/assets/wise5/classroomMonitor/dataExport/export-step-visits/export-step-visits.component.ts + 146 + + + + The ID of the project. + + src/assets/wise5/classroomMonitor/dataExport/export-step-visits/export-step-visits.component.ts + 147 + + + + The name of the project. + + src/assets/wise5/classroomMonitor/dataExport/export-step-visits/export-step-visits.component.ts + 148 + + + + The ID of the period that this student is in. + + src/assets/wise5/classroomMonitor/dataExport/export-step-visits/export-step-visits.component.ts + 149 + + + + The period name that this student is in. This name is chosen by the teacher that created the run. + + src/assets/wise5/classroomMonitor/dataExport/export-step-visits/export-step-visits.component.ts + 152 + + + + The start date of the run. + + src/assets/wise5/classroomMonitor/dataExport/export-step-visits/export-step-visits.component.ts + 154 + + + + The end date of the run. + + src/assets/wise5/classroomMonitor/dataExport/export-step-visits/export-step-visits.component.ts + 155 + + + + The ID of the step. Each step in a unit has a unique Node ID. + + src/assets/wise5/classroomMonitor/dataExport/export-step-visits/export-step-visits.component.ts + 158 + + + + The title of the step. + + src/assets/wise5/classroomMonitor/dataExport/export-step-visits/export-step-visits.component.ts + 160 + + + + The timestamp when the student entered the step. + + src/assets/wise5/classroomMonitor/dataExport/export-step-visits/export-step-visits.component.ts + 163 + + + + The timestamp when the student exited the step. This value can be empty if WISE did not get the chance to save a step exit event. This can happen if the student closes their laptop without signing out of WISE or if they refresh the WISE page. + + src/assets/wise5/classroomMonitor/dataExport/export-step-visits/export-step-visits.component.ts + 167 + + + + The amount of time the student spent on the step during this visit measured in seconds. + + src/assets/wise5/classroomMonitor/dataExport/export-step-visits/export-step-visits.component.ts + 171 + + + + The number of times the student has visited this step so far. + + src/assets/wise5/classroomMonitor/dataExport/export-step-visits/export-step-visits.component.ts + 175 + + + + The number of times the student has revisited this step so far. This will always be 1 less than the 'Visit Counter' for a given visit. + + src/assets/wise5/classroomMonitor/dataExport/export-step-visits/export-step-visits.component.ts + 179 + + + + The Node ID of the step the student was on before visiting this step. + + src/assets/wise5/classroomMonitor/dataExport/export-step-visits/export-step-visits.component.ts + 183 + + + + The step title of the step the student was on before visiting this step. + + src/assets/wise5/classroomMonitor/dataExport/export-step-visits/export-step-visits.component.ts + 187 + + + + A list of Node IDs that contain the steps the student visited before revisiting this step. This cell will only contain values if they revisit a step. For example if the student navigated to node1, then node2, then node3, then node1. For the second visit to node1, the 'Node IDs Since Last Visit' will show node2, node3. + + src/assets/wise5/classroomMonitor/dataExport/export-step-visits/export-step-visits.component.ts + 191 + + + + A list of step numbers that contain the steps the student visited before revisiting this step. This cell will only contain values if they revisit a step. For example if the student navigated to 1.1, then 1.2, then 1.3, then 1.1. For the second visit to 1.1, the 'Steps Since Last Visit' will show 1.2, 1.3. + + src/assets/wise5/classroomMonitor/dataExport/export-step-visits/export-step-visits.component.ts + 195 + + + Expand All