Merge branch 'feature/statistic-charts' of hardi/opt-cc into master

pull/4/head v1.2.0
hardi 2017-07-30 10:58:46 +02:00 committed by HardiReady
commit e8f8936b5e
12 changed files with 2976 additions and 2021 deletions

View File

@ -24,6 +24,18 @@ const WarSchema = new Schema({
set: v => Math.round(v), set: v => Math.round(v),
required: true required: true
}, },
playersBlufor: {
type: Number,
get: v => Math.round(v),
set: v => Math.round(v),
default: 0
},
playersOpfor: {
type: Number,
get: v => Math.round(v),
set: v => Math.round(v),
default: 0
},
bestPlayerId: { bestPlayerId: {
type: mongoose.Schema.Types.ObjectId, type: mongoose.Schema.Types.ObjectId,
ref: 'Player', ref: 'Player',

2540
api/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -51,11 +51,11 @@ wars.route('/')
const war = new WarModel(body); const war = new WarModel(body);
if (req.file) { if (req.file) {
war.save((err, item) => { war.save((err, war) => {
if (err) { if (err) {
return next(err); return next(err);
} }
const folderName = __dirname + '/../resource/logs/' + item._id; const folderName = __dirname + '/../resource/logs/' + war._id;
mkdirp(folderName, function (err) { mkdirp(folderName, function (err) {
if (err) { if (err) {
return next(err); return next(err);
@ -65,7 +65,7 @@ wars.route('/')
return next(err); return next(err);
} }
//TODO: combine run and clean in one script, so log file gets touched only once //TODO: combine run and clean in one script, so log file gets touched only once
exec(__dirname + '/../war-parser/run.sh ' + folderName + ' ' + item._id, (error, stdout) => { exec(__dirname + '/../war-parser/run.sh ' + folderName + ' ' + war._id, (error, stdout) => {
if (error) { if (error) {
return next(error); return next(error);
} }
@ -74,14 +74,33 @@ wars.route('/')
return next(error); return next(error);
} }
let obj = JSON.parse(`${stdout}`); let obj = JSON.parse(`${stdout}`);
PlayerModel.create(obj, function (err) { for (let i = 0; i < obj.length; i++) {
if (err) { if (obj[i].fraction === 'BLUFOR') {
return next(err); war.playersBlufor++;
} else {
war.playersOpfor++;
} }
res.status(codes.created); }
res.locals.items = item;
return next(); WarModel.findByIdAndUpdate(war._id, war, {new: true}, (err, item) => {
}); if (err) {
err.status = codes.wrongrequest;
}
else if (!item) {
err = new Error("item not found");
err.status = codes.notfound;
}
else {
PlayerModel.create(obj, function (err) {
if (err) {
return next(err);
}
res.status(codes.created);
res.locals.items = war;
return next();
});
}
})
}); });
}); });
}); });

2220
static/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -11,19 +11,22 @@
"e2e": "ng e2e --serve=false" "e2e": "ng e2e --serve=false"
}, },
"dependencies": { "dependencies": {
"@angular/cli": "1.0.5", "@angular/animations": "^4.3.2",
"@angular/common": "^4.1.1", "@angular/cli": "^1.2.6",
"@angular/compiler": "^4.1.1", "@angular/common": "^4.3.2",
"@angular/compiler-cli": "^4.1.1", "@angular/compiler": "^4.3.2",
"@angular/core": "^4.1.1", "@angular/compiler-cli": "^4.3.2",
"@angular/forms": "^4.1.1", "@angular/core": "^4.3.2",
"@angular/http": "^4.1.1", "@angular/forms": "^4.3.2",
"@angular/platform-browser": "^4.1.1", "@angular/http": "^4.3.2",
"@angular/platform-browser-dynamic": "^4.1.1", "@angular/platform-browser": "^4.3.2",
"@angular/router": "^4.1.1", "@angular/platform-browser-dynamic": "^4.3.2",
"@angular/router": "^4.3.2",
"@swimlane/ngx-charts": "^6.0.1",
"angular2-datatable": "^0.6.0", "angular2-datatable": "^0.6.0",
"bootstrap": "^3.3.7", "bootstrap": "^3.3.7",
"core-js": "^2.4.1", "core-js": "^2.4.1",
"d3": "^4.10.0",
"jquery": "^3.1.0", "jquery": "^3.1.0",
"jquery-ui": "^1.12.0", "jquery-ui": "^1.12.0",
"jquery-ui-bundle": "^1.11.4", "jquery-ui-bundle": "^1.11.4",
@ -31,6 +34,7 @@
"rxjs": "^5.2.0", "rxjs": "^5.2.0",
"ts-helpers": "^1.1.1", "ts-helpers": "^1.1.1",
"typescript": "^2.3.2", "typescript": "^2.3.2",
"webpack": "^3.4.1",
"zone.js": "^0.8.5" "zone.js": "^0.8.5"
}, },
"devDependencies": { "devDependencies": {

View File

@ -24,13 +24,16 @@
<li class="dropdown"> <li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true"
aria-expanded="false"> aria-expanded="false">
Schlacht Statistik Statistik
<span class="caret"></span> <span class="caret"></span>
</a> </a>
<ul class="dropdown-menu scrollable-menu"> <ul class="dropdown-menu scrollable-menu">
<li *ngIf="loginService.hasPermission(3)"> <li *ngIf="loginService.hasPermission(3)">
<a routerLink='/cc-wars' class="link">Hinzufügen...</a> <a routerLink='/cc-wars' class="link">Hinzufügen...</a>
</li> </li>
<li>
<a routerLink='/cc-statistic' class="link">Übersicht</a>
</li>
<li *ngFor="let war of wars"> <li *ngFor="let war of wars">
<a [routerLink]="['/cc-wars/' + war._id]">{{war.title}} <a [routerLink]="['/cc-wars/' + war._id]">{{war.title}}
<small>{{war.date | date: 'dd.MM.yy'}}</small> <small>{{war.date | date: 'dd.MM.yy'}}</small>

View File

@ -33,9 +33,12 @@ import {PromotionService} from "./services/promotion-service/promotion.service";
import {FilterRankPipe} from "./filter/filter.pipe"; import {FilterRankPipe} from "./filter/filter.pipe";
import {WarService} from "./services/war-service/war.service"; import {WarService} from "./services/war-service/war.service";
import {DataTableModule} from "angular2-datatable"; import {DataTableModule} from "angular2-datatable";
import {NgxChartsModule} from "@swimlane/ngx-charts";
import {BrowserAnimationsModule} from "@angular/platform-browser/animations";
@NgModule({ @NgModule({
imports: [BrowserModule, FormsModule, ReactiveFormsModule, appRouting, HttpModule, ClipboardModule, DataTableModule], imports: [BrowserModule, FormsModule, ReactiveFormsModule, appRouting, HttpModule, ClipboardModule, DataTableModule,
BrowserAnimationsModule, NgxChartsModule],
providers: [ providers: [
HttpClient, HttpClient,
LoginService, LoginService,

View File

@ -14,6 +14,7 @@ import {RequestPromotionComponent} from "./request/promotion/req-promotion.compo
import {ConfirmPromotionComponent} from "./request/confirm-promotion/confirm-promotion.component"; import {ConfirmPromotionComponent} from "./request/confirm-promotion/confirm-promotion.component";
import {ConfirmAwardComponent} from "./request/confirm-award/confirm-award.component"; import {ConfirmAwardComponent} from "./request/confirm-award/confirm-award.component";
import {warRoutes, warsRoutingComponents} from "./wars/wars.routing"; import {warRoutes, warsRoutingComponents} from "./wars/wars.routing";
import {StatisticComponent} from "./statistic/statistic.component";
export const appRoutes: Routes = [ export const appRoutes: Routes = [
@ -22,6 +23,7 @@ export const appRoutes: Routes = [
{path: '', redirectTo: '/cc-overview', pathMatch: 'full'}, {path: '', redirectTo: '/cc-overview', pathMatch: 'full'},
{path: 'cc-wars', children: warRoutes}, {path: 'cc-wars', children: warRoutes},
{path: 'cc-statistic', component: StatisticComponent},
{path: 'login', component: LoginComponent}, {path: 'login', component: LoginComponent},
{path: 'signup', component: SignupComponent}, {path: 'signup', component: SignupComponent},
@ -46,7 +48,7 @@ export const appRoutes: Routes = [
export const appRouting = RouterModule.forRoot(appRoutes); export const appRouting = RouterModule.forRoot(appRoutes);
export const routingComponents = [LoginComponent, SignupComponent, RequestAwardComponent, RequestPromotionComponent, ConfirmAwardComponent, export const routingComponents = [LoginComponent, SignupComponent, RequestAwardComponent, RequestPromotionComponent, ConfirmAwardComponent,
ConfirmPromotionComponent, AdminComponent, ...armyRoutingComponents, NotFoundComponent, ...usersRoutingComponents, ConfirmPromotionComponent, StatisticComponent, AdminComponent, ...armyRoutingComponents, NotFoundComponent, ...usersRoutingComponents,
...squadsRoutingComponents, ...decorationsRoutingComponents, ...ranksRoutingComponents, ...warsRoutingComponents]; ...squadsRoutingComponents, ...decorationsRoutingComponents, ...ranksRoutingComponents, ...warsRoutingComponents];
export const routingProviders = [LoginGuardSQL, LoginGuardHL, LoginGuardMT, LoginGuardAdmin]; export const routingProviders = [LoginGuardSQL, LoginGuardHL, LoginGuardMT, LoginGuardAdmin];

View File

@ -0,0 +1,9 @@
h3 {
width: 920px;
margin-left: 25%;
}
.chart-container {
width: 1200px;
margin-left: 25%;
}

View File

@ -0,0 +1,45 @@
<h3>Punkte</h3>
<div class="chart-container">
<ngx-charts-line-chart
[view]="[1050, 400]"
[scheme]="colorScheme"
[results]="playerData"
[gradient]="false"
[xAxis]="true"
[yAxis]="true"
[legend]="true"
[legendTitle]="''"
[showXAxisLabel]="true"
[showYAxisLabel]="true"
[xAxisLabel]="'Schlachtdatum'"
[yAxisLabel]="'Punkte'"
[autoScale]="false"
(select)="onSelect($event)">
</ngx-charts-line-chart>
</div>
<h3 style="margin-top: 370px;">Spielerzahlen</h3>
<div class="chart-container">
<ngx-charts-line-chart
[view]="[1050, 350]"
[scheme]="colorScheme"
[results]="pointData"
[gradient]="false"
[xAxis]="true"
[yAxis]="true"
[legend]="true"
[legendTitle]="''"
[showXAxisLabel]="true"
[showYAxisLabel]="true"
[xAxisLabel]="'Schlachtdatum'"
[yAxisLabel]="'Anzahl Spieler'"
[autoScale]="true"
(select)="onSelect($event)">
</ngx-charts-line-chart>
</div>

View File

@ -0,0 +1,92 @@
import {Component} from "@angular/core";
import {AppComponent} from "../app.component";
import {WarService} from "../services/war-service/war.service";
@Component({
selector: 'statistic',
templateUrl: './statistic.component.html',
styleUrls: ['./statistic.component.css']
})
export class StatisticComponent {
pointData: any[] = [];
playerData: any[] = [];
colorScheme = {
domain: ['#0000FF', '#B22222']
};
constructor(private appComponent: AppComponent,
private warService: WarService) {
}
ngOnInit() {
let wars = this.appComponent.wars;
if (wars.length === 0) {
this.warService.getAllWars().subscribe(items => {
this.initChart(items);
})
}
this.initChart(wars);
}
initChart(wars: any[]) {
let pointsObj = [
{
"name": "NATO",
"series": []
},
{
"name": "CSAT",
"series": []
}];
let playersObj = [
{
"name": "NATO",
"series": []
},
{
"name": "CSAT",
"series": []
}
];
for (let i = wars.length - 1; i >= 0; i--) {
const isoDate = wars[i].date.slice(0, 10);
const dayDate = parseInt(isoDate.slice(8, 10)) + 1;
const warDateString = (dayDate < 10 ? "0" + dayDate : dayDate) + '.'
+ isoDate.slice(5, 7) + '.' + isoDate.slice(0, 4);
const bluforData = {
name: warDateString,
value: wars[i].ptBlufor
};
const opforData = {
name: warDateString,
value: wars[i].ptOpfor
};
const bluforPlayers = {
name: warDateString,
value: wars[i].playersBlufor
};
const opforPlayers = {
name: warDateString,
value: wars[i].playersOpfor
};
playersObj[0].series.push(bluforData);
playersObj[1].series.push(opforData);
pointsObj[0].series.push(bluforPlayers);
pointsObj[1].series.push(opforPlayers);
}
this.pointData = pointsObj;
this.playerData = playersObj;
Object.assign(this, [this.playerData, this.pointData])
}
onSelect(event) {
console.log(event);
}
}

View File

@ -55,7 +55,7 @@ export class WarSubmitComponent {
this.router.navigate([war._id], {relativeTo: this.route}); this.router.navigate([war._id], {relativeTo: this.route});
}, },
error => { error => {
this.error = error._body; this.error = error._body.error.message;
this.showErrorLabel = true; this.showErrorLabel = true;
this.loading = false; this.loading = false;
}); });