adminmate-express-mongoose 1.2.2 → 1.2.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +40 -0
- package/jest.config.js +7 -1
- package/package.json +17 -6
- package/src/controllers/chart-pie.js +32 -0
- package/src/controllers/chart-ranking.js +9 -0
- package/src/controllers/chart-time.js +147 -0
- package/src/controllers/chart-value.js +55 -0
- package/src/controllers/model-autocomplete.js +2 -2
- package/src/controllers/model-getall.js +8 -8
- package/src/controllers/model-getone.js +5 -4
- package/src/controllers/model-query.js +35 -195
- package/test/__snapshots__/actions.test.js.snap +0 -56
- package/test/__snapshots__/model-getall.test.js.snap +0 -656
- package/test/__snapshots__/model-getone.test.js.snap +0 -33
- package/test/actions.test.js +0 -163
- package/test/app.js +0 -63
- package/test/model-getall.test.js +0 -203
- package/test/model-getone.test.js +0 -69
package/README.md
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# Adminmate (Express.js + Mongoose)
|
|
2
|
+
|
|
3
|
+
Adminmate is a powerful & flexible back-office solution build for small to big teams. ✌️
|
|
4
|
+
|
|
5
|
+
It provides an extremely flexible API developed in NodeJS that communicate with a powerful frontend back-office we host.
|
|
6
|
+
|
|
7
|
+
As the security & privacy of your data is our main focus, the Data API is host by yourself and secured by your own credentials.
|
|
8
|
+
|
|
9
|
+
## Getting started
|
|
10
|
+
|
|
11
|
+
[https://adminmate.io](https://adminmate.io)
|
|
12
|
+
|
|
13
|
+
## Databases compatibility
|
|
14
|
+
|
|
15
|
+
Adminmate is compatible with the most famous database systems like **MySQL**, **PostgreSQL**, **SQLite** and **MongoDB**. We are working hard on adding more soon!
|
|
16
|
+
|
|
17
|
+
## Features
|
|
18
|
+
|
|
19
|
+
Adminmate comes with all the features you need for your back-office:
|
|
20
|
+
* **Data**: Data Explorer, CRUD, Filters, Segments, Actions
|
|
21
|
+
* **Dashboards & Charts**: Unlimited Dashboards & Charts
|
|
22
|
+
* **Collaboration**: Powerful collaboration tool
|
|
23
|
+
* **Activity**: Track everything that happening on your database data
|
|
24
|
+
* **Access Control**: Team-based Access Control
|
|
25
|
+
|
|
26
|
+
### Data explorer
|
|
27
|
+
|
|
28
|
+

|
|
29
|
+
|
|
30
|
+
### Dashboards & Charts
|
|
31
|
+
|
|
32
|
+

|
|
33
|
+
|
|
34
|
+
### Activity
|
|
35
|
+
|
|
36
|
+

|
|
37
|
+
|
|
38
|
+
## Who are the contributors ?
|
|
39
|
+
|
|
40
|
+
Adminmate is a bootstrapped project tailored by **Marc Delalonde** and aims to stay an *independent project, driven by the community*.
|
package/jest.config.js
CHANGED
package/package.json
CHANGED
|
@@ -1,11 +1,22 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "adminmate-express-mongoose",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.6",
|
|
4
4
|
"description": "Adminmate Express/Mongoose connector",
|
|
5
5
|
"author": "Marc Delalonde",
|
|
6
|
+
"homepage": "http://adminmate.io",
|
|
7
|
+
"license": "GPL-3.0",
|
|
8
|
+
"keywords": [
|
|
9
|
+
"adminmate",
|
|
10
|
+
"admin",
|
|
11
|
+
"panel",
|
|
12
|
+
"interface",
|
|
13
|
+
"back-office",
|
|
14
|
+
"mongodb",
|
|
15
|
+
"mongoose"
|
|
16
|
+
],
|
|
6
17
|
"scripts": {
|
|
7
18
|
"start": "node ./index",
|
|
8
|
-
"test": "jest --
|
|
19
|
+
"test": "jest --runInBand",
|
|
9
20
|
"reset-db": "node migration.js"
|
|
10
21
|
},
|
|
11
22
|
"repository": {
|
|
@@ -13,7 +24,7 @@
|
|
|
13
24
|
"url": "https://github.com/Adminmate/adminmate-express-mongoose.git"
|
|
14
25
|
},
|
|
15
26
|
"dependencies": {
|
|
16
|
-
"adminmate-express-core": "^1.1.
|
|
27
|
+
"adminmate-express-core": "^1.1.5",
|
|
17
28
|
"lodash": "^4.17.21",
|
|
18
29
|
"moment": "^2.29.1",
|
|
19
30
|
"mongoose": "^5.9.7",
|
|
@@ -21,8 +32,8 @@
|
|
|
21
32
|
"serialize-error": "^7.0.1"
|
|
22
33
|
},
|
|
23
34
|
"devDependencies": {
|
|
24
|
-
"jest": "^
|
|
25
|
-
"
|
|
26
|
-
"
|
|
35
|
+
"jest": "^27.3.1",
|
|
36
|
+
"jest-specific-snapshot": "^5.0.0",
|
|
37
|
+
"node-mocks-http": "^1.11.0"
|
|
27
38
|
}
|
|
28
39
|
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
module.exports = async (currentModel, data) => {
|
|
2
|
+
let _value = 1;
|
|
3
|
+
if (data.field && ['sum', 'avg'].includes(data.operation)) {
|
|
4
|
+
_value = `$${data.field}`;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
const repartitionData = await currentModel
|
|
8
|
+
.aggregate([
|
|
9
|
+
{
|
|
10
|
+
$group: {
|
|
11
|
+
_id: `$${data.group_by}`,
|
|
12
|
+
count: data.operation === 'avg' ? { $avg: _value } : { $sum: _value },
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
$project: {
|
|
17
|
+
key: '$_id',
|
|
18
|
+
value: '$count',
|
|
19
|
+
_id: false
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
])
|
|
23
|
+
.sort({ key: 1 });
|
|
24
|
+
|
|
25
|
+
return {
|
|
26
|
+
success: true,
|
|
27
|
+
data: {
|
|
28
|
+
config: null,
|
|
29
|
+
data: repartitionData
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
};
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
|
|
2
|
+
const _ = require('lodash');
|
|
3
|
+
const moment = require('moment');
|
|
4
|
+
|
|
5
|
+
module.exports = async (currentModel, data) => {
|
|
6
|
+
if (!['day', 'week', 'month', 'year'].includes(data.timeframe)) {
|
|
7
|
+
return {
|
|
8
|
+
success: false,
|
|
9
|
+
message: 'Invalid timeframe'
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const toSum = data.field && data.operation === 'sum' ? `$${data.field}` : 1;
|
|
14
|
+
|
|
15
|
+
let matchReq = {};
|
|
16
|
+
let groupFormat = '';
|
|
17
|
+
|
|
18
|
+
// Day timeframe
|
|
19
|
+
if (data.timeframe === 'day') {
|
|
20
|
+
const startOfCurrentDay = moment().startOf('day');
|
|
21
|
+
matchReq = {
|
|
22
|
+
'$gte': new Date(startOfCurrentDay.clone().subtract(30, 'day').startOf('day').format()),
|
|
23
|
+
'$lt': new Date(startOfCurrentDay.format())
|
|
24
|
+
};
|
|
25
|
+
groupFormat = '%Y-%m-%d';
|
|
26
|
+
}
|
|
27
|
+
// Week timeframe
|
|
28
|
+
else if (data.timeframe === 'week') {
|
|
29
|
+
const startOfCurrentWeek = moment().startOf('week');
|
|
30
|
+
matchReq = {
|
|
31
|
+
'$gte': new Date(startOfCurrentWeek.clone().subtract(26, 'week').startOf('week').format()),
|
|
32
|
+
'$lt': new Date(startOfCurrentWeek.format())
|
|
33
|
+
};
|
|
34
|
+
groupFormat = '%V';
|
|
35
|
+
}
|
|
36
|
+
// Month timeframe
|
|
37
|
+
else if (data.timeframe === 'month') {
|
|
38
|
+
const startOfCurrentMonth = moment().startOf('month');
|
|
39
|
+
matchReq = {
|
|
40
|
+
'$gte': new Date(startOfCurrentMonth.clone().subtract(12, 'month').startOf('month').format()),
|
|
41
|
+
'$lt': new Date(startOfCurrentMonth.format())
|
|
42
|
+
};
|
|
43
|
+
groupFormat = '%m';
|
|
44
|
+
}
|
|
45
|
+
// Year timeframe
|
|
46
|
+
else if (data.timeframe === 'year') {
|
|
47
|
+
const startOfCurrentYear = moment().startOf('year');
|
|
48
|
+
matchReq = {
|
|
49
|
+
'$gte': new Date(startOfCurrentYear.clone().subtract(8, 'year').startOf('year').format()),
|
|
50
|
+
'$lt': new Date(startOfCurrentYear.format())
|
|
51
|
+
};
|
|
52
|
+
groupFormat = '%Y';
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (!groupFormat) {
|
|
56
|
+
return res.status(403).json({ message: 'Invalid request' });
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const repartitionData = await currentModel
|
|
60
|
+
.aggregate([
|
|
61
|
+
{
|
|
62
|
+
$match: {
|
|
63
|
+
[data.group_by]: matchReq
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
$group: {
|
|
68
|
+
_id: { $dateToString: { format: groupFormat, date: `$${data.group_by}` } },
|
|
69
|
+
count: { $sum: toSum }
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
$project: {
|
|
74
|
+
key: '$_id',
|
|
75
|
+
value: '$count',
|
|
76
|
+
_id: false
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
]);
|
|
80
|
+
|
|
81
|
+
const formattedData = [];
|
|
82
|
+
|
|
83
|
+
// Day timeframe
|
|
84
|
+
if (data.timeframe === 'day') {
|
|
85
|
+
for (let i = 1; i <= 30; i++) {
|
|
86
|
+
const currentDate = moment().subtract(i, 'day').startOf('day');
|
|
87
|
+
const countForTheTimeframe = _.find(repartitionData, { key: currentDate.format('YYYY-MM-DD') });
|
|
88
|
+
formattedData.push({
|
|
89
|
+
key: currentDate.format('DD/MM'),
|
|
90
|
+
value: countForTheTimeframe ? countForTheTimeframe.value : 0
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
// Week timeframe
|
|
95
|
+
else if (data.timeframe === 'week') {
|
|
96
|
+
for (let i = 1; i <= 26; i++) {
|
|
97
|
+
const currentWeek = moment().subtract(i, 'week').startOf('week');
|
|
98
|
+
const countForTheTimeframe = _.find(repartitionData, { key: currentWeek.format('WW') });
|
|
99
|
+
formattedData.push({
|
|
100
|
+
key: currentWeek.startOf('week').format('DD/MM'),
|
|
101
|
+
value: countForTheTimeframe ? countForTheTimeframe.value : 0
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
// Month timeframe
|
|
106
|
+
else if (data.timeframe === 'month') {
|
|
107
|
+
for (let i = 1; i <= 12; i++) {
|
|
108
|
+
const currentMonth = moment().subtract(i, 'month').startOf('month');
|
|
109
|
+
const countForTheTimeframe = _.find(repartitionData, { key: currentMonth.format('MM') });
|
|
110
|
+
formattedData.push({
|
|
111
|
+
key: currentMonth.startOf('month').format('MMM'),
|
|
112
|
+
value: countForTheTimeframe ? countForTheTimeframe.value : 0
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
// Year timeframe
|
|
117
|
+
else if (data.timeframe === 'year') {
|
|
118
|
+
for (let i = 1; i <= 8; i++) {
|
|
119
|
+
const currentYear = moment().subtract(i, 'year').startOf('year');
|
|
120
|
+
const countForTheTimeframe = _.find(repartitionData, { key: currentYear.format('YYYY') });
|
|
121
|
+
formattedData.push({
|
|
122
|
+
key: currentYear.startOf('year').format('YYYY'),
|
|
123
|
+
value: countForTheTimeframe ? countForTheTimeframe.value : 0
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const formattedDataOrdered = formattedData.reverse();
|
|
129
|
+
|
|
130
|
+
const chartConfig = {
|
|
131
|
+
xaxis: [
|
|
132
|
+
{ dataKey: 'key' }
|
|
133
|
+
],
|
|
134
|
+
yaxis: [
|
|
135
|
+
{ dataKey: 'value' },
|
|
136
|
+
// { dataKey: 'test', orientation: 'right' }
|
|
137
|
+
]
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
return {
|
|
141
|
+
success: true,
|
|
142
|
+
data: {
|
|
143
|
+
config: chartConfig,
|
|
144
|
+
data: formattedDataOrdered
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
};
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
module.exports = async (currentModel, data) => {
|
|
2
|
+
if (data.operation === 'sum') {
|
|
3
|
+
const sumData = await currentModel
|
|
4
|
+
.aggregate([{
|
|
5
|
+
$group: {
|
|
6
|
+
_id: `$${data.group_by}`,
|
|
7
|
+
count: { $sum: `$${data.field}` },
|
|
8
|
+
}
|
|
9
|
+
}]);
|
|
10
|
+
|
|
11
|
+
if (!sumData || !sumData[0] || typeof sumData[0].count !== 'number') {
|
|
12
|
+
return res.status(403).json();
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
return {
|
|
16
|
+
success: true,
|
|
17
|
+
data: {
|
|
18
|
+
config: null,
|
|
19
|
+
data: sumData[0].count
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
else if (data.operation === 'avg') {
|
|
24
|
+
const avgData = await currentModel
|
|
25
|
+
.aggregate([{
|
|
26
|
+
$group: {
|
|
27
|
+
_id: `$${data.group_by}`,
|
|
28
|
+
avg: { $avg: `$${data.field}` },
|
|
29
|
+
}
|
|
30
|
+
}]);
|
|
31
|
+
|
|
32
|
+
if (!avgData || !avgData[0] || typeof avgData[0].avg !== 'number') {
|
|
33
|
+
return res.status(403).json();
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return {
|
|
37
|
+
success: true,
|
|
38
|
+
data: {
|
|
39
|
+
config: null,
|
|
40
|
+
data: avgData[0].avg
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
const dataCount = await currentModel.countDocuments({});
|
|
46
|
+
|
|
47
|
+
return {
|
|
48
|
+
success: true,
|
|
49
|
+
data: {
|
|
50
|
+
config: null,
|
|
51
|
+
data: dataCount
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
};
|
|
@@ -3,8 +3,8 @@ const fnHelper = require('../helpers/functions');
|
|
|
3
3
|
|
|
4
4
|
module.exports.getAutocomplete = async (req, res) => {
|
|
5
5
|
const modelName = req.params.model;
|
|
6
|
-
const search = (req.
|
|
7
|
-
const refFields = req.
|
|
6
|
+
const search = (req.query.s || '').trim();
|
|
7
|
+
const refFields = req.headers['am-ref-fields'] || {};
|
|
8
8
|
const maxItem = 10;
|
|
9
9
|
|
|
10
10
|
const currentModel = fnHelper.getModelObject(modelName);
|
|
@@ -3,16 +3,16 @@ const fnHelper = require('../helpers/functions');
|
|
|
3
3
|
|
|
4
4
|
module.exports.getAll = async (req, res) => {
|
|
5
5
|
const modelName = req.params.model;
|
|
6
|
-
const segment = req.
|
|
7
|
-
const search = (req.
|
|
8
|
-
const filters = req.
|
|
9
|
-
const fieldsToFetch = req.
|
|
10
|
-
const refFields = req.
|
|
11
|
-
const fieldsToSearchIn = req.
|
|
12
|
-
const page = parseInt(req.
|
|
6
|
+
const segment = req.query.segment;
|
|
7
|
+
const search = (req.query.search || '').trim();
|
|
8
|
+
const filters = req.query.filters;
|
|
9
|
+
const fieldsToFetch = req.headers['am-model-fields'] || [];
|
|
10
|
+
const refFields = req.headers['am-ref-fields'] || {};
|
|
11
|
+
const fieldsToSearchIn = req.query.fieldsToSearchIn || [];
|
|
12
|
+
const page = parseInt(req.query.page || 1);
|
|
13
13
|
const nbItemPerPage = 10;
|
|
14
14
|
const defaultOrdering = [ ['_id', 'DESC'] ];
|
|
15
|
-
const order = req.
|
|
15
|
+
const order = req.query.order || null;
|
|
16
16
|
|
|
17
17
|
const currentModel = fnHelper.getModelObject(modelName);
|
|
18
18
|
if (!currentModel) {
|
|
@@ -4,7 +4,8 @@ const fnHelper = require('../helpers/functions');
|
|
|
4
4
|
module.exports.getOne = async (req, res) => {
|
|
5
5
|
const modelName = req.params.model;
|
|
6
6
|
const modelItemId = req.params.id;
|
|
7
|
-
const
|
|
7
|
+
const fieldsToFetch = req.headers['am-model-fields'] || [];
|
|
8
|
+
const refFields = req.headers['am-ref-fields'] || {};
|
|
8
9
|
|
|
9
10
|
const currentModel = fnHelper.getModelObject(modelName);
|
|
10
11
|
if (!currentModel) {
|
|
@@ -13,14 +14,14 @@ module.exports.getOne = async (req, res) => {
|
|
|
13
14
|
|
|
14
15
|
const keys = fnHelper.getModelProperties(currentModel);
|
|
15
16
|
const defaultFieldsToFetch = keys.map(key => key.path);
|
|
16
|
-
const
|
|
17
|
+
const fieldsToFetchSafe = Array.isArray(fieldsToFetch) && fieldsToFetch.length ? fieldsToFetch : defaultFieldsToFetch;
|
|
17
18
|
|
|
18
19
|
// Build ref fields for the model (for mongoose population purpose)
|
|
19
|
-
const fieldsToPopulate = fnHelper.getFieldsToPopulate(keys,
|
|
20
|
+
const fieldsToPopulate = fnHelper.getFieldsToPopulate(keys, fieldsToFetchSafe, refFields);
|
|
20
21
|
|
|
21
22
|
let data = await currentModel
|
|
22
23
|
.findById(modelItemId)
|
|
23
|
-
.select(
|
|
24
|
+
.select(fieldsToFetchSafe)
|
|
24
25
|
.populate(fieldsToPopulate)
|
|
25
26
|
.lean()
|
|
26
27
|
.catch(e => {
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
const _ = require('lodash');
|
|
2
|
-
const moment = require('moment');
|
|
3
1
|
const fnHelper = require('../helpers/functions');
|
|
2
|
+
const chartPie = require('./chart-pie');
|
|
3
|
+
const chartValue = require('./chart-value');
|
|
4
|
+
const chartTime = require('./chart-time');
|
|
5
|
+
const chartRanking = require('./chart-ranking');
|
|
4
6
|
|
|
5
7
|
module.exports.customQuery = async (req, res) => {
|
|
6
8
|
const data = req.body.data;
|
|
@@ -11,201 +13,39 @@ module.exports.customQuery = async (req, res) => {
|
|
|
11
13
|
return res.status(403).json({ message: 'Invalid request' });
|
|
12
14
|
}
|
|
13
15
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
res.json({ data: repartitionData });
|
|
16
|
+
let result = {
|
|
17
|
+
success: false,
|
|
18
|
+
message: ''
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
switch(data.type) {
|
|
22
|
+
case 'pie':
|
|
23
|
+
result = await chartPie(currentModel, data);
|
|
24
|
+
break;
|
|
25
|
+
|
|
26
|
+
case 'single_value':
|
|
27
|
+
case 'objective':
|
|
28
|
+
result = await chartValue(currentModel, data);
|
|
29
|
+
break;
|
|
30
|
+
|
|
31
|
+
case 'bar':
|
|
32
|
+
case 'line':
|
|
33
|
+
result = await chartTime(currentModel, data);
|
|
34
|
+
break;
|
|
35
|
+
|
|
36
|
+
case 'ranking':
|
|
37
|
+
result = await chartRanking(currentModel, data);
|
|
38
|
+
break;
|
|
39
39
|
}
|
|
40
|
-
else if (data.type === 'single_value' || data.type === 'objective') {
|
|
41
|
-
if (data.operation === 'sum') {
|
|
42
|
-
const sumData = await currentModel
|
|
43
|
-
.aggregate([{
|
|
44
|
-
$group: {
|
|
45
|
-
_id: `$${data.group_by}`,
|
|
46
|
-
count: { $sum: `$${data.field}` },
|
|
47
|
-
}
|
|
48
|
-
}]);
|
|
49
|
-
|
|
50
|
-
if (!sumData || !sumData[0] || typeof sumData[0].count !== 'number') {
|
|
51
|
-
return res.status(403).json();
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
res.json({ data: sumData[0].count });
|
|
55
|
-
}
|
|
56
|
-
else if (data.operation === 'avg') {
|
|
57
|
-
const avgData = await currentModel
|
|
58
|
-
.aggregate([{
|
|
59
|
-
$group: {
|
|
60
|
-
_id: `$${data.group_by}`,
|
|
61
|
-
avg: { $avg: `$${data.field}` },
|
|
62
|
-
}
|
|
63
|
-
}]);
|
|
64
|
-
|
|
65
|
-
if (!avgData || !avgData[0] || typeof avgData[0].avg !== 'number') {
|
|
66
|
-
return res.status(403).json();
|
|
67
|
-
}
|
|
68
40
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
}
|
|
41
|
+
// Response
|
|
42
|
+
if (result.success === true) {
|
|
43
|
+
return res.json({
|
|
44
|
+
chart: result.data
|
|
45
|
+
});
|
|
75
46
|
}
|
|
76
|
-
else if (data.type === 'bar' || data.type === 'line') {
|
|
77
|
-
const toSum = data.field && data.operation === 'sum' ? `$${data.field}` : 1;
|
|
78
|
-
|
|
79
|
-
let matchReq = {};
|
|
80
|
-
let groupFormat = '';
|
|
81
|
-
|
|
82
|
-
// Day timeframe
|
|
83
|
-
if (data.timeframe === 'day') {
|
|
84
|
-
matchReq = {
|
|
85
|
-
'$gte': new Date(moment().subtract(30, 'day').startOf('day').format()),
|
|
86
|
-
'$lte': new Date(moment().endOf('day').format())
|
|
87
|
-
};
|
|
88
|
-
groupFormat = '%Y-%m-%d';
|
|
89
|
-
}
|
|
90
|
-
// Week timeframe
|
|
91
|
-
else if (data.timeframe === 'week') {
|
|
92
|
-
matchReq = {
|
|
93
|
-
'$gte': new Date(moment().subtract(26, 'week').startOf('week').format()),
|
|
94
|
-
'$lte': new Date(moment().endOf('week').format())
|
|
95
|
-
};
|
|
96
|
-
groupFormat = '%V';
|
|
97
|
-
}
|
|
98
|
-
// Month timeframe
|
|
99
|
-
else if (data.timeframe === 'month') {
|
|
100
|
-
matchReq = {
|
|
101
|
-
'$gte': new Date(moment().subtract(12, 'month').startOf('month').format()),
|
|
102
|
-
'$lte': new Date(moment().endOf('month').format())
|
|
103
|
-
};
|
|
104
|
-
groupFormat = '%m';
|
|
105
|
-
}
|
|
106
|
-
// Year timeframe
|
|
107
|
-
else if (data.timeframe === 'year') {
|
|
108
|
-
matchReq = {
|
|
109
|
-
'$gte': new Date(moment().subtract(8, 'year').startOf('year').format()),
|
|
110
|
-
'$lte': new Date(moment().endOf('year').format())
|
|
111
|
-
};
|
|
112
|
-
groupFormat = '%Y';
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
if (!groupFormat) {
|
|
116
|
-
return res.status(403).json({ message: 'Invalid request' });
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
const repartitionData = await currentModel
|
|
120
|
-
.aggregate([
|
|
121
|
-
{
|
|
122
|
-
$match: {
|
|
123
|
-
[data.group_by]: matchReq
|
|
124
|
-
}
|
|
125
|
-
},
|
|
126
|
-
{
|
|
127
|
-
$group: {
|
|
128
|
-
_id: { $dateToString: { format: groupFormat, date: `$${data.group_by}` } },
|
|
129
|
-
count: { $sum: toSum }
|
|
130
|
-
}
|
|
131
|
-
},
|
|
132
|
-
{
|
|
133
|
-
$project: {
|
|
134
|
-
key: '$_id',
|
|
135
|
-
value: '$count',
|
|
136
|
-
_id: false
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
]);
|
|
140
47
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
if (data.timeframe === 'day') {
|
|
145
|
-
for (let i = 0; i < 30; i++) {
|
|
146
|
-
const currentDate = moment().subtract(i, 'day');
|
|
147
|
-
const countForTheTimeframe = _.find(repartitionData, { key: currentDate.format('YYYY-MM-DD') });
|
|
148
|
-
formattedData.push({
|
|
149
|
-
key: currentDate.format('DD/MM'),
|
|
150
|
-
value: countForTheTimeframe ? countForTheTimeframe.value : 0
|
|
151
|
-
});
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
// Week timeframe
|
|
155
|
-
else if (data.timeframe === 'week') {
|
|
156
|
-
for (let i = 0; i < 26; i++) {
|
|
157
|
-
const currentWeek = moment().subtract(i, 'week');
|
|
158
|
-
|
|
159
|
-
const countForTheTimeframe = _.find(repartitionData, { key: currentWeek.format('WW') });
|
|
160
|
-
formattedData.push({
|
|
161
|
-
key: currentWeek.startOf('week').format('DD/MM'),
|
|
162
|
-
value: countForTheTimeframe ? countForTheTimeframe.value : 0
|
|
163
|
-
});
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
// Month timeframe
|
|
167
|
-
else if (data.timeframe === 'month') {
|
|
168
|
-
for (let i = 0; i < 12; i++) {
|
|
169
|
-
const currentMonth = moment().subtract(i, 'month');
|
|
170
|
-
|
|
171
|
-
const countForTheTimeframe = _.find(repartitionData, { key: currentMonth.format('MM') });
|
|
172
|
-
formattedData.push({
|
|
173
|
-
key: currentMonth.startOf('month').format('MMM'),
|
|
174
|
-
value: countForTheTimeframe ? countForTheTimeframe.value : 0
|
|
175
|
-
});
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
// Year timeframe
|
|
179
|
-
else if (data.timeframe === 'year') {
|
|
180
|
-
for (let i = 0; i < 8; i++) {
|
|
181
|
-
const currentYear = moment().subtract(i, 'year');
|
|
182
|
-
|
|
183
|
-
const countForTheTimeframe = _.find(repartitionData, { key: currentYear.format('YYYY') });
|
|
184
|
-
formattedData.push({
|
|
185
|
-
key: currentYear.startOf('year').format('YYYY'),
|
|
186
|
-
value: countForTheTimeframe ? countForTheTimeframe.value : 0
|
|
187
|
-
});
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
formattedDataOrdered = formattedData.reverse();
|
|
192
|
-
|
|
193
|
-
const finalData = {
|
|
194
|
-
config: {
|
|
195
|
-
xaxis: [
|
|
196
|
-
{ dataKey: 'key' }
|
|
197
|
-
],
|
|
198
|
-
yaxis: [
|
|
199
|
-
{ dataKey: 'value' },
|
|
200
|
-
// { dataKey: 'test', orientation: 'right' }
|
|
201
|
-
]
|
|
202
|
-
},
|
|
203
|
-
data: formattedDataOrdered
|
|
204
|
-
};
|
|
205
|
-
|
|
206
|
-
res.json({ data: finalData });
|
|
207
|
-
}
|
|
208
|
-
else {
|
|
209
|
-
res.json({ data: null });
|
|
210
|
-
}
|
|
48
|
+
res.status(403).json({
|
|
49
|
+
message: result.message || 'Invalid request'
|
|
50
|
+
});
|
|
211
51
|
};
|