From c6d55011b6b4916260a871ee3e014d56ff54568b Mon Sep 17 00:00:00 2001 From: HardiReady Date: Sun, 12 Nov 2017 23:32:31 +0100 Subject: [PATCH] Add extra component for fraction war stats --- static/src/app/statistic/stats.routing.ts | 5 +- .../war-detail-fraction.component.css | 129 +++++++ .../war-detail-fraction.component.html | 74 ++++ .../war-detail-fraction.component.ts | 344 ++++++++++++++++++ .../war-detail-header.component.css | 13 + .../war-detail-header.component.html | 12 +- .../war-detail/war-detail.component.html | 53 --- 7 files changed, 571 insertions(+), 59 deletions(-) create mode 100644 static/src/app/statistic/war-detail-fraction/war-detail-fraction.component.css create mode 100644 static/src/app/statistic/war-detail-fraction/war-detail-fraction.component.html create mode 100644 static/src/app/statistic/war-detail-fraction/war-detail-fraction.component.ts diff --git a/static/src/app/statistic/stats.routing.ts b/static/src/app/statistic/stats.routing.ts index a1e914a..92b1188 100644 --- a/static/src/app/statistic/stats.routing.ts +++ b/static/src/app/statistic/stats.routing.ts @@ -9,6 +9,7 @@ import {ModuleWithProviders} from "@angular/core"; import {CampaignSubmitComponent} from "./campaign-submit/campaign-submit.component"; import {CampaignPlayerDetailComponent} from "./campaign-player-detail/campaign-player-detail.component"; import {WarDetailHeaderComponent} from "./war-detail-header/war-detail-header.component"; +import {WarDetailFractionComponent} from "./war-detail-fraction/war-detail-fraction.component"; export const statsRoutes: Routes = [{ @@ -49,6 +50,6 @@ export const statsRoutes: Routes = [{ export const statsRouterModule: ModuleWithProviders = RouterModule.forChild(statsRoutes); export const statsRoutingComponents = [StatisticComponent, StatisticOverviewComponent, CampaignSubmitComponent, - WarListComponent, WarSubmitComponent, WarDetailHeaderComponent, WarDetailComponent, CampaignPlayerDetailComponent, - WarItemComponent]; + WarListComponent, WarSubmitComponent, WarDetailHeaderComponent, WarDetailComponent, WarDetailFractionComponent, + CampaignPlayerDetailComponent, WarItemComponent]; diff --git a/static/src/app/statistic/war-detail-fraction/war-detail-fraction.component.css b/static/src/app/statistic/war-detail-fraction/war-detail-fraction.component.css new file mode 100644 index 0000000..ac69342 --- /dev/null +++ b/static/src/app/statistic/war-detail-fraction/war-detail-fraction.component.css @@ -0,0 +1,129 @@ +.vertical-spacer { + height: 205px; + float: left; + width: 4%; +} + +.head-field { + font-size: 24px; + margin-top: 10px; + margin-bottom: 10px; +} + +@media screen and (min-width: 1500px) { + .vertical-spacer { + width: 15%; + } +} + +@media screen and (min-width: 2000px) { + .vertical-spacer { + width: 20%; + } +} + +.overview { + overflow-y: scroll; + overflow-x: hidden; + border-left: thin solid lightgrey; + bottom: 20px; + height: 100vh; +} + +.player-name { + font-weight: bold; +} + +/* ########### TABS ########### */ + +:host /deep/ .nav-tabs { + padding-left: 35% !important; +} + +:host /deep/ .nav-link { + background: #4b4b4b; + color: white; +} + +:host /deep/ .nav-link:hover { + background: #afafaf; + color: #000; +} + +:host /deep/ .nav-tabs > li.active > a { + background: #222222; + color: white; +} + +/* ########### DATATABLE ########### */ + +:host /deep/ .datatable-header { + background: #222222; + font-weight: 700; + border-radius: 10px 10px 0 0; + color: white; +} + +:host /deep/ span.datatable-header-cell-label, :host /deep/ div.datatable-body-cell-label { + padding-left: 8px; +} + +:host /deep/ .ngx-datatable .datatable-header { + /*vertical center alignment*/ + display: table-cell; + vertical-align: middle; +} + +:host /deep/ .ngx-datatable .datatable-body .datatable-body-row > div { + /*vertical alignment*/ + position: relative; + top: 10px; +} + +:host /deep/ .datatable-body-row { + color: #222222; + border-bottom: 1px solid grey; + cursor: pointer; +} + +:host /deep/ .datatable-body-row:hover { + background-color: #f7f7f7; +} + +/* ########### CHART-TAB ######## */ + +.btn-dark { + background: #4b4b4b; + color: #f5f5f5; + border-color: #000; +} + +.btn-dark:hover { + background: #afafaf; + color: #f5f5f5; +} + +.btn-dark.active { + background: #222222; +} + +.chart-container { + width: 95%; + margin: 2%; + min-width: 900px; + height: 600px; + padding: 15px; + float: left; +} + +.chart-select-group { + width: 50%; + margin: auto; + position: inherit; + display: block; + vertical-align: middle; +} + +/*.dropdown-menu > li > a {*/ + /*cursor: pointer;*/ +/*}*/ diff --git a/static/src/app/statistic/war-detail-fraction/war-detail-fraction.component.html b/static/src/app/statistic/war-detail-fraction/war-detail-fraction.component.html new file mode 100644 index 0000000..82719c5 --- /dev/null +++ b/static/src/app/statistic/war-detail-fraction/war-detail-fraction.component.html @@ -0,0 +1,74 @@ + + + + + + + + + + + + + + + + + + + +
+
+ + + + + + + + +
+ +
+ + +
+ +
+ + +
+
diff --git a/static/src/app/statistic/war-detail-fraction/war-detail-fraction.component.ts b/static/src/app/statistic/war-detail-fraction/war-detail-fraction.component.ts new file mode 100644 index 0000000..cec9da3 --- /dev/null +++ b/static/src/app/statistic/war-detail-fraction/war-detail-fraction.component.ts @@ -0,0 +1,344 @@ +import {Component, ElementRef, SimpleChanges, ViewChild} from "@angular/core"; +import {LogsService} from "../../services/logs/logs.service"; +import * as d3 from "d3"; +import {ChartUtils} from "../../utils/chart-utils"; +import {Fraction} from "../../utils/fraction.enum"; +import {War} from "../../models/model-interfaces"; + + +@Component({ + selector: 'war-detail-fraction', + templateUrl: './war-detail-fraction.component.html', + inputs: ['war'], + styleUrls: ['./war-detail-fraction.component.css', '../../style/list-entry.css', '../../style/hide-scrollbar.css'] +}) +export class WarDetailFractionComponent { + + readonly fraction = Fraction; + + @ViewChild('overview') private overviewContainer: ElementRef; + + war: War; + + logData: any; + + startDateObj: Date; + + initialized: any; + + public chartSelectModel: string; + + lineChartData: any[] = []; + areaChartData: any[] = []; + + tmpPointData; + tmpBudgetData; + tmpKillData; + tmpFrienlyFireData; + tmpTransportData; + tmpReviveData; + tmpStabilizeData; + tmpFlagCaptureData; + + colorScheme = { + domain: [Fraction.COLOR_BLUFOR, Fraction.COLOR_OPFOR] + }; + + labelPoints = 'Punkte'; + labelBudget = 'Budget'; + labelKill = 'Kills'; + labelFriendlyFire = 'FriendlyFire'; + labelTransport = 'Lufttransport'; + labelRevive = 'Revive'; + labelStabilize = 'Stabilisiert'; + labelFlag = 'Flaggenbesitz'; + lineChartLabel: string = this.labelPoints; + + showLineChart = true; + stepCurve = d3.curveStepAfter; + gradient = false; + yAxis = true; + xAxis = true; + legend = false; + legendTitle = false; + showXAxisLabel = false; + showYAxisLabel = true; + autoscale = true; + timeline = false; + roundDomains = true; + + constructor(private logsService: LogsService) { + } + + ngOnInit() { + } + + ngOnChanges(changes: SimpleChanges) { + if (changes.war) { + this.initialized = { + budget: false, + kill: false, + revive: false, + transport: false, + flag: false + }; + Object.assign(this, [this.lineChartData, this.areaChartData]); + this.chartSelectModel = this.labelPoints; + + this.startDateObj = new Date(this.war.date); + this.startDateObj.setHours(0); + this.startDateObj.setMinutes(1); + this.loadFractionData(); + } + } + + selectChart() { + if (this.chartSelectModel !== this.labelFlag) { + this.showLineChart = true; + this.lineChartLabel = this.chartSelectModel; + switch (this.chartSelectModel) { + case this.labelPoints: + this.lineChartData = this.tmpPointData; + break; + case this.labelBudget: + this.initBudgetData(); + this.lineChartData = this.tmpBudgetData; + break; + case this.labelKill: + this.initKillData(); + this.lineChartData = this.tmpKillData; + break; + case this.labelFriendlyFire: + this.initKillData(); + this.lineChartData = this.tmpFrienlyFireData; + break; + case this.labelRevive: + this.initRevive(); + this.lineChartData = this.tmpReviveData; + break; + case this.labelStabilize: + this.initRevive(); + this.lineChartData = this.tmpStabilizeData; + break; + case this.labelTransport: + this.initTransportData(); + this.lineChartData = this.tmpTransportData; + break; + } + } else { + this.initFlagHoldData(); + this.showLineChart = false; + this.areaChartData = this.tmpFlagCaptureData; + } + } + + loadFractionData() { + this.initializeTempCollections(); + + this.logsService.getFullLog(this.war._id).subscribe((data) => { + this.logData = data; + this.initPointData(); + this.showLineChart = true; + this.lineChartLabel = this.labelPoints; + this.lineChartData = this.tmpPointData; + }); + } + + initPointData() { + this.logData.points.forEach(pointEntry => { + this.tmpPointData[0].series.push(ChartUtils.getSeriesEntry(new Date(pointEntry.time), pointEntry.ptBlufor)); + this.tmpPointData[1].series.push(ChartUtils.getSeriesEntry(new Date(pointEntry.time), pointEntry.ptOpfor)); + }); + this.addFinalTimeData(this.tmpPointData); + } + + initBudgetData() { + if (this.initialized.budget) { + return; + } + this.logData.budget.forEach(budgetEntry => { + const budgetEntryDate = new Date(budgetEntry.time); + const fractionChange = budgetEntry.fraction === 'BLUFOR' ? 0 : 1; + const fractionOld = budgetEntry.fraction !== 'BLUFOR' ? 0 : 1; + + if (this.isTwoMinutesAhead(budgetEntryDate, this.tmpBudgetData)) { + this.tmpBudgetData[fractionChange].series.push(ChartUtils.getSeriesEntry(new Date(budgetEntry.time), budgetEntry.newBudget)); + this.tmpBudgetData[fractionOld].series.push(ChartUtils.getSeriesEntry(new Date(budgetEntry.time), + this.tmpBudgetData[fractionOld].series[this.tmpBudgetData[fractionOld].series.length - 1].value)); + } + }); + this.addFinalTimeData(this.tmpBudgetData); + this.initialized.budget = true; + } + + initKillData() { + if (this.initialized.kill) { + return; + } + let killCountBlufor = 0, killCountOpfor = 0, ffKillCountBlufor = 0, ffKillCountOpfor = 0; + + for (const {killEntry, index} of this.logData.kill.map((killEntry, index) => ({killEntry, index}))) { + const killEntryDate = new Date(killEntry.time); + if (killEntry.friendlyFire === false) { + if (killEntry.fraction === 'BLUFOR') { + killCountBlufor++; + } + if (killEntry.fraction === 'OPFOR') { + killCountOpfor++; + } + if (this.isTwoMinutesAhead(killEntryDate, this.tmpKillData)) { + this.tmpKillData[0].series.push(ChartUtils.getSeriesEntry(killEntryDate, killCountBlufor)); + this.tmpKillData[1].series.push(ChartUtils.getSeriesEntry(killEntryDate, killCountOpfor)); + } + } else { + if (killEntry.fraction === 'BLUFOR') { + ffKillCountBlufor++; + } + if (killEntry.fraction === 'OPFOR') { + ffKillCountOpfor++; + } + if (this.isTwoMinutesAhead(killEntryDate, this.tmpFrienlyFireData)) { + this.tmpFrienlyFireData[0].series.push(ChartUtils.getSeriesEntry(killEntryDate, ffKillCountBlufor)); + this.tmpFrienlyFireData[1].series.push(ChartUtils.getSeriesEntry(killEntryDate, ffKillCountOpfor)); + } + } + if (index === this.logData.kill.length - 1) { + this.tmpKillData[0].series.push(ChartUtils.getSeriesEntry(killEntryDate, killCountBlufor)); + this.tmpKillData[1].series.push(ChartUtils.getSeriesEntry(killEntryDate, killCountOpfor)); + this.tmpFrienlyFireData[0].series.push(ChartUtils.getSeriesEntry(killEntryDate, ffKillCountBlufor)); + this.tmpFrienlyFireData[1].series.push(ChartUtils.getSeriesEntry(killEntryDate, ffKillCountOpfor)); + } + } + + this.addFinalTimeData(this.tmpKillData); + this.addFinalTimeData(this.tmpFrienlyFireData) + this.initialized.kill = true; + } + + initRevive() { + if (this.initialized.revive) { + return; + } + let reviveCountBlufor = 0, reviveCountOpfor = 0, stabilizeCountBlufor = 0, stabilizeCountOpfor = 0; + for (const {reviveEntry, index} of this.logData.revive.map((reviveEntry, index) => ({reviveEntry, index}))) { + const reviveEntryDate = new Date(reviveEntry.time); + if (reviveEntry.stabilized === false) { + if (reviveEntry.fraction === 'BLUFOR') { + reviveCountBlufor++; + } else { + reviveCountOpfor++; + } + if (this.isTwoMinutesAhead(reviveEntryDate, this.tmpReviveData)) { + this.tmpReviveData[0].series.push(ChartUtils.getSeriesEntry(reviveEntryDate, reviveCountBlufor)); + this.tmpReviveData[1].series.push(ChartUtils.getSeriesEntry(reviveEntryDate, reviveCountOpfor)); + } + } else { + if (reviveEntry.fraction === 'BLUFOR') { + stabilizeCountBlufor++; + } else { + stabilizeCountOpfor++; + } + if (this.isTwoMinutesAhead(reviveEntryDate, this.tmpStabilizeData)) { + this.tmpStabilizeData[0].series.push(ChartUtils.getSeriesEntry(reviveEntryDate, stabilizeCountBlufor)); + this.tmpStabilizeData[1].series.push(ChartUtils.getSeriesEntry(reviveEntryDate, stabilizeCountOpfor)); + } + } + if (index === this.logData.revive.length - 1) { + this.tmpReviveData[0].series.push(ChartUtils.getSeriesEntry(reviveEntryDate, reviveCountBlufor)); + this.tmpReviveData[1].series.push(ChartUtils.getSeriesEntry(reviveEntryDate, reviveCountOpfor)); + this.tmpStabilizeData[0].series.push(ChartUtils.getSeriesEntry(reviveEntryDate, stabilizeCountBlufor)); + this.tmpStabilizeData[1].series.push(ChartUtils.getSeriesEntry(reviveEntryDate, stabilizeCountOpfor)); + } + } + this.addFinalTimeData(this.tmpReviveData); + this.addFinalTimeData(this.tmpStabilizeData); + this.initialized.revive = true; + } + + initTransportData() { + if (this.initialized.transport) { + return; + } + let transportCountBlufor = 0, transportCountOpfor = 0; + for (const {transportEntry, index} of this.logData.transport.map((transportEntry, index) => ({ + transportEntry, + index + }))) { + const transportEntryDate = new Date(transportEntry.time); + if (transportEntry.fraction === 'BLUFOR') { + transportCountBlufor++; + } else { + transportCountOpfor++; + } + if (this.isTwoMinutesAhead(transportEntryDate, this.tmpTransportData) || index === this.logData.transport.length - 1) { + this.tmpTransportData[0].series.push(ChartUtils.getSeriesEntry(transportEntryDate, transportCountBlufor)); + this.tmpTransportData[1].series.push(ChartUtils.getSeriesEntry(transportEntryDate, transportCountOpfor)); + } + } + this.addFinalTimeData(this.tmpTransportData); + this.initialized.transport = true; + + } + + initFlagHoldData() { + if (this.initialized.flag) { + return; + } + let flagStatusBlufor = true; + let flagStatusOpfor = true; + this.tmpFlagCaptureData[0].series.push(ChartUtils.getSeriesEntry(this.startDateObj, flagStatusBlufor)); + this.tmpFlagCaptureData[1].series.push(ChartUtils.getSeriesEntry(this.startDateObj, flagStatusOpfor)); + + this.logData.flag.forEach(flagEntry => { + if (flagEntry.flagFraction === 'BLUFOR') { + flagStatusBlufor = !flagEntry.capture; + } else { + flagStatusOpfor = !flagEntry.capture; + } + this.tmpFlagCaptureData[flagEntry.flagFraction === 'BLUFOR' ? 0 : 1].series.push( + ChartUtils.getSeriesEntry(new Date(flagEntry.time), flagEntry.flagFraction === 'BLUFOR' ? flagStatusBlufor : flagStatusOpfor) + ) + }); + + this.addFinalTimeData(this.tmpFlagCaptureData); + this.initialized.flag = true; + } + + protected isTwoMinutesAhead(entryDate: Date, tmpData: any): boolean { + return entryDate.getTime() >= tmpData[0].series[tmpData[0].series.length - 1].name.getTime() + (1.5 * 60000) + } + + initializeTempCollections() { + this.tmpPointData = ChartUtils.getMultiDataArray(Fraction.BLUFOR, Fraction.OPFOR); + this.tmpBudgetData = ChartUtils.getMultiDataArray(Fraction.BLUFOR, Fraction.OPFOR); + this.tmpKillData = ChartUtils.getMultiDataArray(Fraction.BLUFOR, Fraction.OPFOR); + this.tmpFrienlyFireData = ChartUtils.getMultiDataArray(Fraction.BLUFOR, Fraction.OPFOR); + this.tmpTransportData = ChartUtils.getMultiDataArray(Fraction.BLUFOR, Fraction.OPFOR); + this.tmpReviveData = ChartUtils.getMultiDataArray(Fraction.BLUFOR, Fraction.OPFOR); + this.tmpStabilizeData = ChartUtils.getMultiDataArray(Fraction.BLUFOR, Fraction.OPFOR); + this.tmpFlagCaptureData = ChartUtils.getMultiDataArray(Fraction.BLUFOR, Fraction.OPFOR); + + [this.tmpKillData, this.tmpFrienlyFireData, this.tmpReviveData, this.tmpStabilizeData, this.tmpTransportData].forEach(tmp => { + [0, 1].forEach(index => { + tmp[index].series.push(ChartUtils.getSeriesEntry(this.startDateObj, 0)); + }) + }); + this.tmpBudgetData[0].series.push(ChartUtils.getSeriesEntry(this.startDateObj, this.war.budgetBlufor)); + this.tmpBudgetData[1].series.push(ChartUtils.getSeriesEntry(this.startDateObj, this.war.budgetOpfor)); + } + + addFinalTimeData(tmpCollection) { + const endDate = new Date(this.war.endDate); + if (tmpCollection === this.tmpBudgetData) { + this.tmpBudgetData[0].series.push(ChartUtils.getSeriesEntry(endDate, this.war.endBudgetBlufor)); + this.tmpBudgetData[1].series.push(ChartUtils.getSeriesEntry(endDate, this.war.endBudgetOpfor)); + } else { + for (let j in [0, 1]) { + if (tmpCollection[j].series[tmpCollection[j].series.length - 1].name < endDate) { + tmpCollection[j].series.push(ChartUtils.getSeriesEntry(endDate, tmpCollection[j].series[tmpCollection[j].series.length - 1].value)); + } + } + } + } + +} diff --git a/static/src/app/statistic/war-detail-header/war-detail-header.component.css b/static/src/app/statistic/war-detail-header/war-detail-header.component.css index d727ec1..9273124 100644 --- a/static/src/app/statistic/war-detail-header/war-detail-header.component.css +++ b/static/src/app/statistic/war-detail-header/war-detail-header.component.css @@ -13,3 +13,16 @@ .war-header { border-bottom: thin solid lightgrey; } + +.nav-tabs > li.active > a { + background: #222222; +} + +.nav-tabs > li > a { + background: #4b4b4b; +} + +.nav-link { + cursor: pointer !important; + color: #FFF !important; +} diff --git a/static/src/app/statistic/war-detail-header/war-detail-header.component.html b/static/src/app/statistic/war-detail-header/war-detail-header.component.html index a5c31c3..c0f77ec 100644 --- a/static/src/app/statistic/war-detail-header/war-detail-header.component.html +++ b/static/src/app/statistic/war-detail-header/war-detail-header.component.html @@ -58,9 +58,13 @@ - - + + + + + + + diff --git a/static/src/app/statistic/war-detail/war-detail.component.html b/static/src/app/statistic/war-detail/war-detail.component.html index 3a1b31e..780c37d 100644 --- a/static/src/app/statistic/war-detail/war-detail.component.html +++ b/static/src/app/statistic/war-detail/war-detail.component.html @@ -1,5 +1,4 @@
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -