Added rudimentary statistics

This commit is contained in:
Jim Martens 2021-10-05 16:29:05 +02:00
parent f2fc85ebc6
commit d7948a94fd
13 changed files with 240 additions and 4 deletions

View File

@ -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({

View File

@ -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'

View File

@ -0,0 +1,8 @@
import { ToMinuteSecondsPipe } from './to-minute-seconds.pipe';
describe('ToMinuteSecondsPipe', () => {
it('create an instance', () => {
const pipe = new ToMinuteSecondsPipe();
expect(pipe).toBeTruthy();
});
});

View File

@ -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;
}
}

View File

@ -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>

View File

@ -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();
});
});

View File

@ -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);
}
}
}

View File

@ -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({

View File

@ -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>

View File

@ -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,