Skip to content

Commit

Permalink
v3.19.0 (#184)
Browse files Browse the repository at this point in the history
* Fixed a few issues with 'edit external host'

* Made team external url visible in team deploy admin.

* Misc minor cleanup

* Fix report css

* More report css cleanup

* Start on report sorting

* Add sorting to all reports

* Move legacy reports to reports module and post deprecation warning

* Remove reports link from admin
  • Loading branch information
sei-bstein authored May 22, 2024
1 parent 90fa4f1 commit 7b7f2cc
Show file tree
Hide file tree
Showing 52 changed files with 720 additions and 407 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ <h1 class="admin-header mb-0 pl-0">Administration</h1>
<a class="btn btn-link" routerLinkActive="active" [routerLink]="['support', 'settings']"
[routerLinkActiveOptions]="{exact: true}">Support</a>
<a class="btn btn-link" routerLinkActive="active" [routerLink]="['notifications']">Notifications</a>
<a class="btn btn-link" routerLinkActive="active" [routerLink]="['report']">Reports</a>
</nav>
<main>
<router-outlet></router-outlet>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@ <h6>Score</h6>
<h4 class="mt-4 px-3">Session Extension</h4>
<div class="form-group d-flex" *ngIf="!isExtending; else loading">
<div class="extend-by-duration flex-grow-1 mr-2">

<label class="mb-0" for="duration-input">By Duration</label>
<div class="input-group">
<input id="duration-input" class="form-control" type="number" placeholder="Extension length (in minutes)"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<!-- Copyright 2021 Carnegie Mellon University. All Rights Reserved. -->
<!-- Released under a MIT (SEI)-style license. See LICENSE.md in the project root for license information. -->

<a class="btn btn-link" routerLink="../">
<a class="btn btn-link" routerLink="/reports">
<fa-icon [icon]="faArrowLeft"></fa-icon>
<span>Back</span>
</a>
Expand All @@ -26,12 +26,14 @@ <h4>Challenge Reports</h4>
</div>
<div class="col-2 p-2 font-weight-bold">
<div>
<button class="btn btn-outline-info btn-sm mx-1" (click)="downloadChallengeStatsReport(currentGame.id)">Export Challenge Stats to CSV</button>
<button class="btn btn-outline-info btn-sm mx-1" (click)="downloadChallengeStatsReport(currentGame.id)">Export
Challenge Stats to CSV</button>
</div>
</div>
<div class="col-2 p-2 font-weight-bold">
<div>
<button class="btn btn-outline-info btn-sm mx-1" (click)="downloadChallengeDetailsReport(currentGame.id)">Export Challenge Details to CSV</button>
<button class="btn btn-outline-info btn-sm mx-1" (click)="downloadChallengeDetailsReport(currentGame.id)">Export
Challenge Details to CSV</button>
</div>
</div>
</div>
Expand All @@ -58,12 +60,15 @@ <h4>Challenge Reports</h4>
<div class="col p-2 border font-weight-bold">{{c.tag}}</div>
<div class="col p-2 border font-weight-bold">{{c.points}}</div>
<div class="col p-2 border font-weight-bold">{{c.attemptCount}}</div>
<div class="col p-2 border font-weight-bold">{{c.successCount}} / {{c.successCount / c.attemptCount | percent}}</div>
<div class="col p-2 border font-weight-bold">{{c.partialCount}} / {{c.partialCount / c.attemptCount | percent}}</div>
<div class="col p-2 border font-weight-bold">{{c.successCount}} / {{c.successCount / c.attemptCount | percent}}
</div>
<div class="col p-2 border font-weight-bold">{{c.partialCount}} / {{c.partialCount / c.attemptCount | percent}}
</div>
<div class="col p-2 border font-weight-bold">{{c.averageTime}}</div>
<div class="col p-2 border font-weight-bold">{{c.averageScore}}</div>
</div>
<div class="container pt-4 pb-4" *ngIf="challengeDetailReports && challengeDetailReports[c.id] && challengeDetailReports[c.id].visible && challengeDetailReports[c.id].parts">
<div class="container pt-4 pb-4"
*ngIf="challengeDetailReports && challengeDetailReports[c.id] && challengeDetailReports[c.id].visible && challengeDetailReports[c.id].parts">
<div class="row">
<div class="col-7 p-2 border font-weight-bold">Question</div>
<div class="col-2 p-2 border font-weight-bold">Points / % of Total</div>
Expand All @@ -73,7 +78,8 @@ <h4>Challenge Reports</h4>
<div class="col-7 p-2 border">{{i + 1}}. {{part.text}}</div>
<div class="col-2 p-2 border">{{ part.weight }} / {{ part.weight / c.points | percent}}
</div>
<div *ngIf="challengeDetailReports[c.id].attemptCount > 0" class="col-3 p-2 border">{{part.solveCount}} / {{part.solveCount / challengeDetailReports[c.id].attemptCount | percent}}</div>
<div *ngIf="challengeDetailReports[c.id].attemptCount > 0" class="col-3 p-2 border">{{part.solveCount}} /
{{part.solveCount / challengeDetailReports[c.id].attemptCount | percent}}</div>
<div *ngIf="challengeDetailReports[c.id].attemptCount == 0" class="col-3 p-2 border">{{0 | percent}}</div>
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ <h5>Challenges</h5>
<col>
<col>

<thead class="thead-light">
<thead>
<tr>
<th></th>
<th class="text-center">Created</th>
Expand Down Expand Up @@ -123,11 +123,15 @@ <h5>Challenges</h5>
<div class="card-section" *ngIf="team.externalGameHostUrl">
<h5>Game Host URL</h5>

<div class="cursor-pointer text-info" [appCopyOnClick]="team.externalGameHostUrl"
appCopyOnClickMessage="Copied this team's external host URL to your clipboard."></div>
<div class="cursor-pointer text-info"
tooltip="Copy this team's external host URL to your clipboard"
[appCopyOnClick]="team.externalGameHostUrl"
appCopyOnClickMessage="Copied this team's external host URL to your clipboard.">
{{team.externalGameHostUrl}}
</div>
</div>
</div>
<button type="button" href="#" class="btn btn-info"
<button type="button" class="btn btn-info"
[disabled]="(team.deployStatus != 'notStarted' && team.deployStatus != 'partiallyDeployed') || !canDeploy"
(click)="handlePreDeployTeamClick(ctx.game.id, team.id)">Deploy</button>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,12 @@ export class ExternalGameHostPickerComponent implements OnInit {

async ngOnInit(): Promise<void> {
await this.loadHosts(this.selectedHostId);

if (!this.selectedHostId && this.hosts.length) {
this.selectedHostId = this.hosts[0].id;
this.selectedHostIdChange.emit(this.selectedHostId);
this.selectedHostChange.emit(this.hosts[0]);
}
}

ngOnChanges(changes: SimpleChanges) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
<div class="form-group">
<label for="client-url">Client URL</label>
<input id="clientUrl" name="client-url" type="text" class="form-control px-0"
[(ngModel)]="editHost.clientUrl" placeholder="https://my-site.com/theClient" required>
[(ngModel)]="editHost.clientUrl" placeholder="https://my-site.com/the-client" required>
</div>

<div class="form-group">
Expand All @@ -32,7 +32,7 @@

<div class="form-group">
<label for="challenge-deploy-batch-size">Challenge Deploy Batch Size</label>
<input id="clientUrl" name="challenge-deploy-batch-size" type="number" class="form-control px-0"
<input id="batchSize" name="challenge-deploy-batch-size" type="number" class="form-control px-0"
[(ngModel)]="editHost.gamespaceDeployBatchSize" placeholder="2">
</div>

Expand All @@ -50,12 +50,29 @@
[(ngModel)]="editHost.teamExtendedEndpoint" placeholder="team/extended">
</div>

<div class="form-group">
<div class="form-group pb-0">
<label for="ping-endpoint">"Ping" Endpoint (GET)</label>
<input id="ping-endpoint" name="ping-endpoint" type="input" class="form-control px-0"
[(ngModel)]="editHost.pingEndpoint" placeholder="status">
<div class="input-group">
<input id="ping-endpoint" name="ping-endpoint" type="input" class="form-control"
[(ngModel)]="editHost.pingEndpoint" placeholder="status">
<div class="input-group-append">
<button class="btn btn-info" (click)="handleTryPing(editHost)"
[disabled]="!editHost.pingEndpoint">Try It</button>
</div>
</div>

<div *ngIf="tryPingResult" class="try-ping-result-container my-3">
<alert *ngIf="!tryPingResult.success" type="danger">
{{ tryPingResult.response }}
</alert>
</div>

</div>
</form>

<app-error-div [errors]="errors" footer></app-error-div>
</app-modal-content>

<ng-template #pingSuccess>
<alert type="success">All set!</alert>
</ng-template>
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,11 @@ export class ExternalHostEditorComponent implements OnInit {
public onSave?: (host: UpsertExternalGameHost) => void | Promise<void>;
protected subtitle?: string;
protected title = "New External Game Host";
protected tryPingResult?: { success: boolean; response?: string };

constructor(private externalGameService: ExternalGameService) { }
constructor(
private externalGameService: ExternalGameService,
) { }

async ngOnInit() {
if (this.hostId) {
Expand All @@ -40,4 +43,8 @@ export class ExternalHostEditorComponent implements OnInit {
if (this.onSave)
await this.onSave(host);
}

protected async handleTryPing(host: UpsertExternalGameHost) {
this.tryPingResult = await this.externalGameService.tryPingHost(host);
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<a class="btn btn-link" routerLink="../">
<a class="btn btn-link" routerLink="/reports">
<fa-icon [icon]="faArrowLeft"></fa-icon>
<span>Back</span>
</a>
Expand Down Expand Up @@ -40,7 +40,7 @@ <h4 class="mb-0">Feedback</h4>

<div class="d-flex justify-content-between">
<h2>{{currentGame?.name}}<ng-container *ngIf="currentType == 'challenge'"> - {{currentChallengeSpec?.name ?? 'All ' +
challengeSpecs?.length + ' Challenges'}}</ng-container>
challengeSpecs?.length + ' Challenges'}}</ng-container>
</h2>
<div class="align-self-center">
<button class="btn btn-outline-info" (click)="refresh()" [disabled]="!configQuestions.length">
Expand Down Expand Up @@ -86,7 +86,8 @@ <h3 class="d-inline">{{currentType | titlecase}} Overview
<div class="col-2">{{feedbackStats.responsesCount}} <span class="m-0 p-0 text-muted">responses</span></div>
<div class="col-2">{{feedbackStats.submittedCount}} <span class="m-0 p-0 text-muted">submitted</span></div>
<div class="col-2">{{feedbackStats.inProgressCount}} <span class="m-0 p-0 text-muted">in progress</span></div>
<div class="col-2">{{feedbackStats.maxResponseCount - feedbackStats.responsesCount}} <span class="m-0 p-0 text-muted">not started</span></div>
<div class="col-2">{{feedbackStats.maxResponseCount - feedbackStats.responsesCount}} <span
class="m-0 p-0 text-muted">not started</span></div>
</div>
</ng-container>
</ng-container>
Expand Down Expand Up @@ -124,9 +125,12 @@ <h3 class="d-inline">Question Summary</h3>
<ng-container *ngIf="feedbackStats; else loading">
<div *ngFor="let q of feedbackStats.questionStats" class="row border-top p-2 pl-4 text-left">
<div class="col-4">{{q.id}}) {{q.prompt}}<span *ngIf="q.required" class="required">*</span></div>
<div class="col-2">{{!!q.count ? (q.average | number:'1.2-2'): ''}}<span class="m-0 p-0 text-muted">/{{q.scaleMax}}</span></div>
<div class="col-2">{{!!q.count ? q.lowest : ''}}<span class="m-0 p-0 text-muted">/{{q.scaleMax}}</span></div>
<div class="col-2">{{!!q.count ? q.highest : ''}}<span class="m-0 p-0 text-muted">/{{q.scaleMax}}</span></div>
<div class="col-2">{{!!q.count ? (q.average | number:'1.2-2'): ''}}<span
class="m-0 p-0 text-muted">/{{q.scaleMax}}</span></div>
<div class="col-2">{{!!q.count ? q.lowest : ''}}<span class="m-0 p-0 text-muted">/{{q.scaleMax}}</span>
</div>
<div class="col-2">{{!!q.count ? q.highest : ''}}<span class="m-0 p-0 text-muted">/{{q.scaleMax}}</span>
</div>
<div class="col-2">{{q.count}}</div>
</div>
</ng-container>
Expand Down Expand Up @@ -162,19 +166,29 @@ <h3 class="d-inline">Individual Submissions</h3>
<th class="time-col">Time</th>
<th *ngIf="currentType == 'challenge'">Challenge</th>
<th>Name</th>
<th *ngFor="let q of configQuestions" [class]="q.type =='text' ? 'text-col' : 'num-col'" class="pt-1 font-weight-bold">
<th *ngFor="let q of configQuestions" [class]="q.type =='text' ? 'text-col' : 'num-col'"
class="pt-1 font-weight-bold">
<small class="q-id">{{q.id}})</small>
<div class="prompt">{{q.shortName ?? q.prompt}}</div>
</th>
</tr>
</thead>
<tbody *ngIf="feedback; else loading">
<tr *ngFor="let r of feedback; let i = index" (click)="toggleRow(i)" id="row-{{i}}" class="minimized table-row">
<td><div class="text-align-left">{{ r.timestamp | shorttime:true }}</div></td>
<td *ngIf="currentType == 'challenge'"><div>{{r.challengeId | slice:0:8}} {{r.challengeTag}}</div></td>
<td><div>{{ r.approvedName }}</div></td>
<td *ngFor="let q of r.questions" class="table-cell" [class]="(q.answer?.length ?? 0) > 500 ? 'long-text' : ''">
<div [class]="(q.answer && q.answer.length) ? '' : 'text-muted blank-cell'">{{(q.answer && q.answer.length) ? q.answer : "--"}}</div>
<tr *ngFor="let r of feedback; let i = index" (click)="toggleRow(i)" id="row-{{i}}"
class="minimized table-row">
<td>
<div class="text-align-left">{{ r.timestamp | shorttime:true }}</div>
</td>
<td *ngIf="currentType == 'challenge'">
<div>{{r.challengeId | slice:0:8}} {{r.challengeTag}}</div>
</td>
<td>
<div>{{ r.approvedName }}</div>
</td>
<td *ngFor="let q of r.questions" class="table-cell"
[class]="(q.answer?.length ?? 0) > 500 ? 'long-text' : ''">
<div [class]="(q.answer && q.answer.length) ? '' : 'text-muted blank-cell'">{{(q.answer &&
q.answer.length) ? q.answer : "--"}}</div>
</td>
</tr>
</tbody>
Expand All @@ -183,7 +197,7 @@ <h3 class="d-inline">Individual Submissions</h3>
<div *ngIf="feedback && feedback.length == 0 && skip == 0" class="m-4 text-center">
No feedback yet.
</div>

</div>

<div *ngIf="feedback" class="d-flex justify-content-between">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,9 +103,11 @@ export class GameEditorComponent implements AfterViewInit {
}

async handleExternalGameHostChanged(host: ExternalGameHost) {
this.game.externalHostId = host.id;
await firstValueFrom(this.api.update(this.game));
this.toast.showMessage(`Changed to host **${host.name}**`);
if (this.game.mode == "external" && this.game.externalHostId != host.id) {
this.game.externalHostId = host.id;
await firstValueFrom(this.api.update(this.game));
this.toast.showMessage(`Changed to host **${host.name}**`);
}
}

async handleFeedbackTemplateChange(template?: FeedbackTemplate) {
Expand All @@ -121,9 +123,14 @@ export class GameEditorComponent implements AfterViewInit {
await firstValueFrom(this.api.update(this.game));
}

handleModeChange(event: Event) {
async handleModeChange(event: Event) {
const gameMode = ((event?.target as any).value as GameEngineMode);
this.game.mode = gameMode;

if (this.game.mode != "external")
this.game.externalHostId = undefined;

await firstValueFrom(this.api.update(this.game));
}

protected handleSpecsUpdated(specs: Spec[]) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,43 +1,49 @@
<!-- Copyright 2021 Carnegie Mellon University. All Rights Reserved. -->
<!-- Released under a MIT (SEI)-style license. See LICENSE.md in the project root for license information. -->

<a class="btn btn-link" routerLink="../">
<fa-icon [icon]="faArrowLeft"></fa-icon>
<span>Back</span>
</a>

<h4>Participation Reports</h4>
<a class="btn btn-link" routerLink="/reports">
<fa-icon [icon]="faArrowLeft"></fa-icon>
<span>Back</span>
</a>

<div class="container table-dark border mb-4">
<h4>Participation Reports</h4>

<div class="container table-dark border mb-4">
<div class="row">
<div class="col-2 p-2 font-weight-bold" *ngIf="series">
<div>
<button class="btn btn-outline-info btn-sm mx-1" (click)="downloadSeriesReport()">Export Series Report to CSV</button>
<button class="btn btn-outline-info btn-sm mx-1" (click)="downloadSeriesReport()">Export Series Report to
CSV</button>
</div>
</div>
<div class="col-2 p-2 font-weight-bold" *ngIf="tracks">
<div>
<button class="btn btn-outline-info btn-sm mx-1" (click)="downloadTrackReport()">Export Track Report to CSV</button>
<button class="btn btn-outline-info btn-sm mx-1" (click)="downloadTrackReport()">Export Track Report to
CSV</button>
</div>
</div>
<div class="col-2 p-2 font-weight-bold" *ngIf="seasons">
<div>
<button class="btn btn-outline-info btn-sm mx-1" (click)="downloadSeasonReport()">Export Season Report to CSV</button>
<button class="btn btn-outline-info btn-sm mx-1" (click)="downloadSeasonReport()">Export Season Report to
CSV</button>
</div>
</div>
<div class="col-2 p-2 font-weight-bold" *ngIf="divisions">
<div>
<button class="btn btn-outline-info btn-sm mx-1" (click)="downloadDivisionReport()">Export Division Report to CSV</button>
<button class="btn btn-outline-info btn-sm mx-1" (click)="downloadDivisionReport()">Export Division Report to
CSV</button>
</div>
</div>
<div class="col-2 p-2 font-weight-bold" *ngIf="modes">
<div>
<button class="btn btn-outline-info btn-sm mx-1" (click)="downloadModeReport()">Export Mode Report to CSV</button>
<button class="btn btn-outline-info btn-sm mx-1" (click)="downloadModeReport()">Export Mode Report to
CSV</button>
</div>
</div>
<div class="col-2 p-2 front-weight-bold" *ngIf="correlations">
<div>
<button class="btn btn-outline-info btn-sm mx-1" (click)="downloadCorrelationReport()">Export Correlation Report to CSV</button>
<button class="btn btn-outline-info btn-sm mx-1" (click)="downloadCorrelationReport()">Export Correlation Report
to CSV</button>
</div>
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<!-- Copyright 2021 Carnegie Mellon University. All Rights Reserved. -->
<!-- Released under a MIT (SEI)-style license. See LICENSE.md in the project root for license information. -->

<a class="btn btn-link" routerLink="../">
<a class="btn btn-link" routerLink="/reports">
<fa-icon [icon]="faArrowLeft"></fa-icon>
<span>Back</span>
</a>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<a class="btn btn-link" routerLink="../">
<a class="btn btn-link" routerLink="/reports">
<fa-icon [icon]="fa.arrowLeft"></fa-icon>
<span>Back</span>
</a>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@
import { Component } from '@angular/core';
import { BehaviorSubject, interval, merge, Observable } from 'rxjs';
import { debounceTime, map, switchMap, tap } from 'rxjs/operators';
import { Search, SortDirection } from '../../api/models';
import { Search } from '../../api/models';
import { ApiUser, UserRole } from '../../api/user-models';
import { UserService } from '../../api/user.service';
import { fa } from '@/services/font-awesome.service';
import { SortService } from '@/services/sort.service';
import { SortDirection } from '@/core/models/sort-direction';

type UserRegistrarSort = "name" | "lastLogin" | "createdOn";

Expand Down
Loading

0 comments on commit 7b7f2cc

Please sign in to comment.