Compare commits

...

2 Commits

12 changed files with 483 additions and 23 deletions

View File

@ -8,6 +8,7 @@ module.exports = {
cmdCreateSig: rootRoute + '/cmd/createSignature', cmdCreateSig: rootRoute + '/cmd/createSignature',
command: rootRoute + '/cmd', command: rootRoute + '/cmd',
decorations: rootRoute + '/decorations', decorations: rootRoute + '/decorations',
logs: rootRoute + '/logs',
overview: rootRoute + '/overview', overview: rootRoute + '/overview',
players: rootRoute + '/players', players: rootRoute + '/players',
ranks: rootRoute + '/ranks', ranks: rootRoute + '/ranks',

39
api/models/logs/budget.js Normal file
View File

@ -0,0 +1,39 @@
"use strict";
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const LogBudgetSchema = new Schema({
war: {
type: mongoose.Schema.Types.ObjectId,
ref: 'War',
required: true
},
time: {
type: Date,
required: true
},
fraction: {
type: String,
enum: ['BLUFOR', 'OPFOR'],
required: true
},
oldBudget: {
type: Number,
get: v => Math.round(v),
set: v => Math.round(v),
required: true
},
newBudget: {
type: Number,
get: v => Math.round(v),
set: v => Math.round(v),
required: true
}
}, {
collection: 'logBudget'
});
// optional more indices
LogBudgetSchema.index({war: 1});
module.exports = mongoose.model('LogBudget', LogBudgetSchema);

35
api/models/logs/flag.js Normal file
View File

@ -0,0 +1,35 @@
"use strict";
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const LogFlagSchema = new Schema({
war: {
type: mongoose.Schema.Types.ObjectId,
ref: 'War',
required: true
},
time: {
type: Date,
required: true
},
player: {
type: String,
required: true
},
flagFraction: {
type: String,
enum: ['BLUFOR', 'OPFOR'],
required: true
},
capture: {
type: Boolean,
required: true
}
}, {
collection: 'logFlag'
});
// optional more indices
LogFlagSchema.index({war: 1, player: 1});
module.exports = mongoose.model('LogFlag', LogFlagSchema);

38
api/models/logs/kill.js Normal file
View File

@ -0,0 +1,38 @@
"use strict";
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const LogKillSchema = new Schema({
war: {
type: mongoose.Schema.Types.ObjectId,
ref: 'War',
required: true
},
time: {
type: Date,
required: true
},
shooter: {
type: String
},
target: {
type: String,
required: true
},
friendlyFire: {
type: Boolean,
required: true
},
fraction: {
type: String,
enum: ['BLUFOR', 'OPFOR', 'NONE'],
required: true
}
}, {
collection: 'logKill'
});
// optional more indices
LogKillSchema.index({war: 1, shooter: 1, target: 1});
module.exports = mongoose.model('LogKill', LogKillSchema);

39
api/models/logs/points.js Normal file
View File

@ -0,0 +1,39 @@
"use strict";
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const LogKillSchema = new Schema({
war: {
type: mongoose.Schema.Types.ObjectId,
ref: 'War',
required: true
},
time: {
type: Date,
required: true
},
ptBlufor: {
type: Number,
get: v => Math.round(v),
set: v => Math.round(v),
required: true
},
ptOpfor: {
type: Number,
get: v => Math.round(v),
set: v => Math.round(v),
required: true
},
fraction: {
type: String,
enum: ['BLUFOR', 'OPFOR', 'NONE'],
required: true
}
}, {
collection: 'logPoints'
});
// optional more indices
LogKillSchema.index({war: 1, shooter: 1, target: 1});
module.exports = mongoose.model('LogPoints', LogKillSchema);

View File

@ -0,0 +1,26 @@
"use strict";
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const LogRespawnSchema = new Schema({
war: {
type: mongoose.Schema.Types.ObjectId,
ref: 'War',
required: true
},
time: {
type: Date,
required: true
},
player: {
type: String,
required: true
}
}, {
collection: 'logRespawn'
});
// optional more indices
LogRespawnSchema.index({war: 1, player: 1});
module.exports = mongoose.model('LogRespawn', LogRespawnSchema);

39
api/models/logs/revive.js Normal file
View File

@ -0,0 +1,39 @@
"use strict";
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const LogReviveSchema = new Schema({
war: {
type: mongoose.Schema.Types.ObjectId,
ref: 'War',
required: true
},
time: {
type: Date,
required: true
},
medic: {
type: String,
required: true
},
patient: {
type: String,
required: true
},
stabilized: {
type: Boolean,
required: true
},
fraction: {
type: String,
enum: ['BLUFOR', 'OPFOR'],
required: true
}
}, {
collection: 'logRevive'
});
// optional more indices
LogReviveSchema.index({war: 1, medic: 1});
module.exports = mongoose.model('LogRevive', LogReviveSchema);

View File

@ -0,0 +1,41 @@
"use strict";
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const LogTransportSchema = new Schema({
war: {
type: mongoose.Schema.Types.ObjectId,
ref: 'War',
required: true
},
time: {
type: Date,
required: true
},
driver: {
type: String,
required: true
},
passenger: {
type: String,
required: true
},
distance: {
type: Number,
get: v => Math.round(v),
set: v => Math.round(v),
required: true
},
fraction: {
type: String,
enum: ['BLUFOR', 'OPFOR'],
required: true
}
}, {
collection: 'logTransport'
});
// optional more indices
LogTransportSchema.index({war: 1});
module.exports = mongoose.model('LogTransport', LogTransportSchema);

175
api/routes/logs.js Normal file
View File

@ -0,0 +1,175 @@
"use strict";
// modules
const express = require('express');
const logger = require('debug')('cc:wars');
// HTTP status codes by name
const codes = require('./http-codes');
const routerHandling = require('../middleware/router-handling');
// Mongoose Model using mongoDB
const LogBudgetModel = require('../models/logs/budget');
const LogRespawnModel = require('../models/logs/respawn');
const LogReviveModel = require('../models/logs/revive');
const LogKillModel = require('../models/logs/kill');
const LogTransportModel = require('../models/logs/transport');
const LogFlagModel = require('../models/logs/flag');
const LogPointsModel = require('../models/logs/points');
const notFoundError = new Error('No logs found');
const logsRouter = express.Router();
// routes **********************
logsRouter.route('/:warId/budget')
.get((req, res, next) => {
const filter = {war: req.params.warId};
if (req.query.fraction) filter['fraction'] = req.query.fraction;
LogBudgetModel.find(filter, {}, {sort: {time: 1}}, (err, logBudget) => {
if (err) return next(err);
if (!logBudget || logBudget.length === 0) {
const err = notFoundError;
err.status = codes.notfound;
return next(err)
}
res.locals.items = logBudget;
next();
})
})
.all(
routerHandling.httpMethodNotAllowed
);
logsRouter.route('/:warId/respawn')
.get((req, res, next) => {
const filter = {war: req.params.warId};
if (req.query.player) filter['player'] = req.query.player;
LogRespawnModel.find(filter, {}, {sort: {time: 1}}, (err, logRespawn) => {
if (err) return next(err);
if (!logRespawn || logRespawn.length === 0) {
const err = notFoundError;
err.status = codes.notfound;
return next(err)
}
res.locals.items = logRespawn;
next();
})
})
.all(
routerHandling.httpMethodNotAllowed
);
logsRouter.route('/:warId/revive')
.get((req, res, next) => {
const filter = {war: req.params.warId};
if (req.query.medic) filter['medic'] = req.query.medic;
if (req.query.patient) filter['patient'] = req.query.patient;
if (req.query.stabilized) filter['stabilized'] = true;
if (req.query.revive) filter['stabilized'] = false;
if (req.query.fraction) filter['fraction'] = req.query.fraction;
LogReviveModel.find(filter, {}, {sort: {time: 1}}, (err, logRevive) => {
if (err) return next(err);
if (!logRevive || logRevive.length === 0) {
const err = notFoundError;
err.status = codes.notfound;
return next(err)
}
res.locals.items = logRevive;
next();
})
})
.all(
routerHandling.httpMethodNotAllowed
);
logsRouter.route('/:warId/kills')
.get((req, res, next) => {
const filter = {war: req.params.warId};
if (req.query.shooter) filter['shooter'] = req.query.shooter;
if (req.query.target) filter['target'] = req.query.target;
if (req.query.friendlyFire) filter['friendlyFire'] = true;
if (req.query.noFriendlyFire) filter['friendlyFire'] = false;
if (req.query.fraction) filter['fraction'] = req.query.fraction;
LogKillModel.find(filter, {}, {sort: {time: 1}}, (err, logKill) => {
if (err) return next(err);
if (!logKill || logKill.length === 0) {
const err = notFoundError;
err.status = codes.notfound;
return next(err)
}
res.locals.items = logKill;
next();
})
})
.all(
routerHandling.httpMethodNotAllowed
);
logsRouter.route('/:warId/transport')
.get((req, res, next) => {
const filter = {war: req.params.warId};
if (req.query.driver) filter['driver'] = req.query.driver;
if (req.query.passenger) filter['passenger'] = req.query.passenger;
if (req.query.fraction) filter['fraction'] = req.query.fraction;
LogTransportModel.find(filter, {}, {sort: {time: 1}}, (err, logTransport) => {
if (err) return next(err);
if (!logTransport || logTransport.length === 0) {
const err = notFoundError;
err.status = codes.notfound;
return next(err)
}
res.locals.items = logTransport;
next();
})
})
.all(
routerHandling.httpMethodNotAllowed
);
logsRouter.route('/:warId/flag')
.get((req, res, next) => {
const filter = {war: req.params.warId};
if (req.query.player) filter['player'] = req.query.player;
if (req.query.capture) filter['capture'] = true;
if (req.query.defend) filter['capture'] = false;
if (req.query.fraction) filter['fraction'] = req.query.fraction;
LogFlagModel.find(filter, {}, {sort: {time: 1}}, (err, logFlag) => {
if (err) return next(err);
if (!logFlag || logFlag.length === 0) {
const err = notFoundError;
err.status = codes.notfound;
return next(err)
}
res.locals.items = logFlag;
next();
})
})
.all(
routerHandling.httpMethodNotAllowed
);
logsRouter.route('/:warId/points')
.get((req, res, next) => {
const filter = {war: req.params.warId};
if (req.query.fraction) filter['fraction'] = req.query.fraction;
LogPointsModel.find(filter, {}, {sort: {time: 1}}, (err, logPoints) => {
if (err) return next(err);
if (!logPoints || logPoints.length === 0) {
const err = notFoundError;
err.status = codes.notfound;
return next(err)
}
res.locals.items = logPoints;
next();
})
})
.all(
routerHandling.httpMethodNotAllowed
);
logsRouter.use(routerHandling.emptyResponse);
module.exports = logsRouter;

View File

@ -23,6 +23,13 @@ const parseWarLog = require('../tools/log-parse-tool');
const CampaignModel = require('../models/campaign'); 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 LogRespawnModel = require('../models/logs/respawn');
const LogReviveModel = require('../models/logs/revive');
const LogTransportModel = require('../models/logs/transport');
const LogFlagModel = require('../models/logs/flag');
const LogBudgetModel = require('../models/logs/budget');
const LogPointsModel = require('../models/logs/points');
const wars = express.Router(); const wars = express.Router();
@ -67,26 +74,40 @@ wars.route('/')
const warBody = new WarModel(body); const warBody = new WarModel(body);
if (req.file) { if (req.file) {
fs.readFile(req.file.buffer, (file, err) => { fs.readFile(req.file.buffer, (file, err) => {
if (err) {
return next(err);
}
const lineArray = file.toString().split("\n");
const statsResult = parseWarLog(lineArray, warBody);
statsResult.war.save((err, war) => {
if (err) { if (err) {
return next(err); return next(err);
} }
const lineArray = file.toString().split("\n"); PlayerModel.create(statsResult.players, function (err) {
const statsResult = parseWarLog(lineArray, warBody);
statsResult.war.save((err, war) => {
if (err) { if (err) {
return next(err); return next(err);
} }
PlayerModel.create(statsResult.players, function (err) { LogKillModel.create(statsResult.kills, function (err) {
if (err) { LogRespawnModel.create(statsResult.respawn, function (err) {
return next(err); LogReviveModel.create(statsResult.revive, function (err) {
} LogFlagModel.create(statsResult.flag, function (err) {
res.status(codes.created); LogBudgetModel.create(statsResult.budget, function (err) {
res.locals.items = war; LogTransportModel.create(statsResult.transport, function (err) {
next(); LogPointsModel.create(statsResult.points, function (err) {
}); res.status(codes.created);
}) res.locals.items = war;
}); next();
})
})
})
})
})
})
})
});
})
});
} else { } else {
const err = new Error('no Logfile provided'); const err = new Error('no Logfile provided');

View File

@ -35,6 +35,7 @@ const signatureRouter = require('./routes/signatures');
const commandRouter = require('./routes/command'); const commandRouter = require('./routes/command');
const campaignRouter = require('./routes/campaigns'); const campaignRouter = require('./routes/campaigns');
const warRouter = require('./routes/wars'); const warRouter = require('./routes/wars');
const logRouter = require('./routes/logs');
// Configuration *********************************** // Configuration ***********************************
// mongoose promise setup // mongoose promise setup
@ -83,6 +84,7 @@ app.use(urls.awards, awardingRouter);
app.use(urls.wars, warRouter); app.use(urls.wars, warRouter);
app.use(urls.players, playerRouter); app.use(urls.players, playerRouter);
app.use(urls.campaigns,campaignRouter); app.use(urls.campaigns,campaignRouter);
app.use(urls.logs,logRouter);
app.use(urls.command, apiAuthenticationMiddleware, checkAdmin, commandRouter); app.use(urls.command, apiAuthenticationMiddleware, checkAdmin, commandRouter);
app.use(urls.account, apiAuthenticationMiddleware, checkAdmin, accountRouter); app.use(urls.account, apiAuthenticationMiddleware, checkAdmin, accountRouter);

View File

@ -40,8 +40,9 @@ const parseWarLog = (lineArray, war) => {
war: war._id, war: war._id,
time: getDateTime(line.split(' ')[5]), time: getDateTime(line.split(' ')[5]),
shooter: shooter ? shooter.name : null, shooter: shooter ? shooter.name : null,
target: target ? target.name : null, target: target.name,
friendlyFire: target && shooter ? target.fraction === shooter.fraction : false friendlyFire: shooter ? target.fraction === shooter.fraction : false,
fraction: shooter ? shooter.fraction : 'NONE'
}); });
addPlayersIfNotExists([shooter, target]); addPlayersIfNotExists([shooter, target]);
@ -94,7 +95,7 @@ const parseWarLog = (lineArray, war) => {
stats.war['ptBlufor'] = parseInt(pt[11]); stats.war['ptBlufor'] = parseInt(pt[11]);
stats.war['ptOpfor'] = parseInt(pt[14].slice(0, -1)); stats.war['ptOpfor'] = parseInt(pt[14].slice(0, -1));
} else { } else {
stats.points.push(getPointsEntry(pt, war._id)) stats.points.push(getPointsEntry(pt, line, war._id))
} }
} }
@ -125,7 +126,8 @@ const parseWarLog = (lineArray, war) => {
time: getDateTime(line.split(' ')[5]), time: getDateTime(line.split(' ')[5]),
stabilized: stabilized, stabilized: stabilized,
medic: medic.name, medic: medic.name,
patient: patientName patient: patient.name,
fraction: medic.fraction
}); });
addPlayersIfNotExists([medic, patient]); addPlayersIfNotExists([medic, patient]);
@ -147,6 +149,7 @@ const parseWarLog = (lineArray, war) => {
time: getDateTime(line.split(' ')[5]), time: getDateTime(line.split(' ')[5]),
driver: driver.name, driver: driver.name,
passenger: passenger.name, passenger: passenger.name,
fraction: driver.fraction,
distance: distance distance: distance
}); });
@ -178,18 +181,19 @@ const getRespawnEntry = (respawn, playerName, warId) => {
} }
}; };
const getPointsEntry = (pt, warId) => { const getPointsEntry = (pt, line, warId) => {
return { return {
warId: warId, war: warId,
time: getDateTime(pt[5]), time: getDateTime(pt[5]),
ptBlufor: parseInt(pt[12]), ptBlufor: parseInt(pt[12]),
ptOpfor: parseInt(pt[15].slice(0, -1)) ptOpfor: parseInt(pt[15].slice(0, -1)),
fraction: line.includes('no Domination') ? 'NONE': line.includes('NATO +1') ? 'BLUFOR' : 'OPFOR'
} }
}; };
const getBudgetEntry = (budg, warId) => { const getBudgetEntry = (budg, warId) => {
return { return {
warId: warId, war: warId,
time: getDateTime(budg[5]), time: getDateTime(budg[5]),
fraction: budg[9] === 'NATO' ? 'BLUFOR' : 'OPFOR', fraction: budg[9] === 'NATO' ? 'BLUFOR' : 'OPFOR',
oldBudget: transformMoneyString(budg[11]), oldBudget: transformMoneyString(budg[11]),
@ -201,7 +205,7 @@ const getPlayerAndFractionFromString = (nameAndFractionString) => {
const nameArray = nameAndFractionString.split(' '); const nameArray = nameAndFractionString.split(' ');
const fraction = nameArray[nameArray.length - 1] !== '(ENEMY)' ? nameArray[nameArray.length - 1] === '(WEST)' ? 'BLUFOR' : 'OPFOR' : undefined; const fraction = nameArray[nameArray.length - 1] !== '(ENEMY)' ? nameArray[nameArray.length - 1] === '(WEST)' ? 'BLUFOR' : 'OPFOR' : undefined;
const name = nameAndFractionString.substring(0, nameAndFractionString.indexOf(nameArray[nameArray.length - 1]) - 1); const name = nameAndFractionString.substring(0, nameAndFractionString.indexOf(nameArray[nameArray.length - 1]) - 1);
// do not return player for 'Selbstverschulden' // do not return player for 'Selbstverschulden' or 'Error: No unit'
if (name && name !== 'Error: No unit') { if (name && name !== 'Error: No unit') {
return {name: name, fraction: fraction}; return {name: name, fraction: fraction};
} }