Skip to content

Commit d2f8ced

Browse files
authored
Export session data (#208)
1 parent 2da812e commit d2f8ced

File tree

3 files changed

+161
-82
lines changed

3 files changed

+161
-82
lines changed

src/app/app.module.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,7 @@ import {
145145
timesIcon,
146146
buildingIcon,
147147
numberListIcon,
148+
downloadIcon,
148149
} from '@cds/core/icon';
149150

150151
ClarityIcons.addIcons(
@@ -183,7 +184,8 @@ ClarityIcons.addIcons(
183184
clockIcon,
184185
timesIcon,
185186
buildingIcon,
186-
numberListIcon
187+
numberListIcon,
188+
downloadIcon
187189
);
188190

189191
const appInitializerFn = (appConfig: AppConfigService) => {
Original file line numberDiff line numberDiff line change
@@ -1,83 +1,125 @@
11
<div class="content-area">
2-
<div class="clr-row clr-align-items-center clr-justify-content-between">
3-
<div class="card clr-col-9">
4-
<div class="clr-row clr-align-items-center">
5-
<div class="clr-col">
6-
<clr-toggle-container id="includeFinishedToggle">
7-
<clr-toggle-wrapper>
8-
<input type="checkbox" clrToggle name="includeFinished" [disabled]="selectedEvent?.finished"
9-
[(ngModel)]="includeFinished" (change)="refresh()">
10-
<label>Include finished</label>
11-
</clr-toggle-wrapper>
12-
<clr-control-helper>Whether finished sessions should be included.
13-
</clr-control-helper>
14-
</clr-toggle-container>
15-
</div>
16-
<div class="clr-col ">
17-
<input id="userFilter" clrInput placeholder="filter users" name="userFilter"
18-
[(ngModel)]="userFilter" (ngModelChange)="filter()"
19-
[disabled]="filteredProgress.length == 0 && userFilter.length == 0" />
20-
<span class="clr-subtext">Filter by users email</span>
21-
</div>
22-
<div class="clr-col">
23-
<clr-dropdown>
24-
<button class="dropdown-toggle btn btn-link" clrDropdownTrigger
25-
[disabled]="scenarioList.size < 2">
26-
<span *ngIf="this.scenarioFilterList.size == 0">Filter
27-
scenario(s)</span>
28-
<span *ngIf="this.scenarioFilterList.size == 1">1 Scenario
29-
selected</span>
30-
<span *ngIf="this.scenarioFilterList.size > 1">{{this.scenarioFilterList.size}}
31-
Scenarios selected</span>
32-
<cds-icon shape="angle" direction="down"></cds-icon>
33-
</button>
34-
35-
<clr-dropdown-menu clrPosition="bottom-right" *clrIfOpen>
36-
<clr-row *ngFor="let sc of scenarioList" clrDropdownItem (click)="filterScenario(sc)"
37-
[ngClass]="{'selected' : this.scenarioFilterList.has(sc)}">
38-
{{sc}}
39-
<span class="filter-selected">
40-
<cds-icon *ngIf="this.scenarioFilterList.has(sc)" shape="check">
41-
</cds-icon>
42-
</span>
43-
</clr-row>
44-
</clr-dropdown-menu>
45-
</clr-dropdown>
46-
</div>
47-
<div class="clr-col">
48-
<p>Showing {{filteredProgress.length}} of {{currentProgress.length}} Sessions
49-
</p>
50-
<button class="btn btn-icon btn-warning" (click)="removeFilter()">
51-
<cds-icon shape="trash"></cds-icon> Clear Filter
52-
</button>
53-
</div>
54-
</div>
2+
<div class="clr-row clr-align-items-center clr-justify-content-between">
3+
<div class="card clr-col-9">
4+
<div class="clr-row clr-align-items-center">
5+
<div class="clr-col">
6+
<clr-toggle-container id="includeFinishedToggle">
7+
<clr-toggle-wrapper>
8+
<input
9+
type="checkbox"
10+
clrToggle
11+
name="includeFinished"
12+
[disabled]="selectedEvent?.finished"
13+
[(ngModel)]="includeFinished"
14+
(change)="refresh()"
15+
/>
16+
<label>Include finished</label>
17+
</clr-toggle-wrapper>
18+
<clr-control-helper
19+
>Whether finished sessions should be included.
20+
</clr-control-helper>
21+
</clr-toggle-container>
5522
</div>
56-
<div class="clr-col buttonCol">
57-
<button class="btn btn-link userlistIcon" [disabled]="users?.length < 1" (click)="openUserList()">
58-
<cds-icon shape="users" size="48" solid></cds-icon>{{users?.length}}
23+
<div class="clr-col">
24+
<input
25+
id="userFilter"
26+
clrInput
27+
placeholder="[email protected]"
28+
name="userFilter"
29+
[(ngModel)]="userFilter"
30+
(ngModelChange)="filter()"
31+
[disabled]="filteredProgress.length == 0 && userFilter.length == 0"
32+
/>
33+
<span class="clr-subtext">Filter by username</span>
34+
<clr-dropdown>
35+
<button
36+
class="dropdown-toggle btn btn-link"
37+
clrDropdownTrigger
38+
[disabled]="scenarioList.size < 2"
39+
>
40+
<span *ngIf="this.scenarioFilterList.size == 0"
41+
>Filter scenario(s)</span
42+
>
43+
<span *ngIf="this.scenarioFilterList.size == 1"
44+
>1 Scenario selected</span
45+
>
46+
<span *ngIf="this.scenarioFilterList.size > 1"
47+
>{{ this.scenarioFilterList.size }} Scenarios selected</span
48+
>
49+
<cds-icon shape="angle" direction="down"></cds-icon>
5950
</button>
51+
52+
<clr-dropdown-menu clrPosition="bottom-right" *clrIfOpen>
53+
<clr-row
54+
*ngFor="let sc of scenarioList"
55+
clrDropdownItem
56+
(click)="filterScenario(sc)"
57+
[ngClass]="{ selected: this.scenarioFilterList.has(sc) }"
58+
>
59+
{{ sc }}
60+
<span class="filter-selected">
61+
<cds-icon
62+
*ngIf="this.scenarioFilterList.has(sc)"
63+
shape="check"
64+
>
65+
</cds-icon>
66+
</span>
67+
</clr-row>
68+
</clr-dropdown-menu>
69+
</clr-dropdown>
6070
</div>
6171
<div class="clr-col">
62-
<interval-timer (intervalElapsed)="refresh()"></interval-timer>
72+
<p>
73+
Showing {{ filteredProgress.length }} of
74+
{{ currentProgress.length }} Sessions
75+
</p>
76+
<button class="btn btn-icon btn-warning" (click)="removeFilter()">
77+
<cds-icon shape="trash"></cds-icon> Clear Filter
78+
</button>
79+
<button class="btn btn-icon btn-icon" (click)="exportCSV()">
80+
<cds-icon shape="download"></cds-icon> Export CSV
81+
</button>
6382
</div>
83+
</div>
6484
</div>
65-
<div class="clr-row">
66-
<ng-container *ngIf="currentProgress?.length > 0; else no_sessions">
67-
<div class="clr-col-12 clr-col-sm-6 clr-col-md-4 clr-col-lg-3" *ngFor="let p of filteredProgress">
68-
<progress-card [progress]="p" [pause]="pause" (nameClickedEvent)="filterName($event)"></progress-card>
69-
</div>
70-
</ng-container>
85+
<div class="clr-col buttonCol">
86+
<button
87+
class="btn btn-link userlistIcon"
88+
[disabled]="users?.length < 1"
89+
(click)="openUserList()"
90+
>
91+
<cds-icon shape="users" size="48" solid></cds-icon>{{ users?.length }}
92+
</button>
7193
</div>
72-
<ng-template #no_sessions>
73-
<div class="clr-col-12">
74-
<p>
75-
No sessions found.
76-
</p>
77-
</div>
78-
</ng-template>
94+
<div class="clr-col">
95+
<interval-timer (intervalElapsed)="refresh()"></interval-timer>
96+
</div>
97+
</div>
98+
<div class="clr-row">
99+
<ng-container *ngIf="currentProgress?.length > 0; else no_sessions">
100+
<div
101+
class="clr-col-12 clr-col-sm-6 clr-col-md-4 clr-col-lg-3"
102+
*ngFor="let p of filteredProgress"
103+
>
104+
<progress-card
105+
[progress]="p"
106+
[pause]="pause"
107+
(nameClickedEvent)="filterName($event)"
108+
></progress-card>
109+
</div>
110+
</ng-container>
111+
</div>
112+
<ng-template #no_sessions>
113+
<div class="clr-col-12">
114+
<p>No sessions found.</p>
115+
</div>
116+
</ng-template>
79117
</div>
80118

81-
82-
<event-user-list #userList [users]="users" [progress]="currentProgress" (userSelected)="filterName($event.email)">
83-
</event-user-list>
119+
<event-user-list
120+
#userList
121+
[users]="users"
122+
[progress]="currentProgress"
123+
(userSelected)="filterName($event.email)"
124+
>
125+
</event-user-list>

src/app/dashboards/progress-dashboard/progress-dashboard.component.ts

+43-8
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,6 @@
1-
import {
2-
Component,
3-
OnInit,
4-
ViewChild,
5-
Input,
6-
SimpleChanges,
7-
} from '@angular/core';
1+
import { Component, OnInit, ViewChild, Input } from '@angular/core';
82
import { ProgressService } from 'src/app/data/progress.service';
9-
import { Progress, ProgressCount } from 'src/app/data/progress';
3+
import { Progress } from 'src/app/data/progress';
104
import { UserService } from '../../data/user.service';
115
import { ScheduledEvent } from '../../data/scheduledevent';
126
import { ScheduledeventService } from '../../data/scheduledevent.service';
@@ -156,4 +150,45 @@ export class ProgressDashboardComponent implements OnInit {
156150
this.filter();
157151
});
158152
}
153+
154+
exportCSV() {
155+
let progressCSV = '';
156+
this.filteredProgress.forEach((progress) => {
157+
progressCSV = progressCSV.concat(
158+
progress.id +
159+
', ' +
160+
progress.user +
161+
', ' +
162+
progress.username +
163+
', ' +
164+
progress.scenario +
165+
', ' +
166+
progress.scenario_name +
167+
', ' +
168+
progress.course +
169+
', ' +
170+
progress.course_name +
171+
', ' +
172+
progress.total_step +
173+
', ' +
174+
progress.max_step +
175+
', ' +
176+
progress.started +
177+
', ' +
178+
progress.last_update +
179+
'\n'
180+
);
181+
});
182+
const filename = this.selectedEvent.event_name + '_sessions.csv';
183+
var element = document.createElement('a');
184+
element.setAttribute(
185+
'href',
186+
'data:text/plain;charset=utf-8,' + encodeURIComponent(progressCSV)
187+
);
188+
element.setAttribute('download', filename);
189+
element.style.display = 'none';
190+
document.body.appendChild(element);
191+
element.click();
192+
document.body.removeChild(element);
193+
}
159194
}

0 commit comments

Comments
 (0)