Added rudimentary statistics
This commit is contained in:
parent
f2fc85ebc6
commit
d7948a94fd
|
@ -5,7 +5,7 @@ import {Observable, Subscription} from 'rxjs';
|
|||
|
||||
import {Faction} from '../../administration/factions/faction';
|
||||
import {RecordingState} from './recording-state';
|
||||
import {SpeechType} from './speech-type';
|
||||
import {SpeechType} from '../../../shared/speech-type';
|
||||
import {SpeechTimeService} from '../../shared/speech-time.service';
|
||||
|
||||
@Component({
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { Injectable } from '@angular/core';
|
||||
import {AngularFireDatabase} from '@angular/fire/compat/database';
|
||||
import {SpeechType} from '../edit/time-tracking/speech-type';
|
||||
import {Speech} from '../edit/time-tracking/speech';
|
||||
import {SpeechType} from '../../shared/speech-type';
|
||||
import {Speech} from '../../shared/speech';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
import { ToMinuteSecondsPipe } from './to-minute-seconds.pipe';
|
||||
|
||||
describe('ToMinuteSecondsPipe', () => {
|
||||
it('create an instance', () => {
|
||||
const pipe = new ToMinuteSecondsPipe();
|
||||
expect(pipe).toBeTruthy();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,18 @@
|
|||
import { Pipe, PipeTransform } from '@angular/core';
|
||||
|
||||
@Pipe({
|
||||
name: 'toMinuteSeconds'
|
||||
})
|
||||
export class ToMinuteSecondsPipe implements PipeTransform {
|
||||
|
||||
transform(value: number|undefined, ...args: unknown[]): string {
|
||||
if (value == null) {
|
||||
return '';
|
||||
}
|
||||
const seconds = Math.floor(value / 1000);
|
||||
const secondsLeft = seconds % 60;
|
||||
const minutes = (seconds - secondsLeft) / 60;
|
||||
return minutes + ':' + secondsLeft;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
<header>
|
||||
<app-navbar [currentPage]="'statistics'"></app-navbar>
|
||||
</header>
|
||||
<main class="container">
|
||||
<div class="row mt-3">
|
||||
<h1>{{(session | async)?.payload?.val()?.body}} Sitzung Nr. {{(session | async)?.payload?.val()?.number}},
|
||||
{{(session | async)?.payload?.val()?.date}}</h1>
|
||||
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Fraktion</th>
|
||||
<th>Gesamtrededauer (mm:ss)</th>
|
||||
<th>Gesamtdauer Zwischenrufe (mm:ss)</th>
|
||||
<th>Kürzeste Rede (mm:ss)</th>
|
||||
<th>Längste Rede (mm:ss)</th>
|
||||
<th>Anzahl Sitze</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Insgesamt</td>
|
||||
<td>{{totalSpeechTime | toMinuteSeconds}} (100%)</td>
|
||||
<td>{{totalCommentaryTime | toMinuteSeconds}} (100%)</td>
|
||||
<td>{{shortestSpeech?.timeInMilliseconds | toMinuteSeconds}}</td>
|
||||
<td>{{longestSpeech?.timeInMilliseconds | toMinuteSeconds}}</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<ng-container *ngFor="let faction of factionInBodyService.factionsInBodies | async">
|
||||
<tr *ngIf="faction.payload.exists()">
|
||||
<td>{{faction.payload.val()?.faction}}</td>
|
||||
<td>{{speechTimesPerFaction.get(faction.payload.val()?.factionKey || '') | toMinuteSeconds}}
|
||||
<span *ngIf="proportionOfSpeechTimePerFaction.get(faction.payload.val()?.factionKey || '') != null">
|
||||
({{proportionOfSpeechTimePerFaction.get(faction.payload.val()?.factionKey || '') | percent}})
|
||||
</span>
|
||||
</td>
|
||||
<td>{{commentaryTimesPerFaction.get(faction.payload.val()?.factionKey || '') | toMinuteSeconds}}
|
||||
<span *ngIf="proportionOfCommentaryTimePerFaction.get(faction.payload.val()?.factionKey || '') != null">
|
||||
({{proportionOfCommentaryTimePerFaction.get(faction.payload.val()?.factionKey || '') | percent}})
|
||||
</span>
|
||||
</td>
|
||||
<td>{{shortestSpeechPerFaction.get(faction.payload.val()?.factionKey || '')?.timeInMilliseconds | toMinuteSeconds}}</td>
|
||||
<td>{{longestSpeechPerFaction.get(faction.payload.val()?.factionKey || '')?.timeInMilliseconds | toMinuteSeconds}}</td>
|
||||
<td>{{faction.payload.val()?.size}}</td>
|
||||
</tr>
|
||||
</ng-container>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</main>
|
|
@ -0,0 +1,25 @@
|
|||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { SessionStatisticsComponent } from './session-statistics.component';
|
||||
|
||||
describe('SessionStatisticsComponent', () => {
|
||||
let component: SessionStatisticsComponent;
|
||||
let fixture: ComponentFixture<SessionStatisticsComponent>;
|
||||
|
||||
beforeEach(async () => {
|
||||
await TestBed.configureTestingModule({
|
||||
declarations: [ SessionStatisticsComponent ]
|
||||
})
|
||||
.compileComponents();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(SessionStatisticsComponent);
|
||||
component = fixture.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,119 @@
|
|||
import {Component, OnDestroy, OnInit} from '@angular/core';
|
||||
import {ActivatedRoute} from '@angular/router';
|
||||
import {Observable, Subscription} from 'rxjs';
|
||||
import {Speech} from '../../shared/speech';
|
||||
import {AngularFireDatabase, SnapshotAction} from '@angular/fire/compat/database';
|
||||
import {map} from 'rxjs/operators';
|
||||
import {FactionInBodyService} from '../../auth/shared/faction-in-body.service';
|
||||
import {SpeechType} from '../../shared/speech-type';
|
||||
import {Session} from '../../auth/edit/session';
|
||||
|
||||
@Component({
|
||||
selector: 'app-session-statistics',
|
||||
templateUrl: './session-statistics.component.html',
|
||||
styleUrls: ['./session-statistics.component.scss']
|
||||
})
|
||||
export class SessionStatisticsComponent implements OnInit, OnDestroy {
|
||||
public totalSpeechTime: number = 0;
|
||||
public totalCommentaryTime: number = 0;
|
||||
public longestSpeech: Speech | undefined;
|
||||
public shortestSpeech: Speech | undefined;
|
||||
public readonly speechTimesPerFaction: Map<string, number> = new Map<string, number>();
|
||||
public readonly commentaryTimesPerFaction: Map<string, number> = new Map<string, number>();
|
||||
public readonly shortestSpeechPerFaction: Map<string, Speech> = new Map<string, Speech>();
|
||||
public readonly longestSpeechPerFaction: Map<string, Speech> = new Map<string, Speech>();
|
||||
public readonly proportionOfSpeechTimePerFaction: Map<string, number> = new Map<string, number>();
|
||||
public readonly proportionOfCommentaryTimePerFaction: Map<string, number> = new Map<string, number>();
|
||||
public session: Observable<SnapshotAction<Session>> = new Observable<SnapshotAction<Session>>();
|
||||
|
||||
private sessionKey: string | undefined;
|
||||
private speechTimes: Observable<SnapshotAction<Speech>[]> = new Observable<SnapshotAction<Speech>[]>();
|
||||
private subscriptions: Subscription[] = [];
|
||||
|
||||
constructor(private database: AngularFireDatabase,
|
||||
private route: ActivatedRoute,
|
||||
public factionInBodyService: FactionInBodyService) {
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
this.sessionKey = this.route.snapshot.params['sessionKey'];
|
||||
const speechTimesRef = this.database.list<Speech>('speechTimes');
|
||||
const sessionRef = this.database.object<Session>('sessions/' + this.sessionKey);
|
||||
this.session = sessionRef.snapshotChanges();
|
||||
|
||||
this.speechTimes = speechTimesRef.snapshotChanges().pipe(
|
||||
map(speechTimesList => {
|
||||
return speechTimesList.filter(speechTime => speechTime.payload.val()?.sessionKey == this.sessionKey);
|
||||
})
|
||||
);
|
||||
|
||||
this.subscriptions.push(
|
||||
this.speechTimes.subscribe((speechTimes) => this.calculateStatistics(speechTimes)),
|
||||
);
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.subscriptions.forEach(sub => sub.unsubscribe());
|
||||
}
|
||||
|
||||
private calculateStatistics(speechTimes: SnapshotAction<Speech>[]): void {
|
||||
this.totalCommentaryTime = 0;
|
||||
this.totalSpeechTime = 0;
|
||||
this.speechTimesPerFaction.clear();
|
||||
this.commentaryTimesPerFaction.clear();
|
||||
this.shortestSpeechPerFaction.clear();
|
||||
this.longestSpeechPerFaction.clear();
|
||||
this.proportionOfCommentaryTimePerFaction.clear();
|
||||
this.proportionOfSpeechTimePerFaction.clear();
|
||||
this.shortestSpeech = undefined;
|
||||
this.longestSpeech = undefined;
|
||||
|
||||
for (const speechTime of speechTimes) {
|
||||
const speech = speechTime.payload.val();
|
||||
if (speech == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (speech.type == SpeechType.SPEECH) {
|
||||
this.totalSpeechTime += speech.timeInMilliseconds;
|
||||
const speechTime = this.speechTimesPerFaction.get(speech.factionKey);
|
||||
this.speechTimesPerFaction.set(speech.factionKey, speech.timeInMilliseconds + (speechTime ? speechTime : 0));
|
||||
if (this.shortestSpeech != null && speech.timeInMilliseconds < this.shortestSpeech.timeInMilliseconds) {
|
||||
this.shortestSpeech = speech;
|
||||
} else if (this.shortestSpeech == null) {
|
||||
this.shortestSpeech = speech;
|
||||
}
|
||||
if (this.longestSpeech != null && speech.timeInMilliseconds > this.longestSpeech.timeInMilliseconds) {
|
||||
this.longestSpeech = speech;
|
||||
} else if (this.longestSpeech == null) {
|
||||
this.longestSpeech = speech;
|
||||
}
|
||||
const shortestSpeechFaction = this.shortestSpeechPerFaction.get(speech.factionKey);
|
||||
if (shortestSpeechFaction != null && speech.timeInMilliseconds < shortestSpeechFaction.timeInMilliseconds) {
|
||||
this.shortestSpeechPerFaction.set(speech.factionKey, speech);
|
||||
} else if (shortestSpeechFaction == null) {
|
||||
this.shortestSpeechPerFaction.set(speech.factionKey, speech);
|
||||
}
|
||||
const longestSpeechFaction = this.longestSpeechPerFaction.get(speech.factionKey);
|
||||
if (longestSpeechFaction != null && speech.timeInMilliseconds > longestSpeechFaction.timeInMilliseconds) {
|
||||
this.longestSpeechPerFaction.set(speech.factionKey, speech);
|
||||
} else if (longestSpeechFaction == null) {
|
||||
this.longestSpeechPerFaction.set(speech.factionKey, speech);
|
||||
}
|
||||
} else if (speech.type == SpeechType.COMMENTARY) {
|
||||
this.totalCommentaryTime += speech.timeInMilliseconds;
|
||||
const commentaryTime = this.commentaryTimesPerFaction.get(speech.factionKey);
|
||||
this.commentaryTimesPerFaction.set(speech.factionKey, speech.timeInMilliseconds + (commentaryTime ? commentaryTime : 0));
|
||||
}
|
||||
}
|
||||
|
||||
for (const factionKey of this.speechTimesPerFaction.keys()) {
|
||||
const speechTimeFaction = this.speechTimesPerFaction.get(factionKey);
|
||||
this.proportionOfSpeechTimePerFaction.set(factionKey, (speechTimeFaction ? speechTimeFaction : 0) / this.totalSpeechTime);
|
||||
}
|
||||
for (const factionKey of this.commentaryTimesPerFaction.keys()) {
|
||||
const commentaryTimeFaction = this.commentaryTimesPerFaction.get(factionKey);
|
||||
this.proportionOfCommentaryTimePerFaction.set(factionKey, (commentaryTimeFaction ? commentaryTimeFaction : 0) / this.totalCommentaryTime);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,9 +1,14 @@
|
|||
import { NgModule } from '@angular/core';
|
||||
import { RouterModule, Routes } from '@angular/router';
|
||||
import {StatisticsComponent} from './statistics.component';
|
||||
import {SessionStatisticsComponent} from './session-statistics/session-statistics.component';
|
||||
|
||||
const routes: Routes = [
|
||||
{path: '', component: StatisticsComponent},
|
||||
{
|
||||
path: '',
|
||||
component: StatisticsComponent,
|
||||
},
|
||||
{path: ':sessionKey', component: SessionStatisticsComponent},
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
|
|
|
@ -13,4 +13,11 @@
|
|||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mt-3">
|
||||
<ul>
|
||||
<li *ngFor="let session of sessionInBodyService.sessions | async">
|
||||
<a [routerLink]="session.payload.key" routerLinkActive="">{{session.payload.val()?.body}} Sitzung Nr. {{session.payload.val()?.number}}, {{session.payload.val()?.date}}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</main>
|
||||
|
|
|
@ -4,11 +4,15 @@ import {CommonModule} from '@angular/common';
|
|||
import {StatisticsRoutingModule} from './statistics-routing.module';
|
||||
import {NavbarModule} from '../navbar/navbar.module';
|
||||
import {StatisticsComponent} from './statistics.component';
|
||||
import { SessionStatisticsComponent } from './session-statistics/session-statistics.component';
|
||||
import {ToMinuteSecondsPipe} from '../shared/to-minute-seconds.pipe';
|
||||
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
StatisticsComponent,
|
||||
SessionStatisticsComponent,
|
||||
ToMinuteSecondsPipe,
|
||||
],
|
||||
imports: [
|
||||
CommonModule,
|
||||
|
|
Loading…
Reference in New Issue