Compare commits

...

2 Commits

12 changed files with 145 additions and 119 deletions

View File

@ -63,6 +63,21 @@ campaigns.route('/')
routerHandling.httpMethodNotAllowed routerHandling.httpMethodNotAllowed
); );
campaigns.route('/with/war/:id')
.get((req, res, next) => {
WarModel.findById(req.params.id, (err, item) => {
if (err) {
err.status = codes.servererror;
return next(err);
}
return genericGetById({params: {id: item.campaign}}, res, next, CampaignModel);
});
})
.all(
routerHandling.httpMethodNotAllowed
);
campaigns.route('/:id') campaigns.route('/:id')
.get(idValidator, (req, res, next) => { .get(idValidator, (req, res, next) => {
return genericGetById(req, res, next, CampaignModel); return genericGetById(req, res, next, CampaignModel);

View File

@ -23,7 +23,6 @@ const resourceLocation = require('../middleware/resource-location').resourceLoca
const parseWarLog = require('../tools/log-parse-tool'); const parseWarLog = require('../tools/log-parse-tool');
// Mongoose Model using mongoDB // Mongoose Model using mongoDB
const CampaignModel = require('../models/campaign');
const WarModel = require('../models/war'); const WarModel = require('../models/war');
const PlayerModel = require('../models/player'); const PlayerModel = require('../models/player');
const LogKillModel = require('../models/logs/kill'); const LogKillModel = require('../models/logs/kill');
@ -43,36 +42,18 @@ const wars = new express.Router();
// routes ********************** // routes **********************
wars.route('/') wars.route('/')
.get((req, res, next) => { .get((req, res, next) => {
let result = []; const filter = {};
CampaignModel.find({}, {}, {sort: {timestamp: 'desc'}}, (err, campaigns) => { if (req.query.campaignId) {
filter.campaign = req.query.campaignId;
}
WarModel.find(filter, {}, {sort: {date: 'desc'}}, (err, wars) => {
if (err) { if (err) {
err.status = codes.servererror; err.status = codes.servererror;
return next(err); return next(err);
} }
if (campaigns) { res.locals.items = wars;
WarModel.find({}, {}, {sort: {date: 'desc'}}, (err, wars) => { res.locals.processed = true;
if (err) { next();
err.status = codes.servererror;
return next(err);
}
if (wars) {
campaigns.forEach((campaign) => {
let entry = {_id: campaign._id, title: campaign.title, wars: []};
wars.forEach((war) => {
if (String(campaign._id) === String(war.campaign)) {
entry.wars.push(war);
}
});
result.push(entry);
});
res.locals.items = result;
}
res.locals.processed = true;
next();
}
)
;
}
}); });
}) })

View File

@ -1,3 +1,5 @@
import {Observable} from 'rxjs';
export interface AppUser { export interface AppUser {
_id?: string; _id?: string;
username?: string; username?: string;
@ -43,7 +45,7 @@ export interface CampaignPlayer {
export interface Campaign { export interface Campaign {
_id?: string; _id?: string;
title?: string; title?: string;
wars?: War[]; wars$?: Observable<War[]>;
} }
export interface War { export interface War {

View File

@ -20,17 +20,18 @@ export class CampaignService {
getAllCampaigns() { getAllCampaigns() {
return this.http.get(this.config.apiCampaignPath) return this.http.get(this.config.apiCampaignPath)
.map(res => res.json())
.do((ranks) => this.campaignStore.dispatch({type: LOAD, data: ranks}));
}
getCampaign(id: string) {
return this.http.get(`${this.config.apiCampaignPath}/${id}`)
.map(res => res.json()); .map(res => res.json());
} }
getAllCampaignsWithWars() { getCampaignByWarId(warId) {
this.http.get(this.config.apiWarPath) return this.http.get(`${this.config.apiCampaignPath}/with/war/${warId}`)
.map(res => res.json()) .map((res) => res.json());
.do((ranks) => {
this.campaignStore.dispatch({type: LOAD, data: ranks});
}).subscribe(_ => {
});
return this.campaigns$;
} }
submitCampaign(campaign: Campaign) { submitCampaign(campaign: Campaign) {
@ -63,13 +64,6 @@ export class CampaignService {
deleteCampaign(campaign: Campaign) { deleteCampaign(campaign: Campaign) {
return this.http.delete(this.config.apiCampaignPath + '/' + campaign._id) return this.http.delete(this.config.apiCampaignPath + '/' + campaign._id)
.do(res => { .do(res => this.campaignStore.dispatch({type: REMOVE, data: campaign}));
this.campaignStore.dispatch({type: REMOVE, data: campaign});
});
}
getCampaign(id: string) {
return this.http.get(this.config.apiCampaignPath + '/' + id)
.map(res => res.json());
} }
} }

View File

@ -2,12 +2,29 @@ import {Injectable} from '@angular/core';
import {War} from '../../models/model-interfaces'; import {War} from '../../models/model-interfaces';
import {AppConfig} from '../../app.config'; import {AppConfig} from '../../app.config';
import {HttpClient} from '../http-client'; import {HttpClient} from '../http-client';
import {ADD, EDIT, LOAD, REMOVE, Store} from '../stores/generic-store';
import {Observable} from 'rxjs';
@Injectable() @Injectable()
export class WarService { export class WarService {
wars$: Observable<War[]>;
warStore = new Store<War>();
constructor(private http: HttpClient, constructor(private http: HttpClient,
private config: AppConfig) { private config: AppConfig) {
this.wars$ = this.warStore.items$;
}
getAllWars(campaignId?: string) {
let targetUrl = this.config.apiWarPath;
if (campaignId) {
targetUrl += `?campaignId=${campaignId}`
}
return this.http.get(targetUrl)
.map(res => res.json())
.do((wars) => this.warStore.dispatch({type: LOAD, data: wars}));
} }
getWar(warId: string) { getWar(warId: string) {
@ -15,7 +32,6 @@ export class WarService {
.map(res => res.json()); .map(res => res.json());
} }
submitWar(war: War, logFile?) { submitWar(war: War, logFile?) {
let body; let body;
@ -30,17 +46,19 @@ export class WarService {
} }
return this.http.post(this.config.apiWarPath, body) return this.http.post(this.config.apiWarPath, body)
.map(res => res.json()); .map(res => res.json())
} .do((newWar) => this.warStore.dispatch({type: ADD, data: newWar}));
deleteWar(id: string) {
return this.http.delete(this.config.apiWarPath + '/' + id)
.map(res => res.json());
} }
updateWar(war: War) { updateWar(war: War) {
return this.http.patch(this.config.apiWarPath + '/' + war._id, war) return this.http.patch(this.config.apiWarPath + '/' + war._id, war)
.map(res => res.json()); .map(res => res.json())
.do((updatedWar) => this.warStore.dispatch({type: EDIT, data: updatedWar}));
}
deleteWar(war: War) {
return this.http.delete(this.config.apiWarPath + '/' + war._id)
.do((emptyRes) => this.warStore.dispatch({type: REMOVE, data: war}));
} }
} }

View File

@ -14,7 +14,7 @@
</mat-button-toggle-group> </mat-button-toggle-group>
</div> </div>
<div class="fade-in"> <div class="fade-in" *ngIf="initialized">
<div class="slide-chart-container"> <div class="slide-chart-container">
<ngx-charts-line-chart <ngx-charts-line-chart
[scheme]="colorScheme" [scheme]="colorScheme"

View File

@ -1,8 +1,9 @@
import {Component, Input, OnInit} from '@angular/core'; import {Component, OnInit} from '@angular/core';
import {ActivatedRoute} from '@angular/router'; import {ActivatedRoute} from '@angular/router';
import {CampaignService} from '../../../services/logs/campaign.service'; import {CampaignService} from '../../../services/logs/campaign.service';
import {ChartUtils} from '../../../utils/chart-utils'; import {ChartUtils} from '../../../utils/chart-utils';
import {Fraction} from '../../../utils/fraction.enum'; import {Fraction} from '../../../utils/fraction.enum';
import {WarService} from '../../../services/logs/war.service';
@Component({ @Component({
@ -12,10 +13,9 @@ import {Fraction} from '../../../utils/fraction.enum';
}) })
export class StatisticOverviewComponent implements OnInit { export class StatisticOverviewComponent implements OnInit {
@Input() campaigns; id: string;
id = ''; initialized = false;
title = '';
pointData: any[] = []; pointData: any[] = [];
pointSumData: any[] = []; pointSumData: any[] = [];
@ -38,7 +38,8 @@ export class StatisticOverviewComponent implements OnInit {
timeline = false; timeline = false;
constructor(private route: ActivatedRoute, constructor(private route: ActivatedRoute,
private campaignService: CampaignService) { private campaignService: CampaignService,
private warService: WarService) {
} }
ngOnInit() { ngOnInit() {
@ -46,26 +47,21 @@ export class StatisticOverviewComponent implements OnInit {
.map(params => params['id']) .map(params => params['id'])
.subscribe((id) => { .subscribe((id) => {
this.id = id; this.id = id;
this.campaignService.getAllCampaignsWithWars().subscribe(campaigns => { this.initWars();
this.initWars(campaigns);
});
}); });
} }
initWars(campaigns) { initWars() {
let wars = []; if (this.id === 'all') {
let itemsProcessed = 0; this.warService.getAllWars().subscribe((wars) => {
campaigns = campaigns.filter(campaign => this.id === 'all' || campaign._id === this.id);
campaigns.forEach(campaign => {
wars = wars.concat(campaign.wars);
itemsProcessed++;
if (itemsProcessed === campaigns.length) {
if (this.id === 'all') {
this.timeline = true;
}
this.initChart(wars); this.initChart(wars);
} });
}); this.timeline = true;
} else {
this.warService.wars$.subscribe((wars) => {
this.initChart(wars);
})
}
} }
goToSlide(index) { goToSlide(index) {
@ -137,5 +133,9 @@ export class StatisticOverviewComponent implements OnInit {
this.goToSlide(1); this.goToSlide(1);
} }
Object.assign(this, this.currentData); Object.assign(this, this.currentData);
setTimeout(() => {
this.initialized = true;
}, 250);
} }
} }

View File

@ -4,6 +4,7 @@ import {CampaignService} from '../services/logs/campaign.service';
import {ActivatedRoute, Router} from '@angular/router'; import {ActivatedRoute, Router} from '@angular/router';
import {TranslateService} from '@ngx-translate/core'; import {TranslateService} from '@ngx-translate/core';
import {SettingsService} from '../services/settings.service'; import {SettingsService} from '../services/settings.service';
import {WarService} from '../services/logs/war.service';
@Component({ @Component({
selector: 'cc-stats', selector: 'cc-stats',
@ -12,13 +13,14 @@ import {SettingsService} from '../services/settings.service';
}) })
export class StatisticComponent implements OnInit { export class StatisticComponent implements OnInit {
selectedCampaign: Campaign; selectedCampaign: Campaign = {};
campaigns: Campaign[] = []; campaigns: Campaign[] = [];
collapsed = false; collapsed = false;
constructor(private campaignService: CampaignService, constructor(private campaignService: CampaignService,
private warService: WarService,
private router: Router, private router: Router,
private route: ActivatedRoute, private route: ActivatedRoute,
private translate: TranslateService, private translate: TranslateService,
@ -27,18 +29,15 @@ export class StatisticComponent implements OnInit {
} }
ngOnInit() { ngOnInit() {
this.campaignService.getAllCampaignsWithWars().subscribe((campaigns) => { this.campaignService.getAllCampaigns()
this.campaigns = campaigns; .filter(campaigns => campaigns.length !== 0)
this.selectedCampaign = this.resolveCampaignFromUrl(); .subscribe((campaigns) => {
this.switchCampaign(this.selectedCampaign); this.campaigns = campaigns;
}); this.resolveCampaignFromUrl();
});
} }
resolveCampaignFromUrl() { resolveCampaignFromUrl() {
if (this.campaigns.length === 0) {
return {};
}
const url = this.router.url; const url = this.router.url;
const idFetchPattern = /right:.*\/(.*)\)$/; const idFetchPattern = /right:.*\/(.*)\)$/;
@ -46,25 +45,33 @@ export class StatisticComponent implements OnInit {
if (url.includes('right:overview') || url.includes('right:highscore')) { if (url.includes('right:overview') || url.includes('right:highscore')) {
const id = idFetchPattern.exec(url)[1]; const id = idFetchPattern.exec(url)[1];
if (id === 'all') { if (id === 'all') {
return {_id: id} this.switchCampaign({_id: id});
} }
const filteredCampaigns = this.campaigns.filter(c => c._id === id); const filteredCampaigns = this.campaigns.filter(c => c._id === id);
if (filteredCampaigns.length === 1) { if (filteredCampaigns.length === 1) {
return filteredCampaigns[0]; this.switchCampaign(filteredCampaigns[0]);
} }
} } else if (url.includes('right:war')) {
if (url.includes('right:war')) {
const id = idFetchPattern.exec(url)[1]; const id = idFetchPattern.exec(url)[1];
const filteredCampaigns = this.campaigns.filter(c => c.wars.filter(war => war._id === id).length > 0); console.log(id)
if (filteredCampaigns.length === 1) { this.campaignService.getCampaignByWarId(id).subscribe((campaign) => {
return filteredCampaigns[0]; this.switchCampaign(campaign);
} });
} else {
this.switchCampaign(this.campaigns[0]);
} }
return this.campaigns[0]
} }
switchCampaign(campaign) { switchCampaign(campaign) {
this.selectedCampaign = campaign; if (campaign._id !== 'all') {
campaign.wars$ = this.warService.wars$;
this.warService.getAllWars(campaign._id).subscribe((wars) => {
this.selectedCampaign = campaign;
});
} else {
this.selectedCampaign = campaign;
}
if (campaign._id === 'all' || this.router.url.includes('/overview/all')) { if (campaign._id === 'all' || this.router.url.includes('/overview/all')) {
setTimeout(_ => { setTimeout(_ => {
window.dispatchEvent(new Event('resize')); window.dispatchEvent(new Event('resize'));

View File

@ -37,7 +37,7 @@
</div> </div>
<div class="battle-list"> <div class="battle-list">
<div *ngFor="let war of campaign.wars"> <div *ngFor="let war of campaign.wars$ | async">
<cc-war-item <cc-war-item
[war]="war" [war]="war"
(warEdit)="editWar($event)" (warEdit)="editWar($event)"

View File

@ -107,12 +107,11 @@ export class WarListComponent implements OnChanges {
this.translate.get('stats.sidebar.battle.manage.delete.confirm', {title: war.title}) this.translate.get('stats.sidebar.battle.manage.delete.confirm', {title: war.title})
.subscribe((confirmQuestion: string) => { .subscribe((confirmQuestion: string) => {
if (confirm(confirmQuestion)) { if (confirm(confirmQuestion)) {
this.warService.deleteWar(war._id) this.warService.deleteWar(war)
.subscribe((res) => { .subscribe((res) => {
if (this.selectedWarId === war._id) { if (this.selectedWarId === war._id) {
this.selectOverview('all'); this.selectOverview('all');
} }
this.campaign.wars.splice(this.campaign.wars.indexOf(war), 1);
}); });
} }
}); });

View File

@ -88,7 +88,7 @@
<button id="save" <button id="save"
type="submit" type="submit"
*ngIf="!loading" *ngIf="!loading"
(click)="war._id ? editWar() : saveWar()" (click)="submitWar()"
class="btn btn-default" class="btn btn-default"
[disabled]="!form.valid"> [disabled]="!form.valid">
{{'stats.war.submit.button.submit' | translate}} {{'stats.war.submit.button.submit' | translate}}

View File

@ -61,29 +61,39 @@ export class WarSubmitComponent {
} }
} }
saveWar() { submitWar() {
if (!this.fileList) { if (this.war._id) {
this.translate.get('stats.war.submit.logfile').subscribe((fieldNameLogfile) => { this.warService.updateWar(this.war).subscribe((updatedWar) => {
this.translate.get('public.error.message.required', this.router.navigate(['../../war/' + updatedWar._id], {relativeTo: this.route});
{fieldName: fieldNameLogfile}).subscribe((message) => { },
this.snackBarService.showError(message, 4000); (error) => {
const errorMsg = JSON.parse(error._body).error.message;
this.snackBarService.showError('Error: '.concat(errorMsg), 15000);
}
);
} else {
if (!this.fileList) {
this.translate.get('stats.war.submit.logfile').subscribe((fieldNameLogfile) => {
this.translate.get('public.error.message.required',
{fieldName: fieldNameLogfile}).subscribe((message) => {
this.snackBarService.showError(message, 4000);
})
}) })
}) }
const file: File = this.fileList[0];
this.loading = true;
this.spinnerService.activate();
this.warService.submitWar(this.war, file)
.subscribe(war => {
this.router.navigate(['../war/' + war._id], {relativeTo: this.route});
},
error => {
this.spinnerService.deactivate();
this.loading = false;
const errorMsg = JSON.parse(error._body).error.message;
this.snackBarService.showError('Error: '.concat(errorMsg), 15000);
});
} }
const file: File = this.fileList[0];
this.loading = true;
this.spinnerService.activate();
this.warService.submitWar(this.war, file)
.subscribe(war => {
this.router.navigate(['../war/' + war._id], {relativeTo: this.route});
},
error => {
this.spinnerService.deactivate();
this.loading = false;
const errorMsg = JSON.parse(error._body).error.message;
this.snackBarService.showError('Error: '.concat(errorMsg), 15000);
});
} }
cancel() { cancel() {