Added support for additional aggregation options for statistics
MODIFY
src/assets/server/rest/v1/schemas/statistic/statistics-get.json
src/assets/server/rest/v1/schemas/statistic/statistics-get.json
},
"SiteID": {
"$ref": "common#/definitions/ids"
+ },
+ "DataScope": {
+ "type": "string",
+ "enum": [
+ "year",
+ "month",
+ "date",
+ "hour",
+ "dayOfMonth",
+ "dayOfWeek",
+ "dayOfYear",
+ "sum"
+ ],
+ "sanitize": "mongo"
}
}
}

MODIFY
src/server/rest/v1/service/StatisticService.ts
src/server/rest/v1/service/StatisticService.ts
@@ -42,7 +42,7 @@ export default class StatisticService {
req.tenant, filter, StatsGroupBy.CONSUMPTION);
// Convert
const transactions = StatisticService.convertToGraphData(
- transactionStats, StatsDataCategory.CHARGING_STATION);
+ transactionStats, StatsDataCategory.CHARGING_STATION, filter.dataScope);
res.json(transactions);
next();
}
@@ -71,7 +71,7 @@ export default class StatisticService {
req.tenant, filter, StatsGroupBy.USAGE);
// Convert
const transactions = StatisticService.convertToGraphData(
- transactionStats, StatsDataCategory.CHARGING_STATION);
+ transactionStats, StatsDataCategory.CHARGING_STATION, filter.dataScope);
res.json(transactions);
next();
}
@@ -100,7 +100,7 @@ export default class StatisticService {
req.tenant, filter, StatsGroupBy.INACTIVITY);
// Convert
const transactions = StatisticService.convertToGraphData(
- transactionStats, StatsDataCategory.CHARGING_STATION);
+ transactionStats, StatsDataCategory.CHARGING_STATION, filter.dataScope);
res.json(transactions);
next();
}
@@ -129,7 +129,7 @@ export default class StatisticService {
req.tenant, filter, StatsGroupBy.TRANSACTIONS);
// Convert
const transactions = StatisticService.convertToGraphData(
- transactionStats, StatsDataCategory.CHARGING_STATION);
+ transactionStats, StatsDataCategory.CHARGING_STATION, filter.dataScope);
res.json(transactions);
next();
}
@@ -158,7 +158,7 @@ export default class StatisticService {
req.tenant, filter, StatsGroupBy.PRICING);
// Convert
const transactions = StatisticService.convertToGraphData(
- transactionStats, StatsDataCategory.CHARGING_STATION);
+ transactionStats, StatsDataCategory.CHARGING_STATION, filter.dataScope);
res.json(transactions);
next();
}
@@ -400,6 +400,12 @@ export default class StatisticService {
if (filteredRequest.ChargingStationID) {
filter.chargeBoxIDs = filteredRequest.ChargingStationID.split('|');
}
+ // DataScope
+ if (filteredRequest.DataScope) {
+ filter.dataScope = filteredRequest.DataScope;
+ } else {
+ filter.dataScope = StatsDataScope.MONTH;
+ }
// User
if (Authorizations.isBasic(loggedUser)) {
if (Authorizations.isSiteAdmin(loggedUser)) {
@@ -415,36 +421,37 @@ export default class StatisticService {
return filter;
}
- static convertToGraphData(transactionStats: ChargingStationStats[] | UserStats[], dataCategory: string): any[] {
+ static convertToGraphData(transactionStats: ChargingStationStats[] | UserStats[], dataCategory: string, dataScope: StatsDataScope = StatsDataScope.MONTH): any[] {
const transactions: Record<string, number>[] = [];
// Create
if (transactionStats && transactionStats.length > 0) {
// Create
- let month = -1;
+ let period = -1;
let unit: string;
let transaction;
let userName: string;
for (const transactionStat of transactionStats) {
+ const stat = transactionStat[dataScope];
// Init
if (transactionStat.unit && (unit !== transactionStat.unit)) {
// Set
- month = transactionStat.month;
+ period = stat;
unit = transactionStat.unit;
// Create new
transaction = {};
- transaction.month = transactionStat.month - 1;
+ transaction[dataScope] = typeof stat === 'number' ? stat - 1 : stat;
transaction.unit = transactionStat.unit;
// Add
if (transaction) {
transactions.push(transaction);
}
}
- if (month !== transactionStat.month) {
+ if (period !== stat) {
// Set
- month = transactionStat.month;
+ period = stat;
// Create new
transaction = {};
- transaction.month = transactionStat.month - 1;
+ transaction[dataScope] = typeof stat === 'number' ? stat - 1 : stat;
if (transactionStat.unit) {
unit = transactionStat.unit;
transaction.unit = transactionStat.unit;

MODIFY
src/storage/mongodb/StatisticsStorage.ts
src/storage/mongodb/StatisticsStorage.ts
-import StatisticFilter, { ChargingStationStats, StatsGroupBy, UserStats } from '../../types/Statistic';
+import StatisticFilter, { ChargingStationStats, StatsAggregationKey, StatsDataScope, StatsGroupBy, UserStats } from '../../types/Statistic';
import DatabaseUtils from './DatabaseUtils';
import Logging from '../../utils/Logging';
@@ -61,14 +61,23 @@ export default class StatisticsStorage {
aggregation.push({
$match: filters
});
+
+ const scope: StatsDataScope = params.dataScope;
+ const createGroupId = (unit = '') => {
+ if (scope === StatsDataScope.DATE) {
+ return { chargeBox: '$chargeBoxID', [StatsDataScope.DATE]: { '$dateToString': { date: '$timestamp', format: '%Y-%m-%d' } }, unit };
+ }
+ const key: StatsAggregationKey = `$${scope}`;
+ return { chargeBox: '$chargeBoxID', [scope]: { [key]: '$timestamp' }, unit };
+ };
+
// Group
switch (groupBy) {
// By Consumption
case StatsGroupBy.CONSUMPTION:
aggregation.push({
$group: {
- // _id: { chargeBox: "$chargeBoxID", year: { $year: "$timestamp" }, month: { $month: "$timestamp" }, unit: '' },
- _id: { chargeBox: '$chargeBoxID', month: { $month: '$timestamp' }, unit: '' },
+ _id: createGroupId(),
total: { $sum: { $divide: ['$stop.totalConsumptionWh', 1000] } }
}
});
@@ -77,7 +86,7 @@ export default class StatisticsStorage {
case StatsGroupBy.USAGE:
aggregation.push({
$group: {
- _id: { chargeBox: '$chargeBoxID', month: { $month: '$timestamp' }, unit: '' },
+ _id: createGroupId(),
total: { $sum: { $divide: [{ $subtract: ['$stop.timestamp', '$timestamp'] }, 60 * 60 * 1000] } }
}
});
@@ -86,7 +95,7 @@ export default class StatisticsStorage {
case StatsGroupBy.INACTIVITY:
aggregation.push({
$group: {
- _id: { chargeBox: '$chargeBoxID', month: { $month: '$timestamp' }, unit: '' },
+ _id: createGroupId(),
total: { $sum: { $divide: [{ $add: ['$stop.totalInactivitySecs', '$stop.extraInactivitySecs'] }, 60 * 60] } }
}
});
@@ -95,7 +104,7 @@ export default class StatisticsStorage {
case StatsGroupBy.TRANSACTIONS:
aggregation.push({
$group: {
- _id: { chargeBox: '$chargeBoxID', month: { $month: '$timestamp' }, unit: '' },
+ _id: createGroupId(),
total: { $sum: 1 }
}
});
@@ -104,7 +113,7 @@ export default class StatisticsStorage {
case StatsGroupBy.PRICING:
aggregation.push({
$group: {
- _id: { chargeBox: '$chargeBoxID', month: { $month: '$timestamp' }, unit: '$stop.priceUnit' },
+ _id: createGroupId('$stop.priceUnit'),
total: { $sum: '$stop.price' }
}
});
@@ -113,12 +122,12 @@ export default class StatisticsStorage {
// Replace root
aggregation.push({
$replaceRoot: {
- newRoot: { month: '$_id.month', unit: '$_id.unit', chargeBox: '$_id.chargeBox', total: '$total' }
+ newRoot: { [scope]: `$_id.${scope}`, unit: '$_id.unit', chargeBox: '$_id.chargeBox', total: '$total' }
}
});
// Sort
aggregation.push({
- $sort: { 'month': 1, 'unit': 1, 'chargeBox': 1 }
+ $sort: { [scope]: 1, 'unit': 1, 'chargeBox': 1 }
});
// Read DB
const chargingStationStatsMDB = await global.database.getCollection<any>(tenant.id, 'transactions')

MODIFY
src/types/Statistic.ts
src/types/Statistic.ts
@@ -15,7 +15,7 @@ export default interface StatisticFilter {
userIDs?: string[];
dataType?: string;
dataCategory?: string;
- dataScope?: string;
+ dataScope?: StatsDataScope;
stop: {
$exists: boolean;
};
@@ -45,14 +45,21 @@ export enum StatsDataType {
export enum StatsDataScope {
YEAR = 'year',
MONTH = 'month',
+ DATE = 'date',
+ DAY_OF_MONTH = 'dayOfMonth',
+ DAY_OF_WEEK = 'dayOfWeek',
+ TOTAL = 'sum',
+ DAY_OF_YEAR = 'dayOfYear',
+ HOUR = 'hour'
}
-export interface ChargingStationStats {
+export type StatsAggregationKey = `$${StatsDataScope}`;
+
+export type ChargingStationStats = {[key in StatsDataScope]: number | string} & {
chargeBox: string;
- month: number;
total: number;
unit: string;
-}
+};
export interface UserStats {
userID: string;

Added support for additional aggregation options for statistics