tango-app-api-analysis-traffic 3.0.0-alpha.0 → 3.0.0-alpha.2
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/package.json +3 -3
- package/src/controllers/tangoTrafficV1.controllers.js +905 -0
- package/src/dtos/validation.dtos.js +188 -0
- package/src/routes/traffic.routes.js +126 -7
- package/src/services/clients.services.js +23 -0
- package/src/services/group.service.js +6 -0
- package/src/services/userAssignedStore.service.js +9 -0
- package/app.js +0 -40
package/package.json
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tango-app-api-analysis-traffic",
|
|
3
|
-
"version": "3.0.0-alpha.
|
|
3
|
+
"version": "3.0.0-alpha.2",
|
|
4
4
|
"description": "Traffic Analysis",
|
|
5
|
-
"main": "
|
|
5
|
+
"main": "index.js",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"scripts": {
|
|
8
|
-
"start": "nodemon --exec \"eslint --fix . && node
|
|
8
|
+
"start": "nodemon --exec \"eslint --fix . && node index.js\""
|
|
9
9
|
},
|
|
10
10
|
"engines": {
|
|
11
11
|
"node": ">=18.10.0"
|
|
@@ -0,0 +1,905 @@
|
|
|
1
|
+
import { logger } from 'tango-app-api-middleware';
|
|
2
|
+
import * as clientService from '../services/clients.services.js';
|
|
3
|
+
import {
|
|
4
|
+
aggregateStore,
|
|
5
|
+
} from '../services/stores.service.js';
|
|
6
|
+
import { aggregateUserAssignedStore, findOneUserAssignedStore } from '../services/userAssignedStore.service.js';
|
|
7
|
+
import { aggregateGroup } from '../services/group.service.js';
|
|
8
|
+
|
|
9
|
+
// Lamda Service Call //
|
|
10
|
+
async function LamdaServiceCall( url, data ) {
|
|
11
|
+
try {
|
|
12
|
+
const requestOptions = {
|
|
13
|
+
method: 'POST',
|
|
14
|
+
headers: {
|
|
15
|
+
'Content-Type': 'application/json',
|
|
16
|
+
},
|
|
17
|
+
body: JSON.stringify( data ),
|
|
18
|
+
};
|
|
19
|
+
const response = await fetch( url, requestOptions );
|
|
20
|
+
if ( !response.ok ) {
|
|
21
|
+
throw new Error( `Response status: ${response.status}` );
|
|
22
|
+
return false;
|
|
23
|
+
}
|
|
24
|
+
const json = await response.json();
|
|
25
|
+
return json;
|
|
26
|
+
} catch ( error ) {
|
|
27
|
+
console.log( 'error =>', error );
|
|
28
|
+
logger.error( { error: error, message: data, function: 'LamdaServiceCall' } );
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// ///// V1 API's ///////
|
|
33
|
+
export const cardsFunnelV1 = async ( req, res ) => {
|
|
34
|
+
try {
|
|
35
|
+
let reqestData = req.body;
|
|
36
|
+
let getClientData = await clientService.findOne( { clientId: reqestData.clientId } );
|
|
37
|
+
if ( !getClientData ) {
|
|
38
|
+
return res.sendError( 'Invalid Client Id', 400 );
|
|
39
|
+
}
|
|
40
|
+
reqestData.currency = getClientData.paymentInvoice?.currencyType || 'INR';
|
|
41
|
+
reqestData.revenue = getClientData.averageTransactionValue || '0';
|
|
42
|
+
let LamdaURL = 'https://55mojecvuvtphucgsalx5jtyki0untzp.lambda-url.ap-south-1.on.aws/';
|
|
43
|
+
let resultData = await LamdaServiceCall( LamdaURL, reqestData );
|
|
44
|
+
if ( resultData ) {
|
|
45
|
+
if ( resultData.status_code == '200' ) {
|
|
46
|
+
return res.sendSuccess( resultData );
|
|
47
|
+
} else {
|
|
48
|
+
return res.sendError( 'No Content', 204 );
|
|
49
|
+
}
|
|
50
|
+
} else {
|
|
51
|
+
return res.sendError( 'No Content', 204 );
|
|
52
|
+
}
|
|
53
|
+
} catch ( error ) {
|
|
54
|
+
logger.error( { error: error, message: req.query, function: 'cardsFunnelV1' } );
|
|
55
|
+
return res.sendError( { error: error }, 500 );
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
export const cardsGraphsV1 = async ( req, res ) => {
|
|
60
|
+
try {
|
|
61
|
+
let reqestData = req.body;
|
|
62
|
+
let LamdaURL = 'https://xvsz4gd4erdlmvhv33hhyot3ui0xzitq.lambda-url.ap-south-1.on.aws/';
|
|
63
|
+
let resultData = await LamdaServiceCall( LamdaURL, reqestData );
|
|
64
|
+
if ( resultData ) {
|
|
65
|
+
if ( resultData.status_code == '200' ) {
|
|
66
|
+
return res.sendSuccess( resultData );
|
|
67
|
+
} else {
|
|
68
|
+
return res.sendError( 'No Content', 204 );
|
|
69
|
+
}
|
|
70
|
+
} else {
|
|
71
|
+
return res.sendError( 'No Content', 204 );
|
|
72
|
+
}
|
|
73
|
+
} catch ( error ) {
|
|
74
|
+
logger.error( { error: error, message: req.query, function: 'cardsFunnelV1' } );
|
|
75
|
+
return res.sendError( { error: error }, 500 );
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
export const recapVideoV1 = async ( req, res ) => {
|
|
80
|
+
try {
|
|
81
|
+
let reqestData = req.body;
|
|
82
|
+
let LamdaURL = 'https://u7xzph4jkl72sbefz2xx5rjw540rocck.lambda-url.ap-south-1.on.aws/';
|
|
83
|
+
let resultData = await LamdaServiceCall( LamdaURL, reqestData );
|
|
84
|
+
if ( resultData ) {
|
|
85
|
+
if ( resultData.status_code == '200' ) {
|
|
86
|
+
return res.sendSuccess( resultData );
|
|
87
|
+
} else {
|
|
88
|
+
return res.sendError( 'No Content', 204 );
|
|
89
|
+
}
|
|
90
|
+
} else {
|
|
91
|
+
return res.sendError( 'No Content', 204 );
|
|
92
|
+
}
|
|
93
|
+
} catch ( error ) {
|
|
94
|
+
logger.error( { error: error, message: req.query, function: 'cardsFunnelV1' } );
|
|
95
|
+
return res.sendError( { error: error }, 500 );
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
export const densityDwellV1 = async ( req, res ) => {
|
|
100
|
+
try {
|
|
101
|
+
let reqestData = req.body;
|
|
102
|
+
let LamdaURL = 'https://wh2d4dkgsao5kbwpjxbmchcjja0cxjhv.lambda-url.ap-south-1.on.aws/';
|
|
103
|
+
let resultData = await LamdaServiceCall( LamdaURL, reqestData );
|
|
104
|
+
if ( resultData ) {
|
|
105
|
+
if ( resultData.status_code == '200' ) {
|
|
106
|
+
return res.sendSuccess( resultData );
|
|
107
|
+
} else {
|
|
108
|
+
return res.sendError( 'No Content', 204 );
|
|
109
|
+
}
|
|
110
|
+
} else {
|
|
111
|
+
return res.sendError( 'No Content', 204 );
|
|
112
|
+
}
|
|
113
|
+
} catch ( error ) {
|
|
114
|
+
logger.error( { error: error, message: req.query, function: 'cardsFunnelV1' } );
|
|
115
|
+
return res.sendError( { error: error }, 500 );
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
export const overallCardsV1 = async ( req, res ) => {
|
|
120
|
+
try {
|
|
121
|
+
let reqestData = req.body;
|
|
122
|
+
let LamdaURL = 'https://dugu3ghkgalnpaydf2wdjp37240pzcmy.lambda-url.ap-south-1.on.aws/';
|
|
123
|
+
let resultData = await LamdaServiceCall( LamdaURL, reqestData );
|
|
124
|
+
if ( resultData ) {
|
|
125
|
+
if ( resultData.status_code == '200' ) {
|
|
126
|
+
return res.sendSuccess( resultData );
|
|
127
|
+
} else {
|
|
128
|
+
return res.sendError( 'No Content', 204 );
|
|
129
|
+
}
|
|
130
|
+
} else {
|
|
131
|
+
return res.sendError( 'No Content', 204 );
|
|
132
|
+
}
|
|
133
|
+
} catch ( error ) {
|
|
134
|
+
logger.error( { error: error, message: req.query, function: 'cardsFunnelV1' } );
|
|
135
|
+
return res.sendError( { error: error }, 500 );
|
|
136
|
+
}
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
export const overallHourlyChartV1 = async ( req, res ) => {
|
|
140
|
+
try {
|
|
141
|
+
let reqestData = req.body;
|
|
142
|
+
let LamdaURL = 'https://p3xcs56mkjj4sfugvsibhyto3i0gdbda.lambda-url.ap-south-1.on.aws/';
|
|
143
|
+
let resultData = await LamdaServiceCall( LamdaURL, reqestData );
|
|
144
|
+
if ( resultData ) {
|
|
145
|
+
if ( resultData.status_code == '200' ) {
|
|
146
|
+
return res.sendSuccess( resultData );
|
|
147
|
+
} else {
|
|
148
|
+
return res.sendError( 'No Content', 204 );
|
|
149
|
+
}
|
|
150
|
+
} else {
|
|
151
|
+
return res.sendError( 'No Content', 204 );
|
|
152
|
+
}
|
|
153
|
+
} catch ( error ) {
|
|
154
|
+
logger.error( { error: error, message: req.query, function: 'cardsFunnelV1' } );
|
|
155
|
+
return res.sendError( { error: error }, 500 );
|
|
156
|
+
}
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
export const overallChartV1 = async ( req, res ) => {
|
|
160
|
+
try {
|
|
161
|
+
let reqestData = req.body;
|
|
162
|
+
let LamdaURL = 'https://wtcllrsyec4iwkx6sg7xi2b7se0seawx.lambda-url.ap-south-1.on.aws/';
|
|
163
|
+
let resultData = await LamdaServiceCall( LamdaURL, reqestData );
|
|
164
|
+
if ( resultData ) {
|
|
165
|
+
if ( resultData.status_code == '200' ) {
|
|
166
|
+
return res.sendSuccess( resultData );
|
|
167
|
+
} else {
|
|
168
|
+
return res.sendError( 'No Content', 204 );
|
|
169
|
+
}
|
|
170
|
+
} else {
|
|
171
|
+
return res.sendError( 'No Content', 204 );
|
|
172
|
+
}
|
|
173
|
+
} catch ( error ) {
|
|
174
|
+
logger.error( { error: error, message: req.query, function: 'cardsFunnelV1' } );
|
|
175
|
+
return res.sendError( { error: error }, 500 );
|
|
176
|
+
}
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
export const singleStoreChartV1 = async ( req, res ) => {
|
|
180
|
+
try {
|
|
181
|
+
let reqestData = req.body;
|
|
182
|
+
let LamdaURL = 'https://j2mqa4qhmku3e7htuptsjvpumi0qkhkf.lambda-url.ap-south-1.on.aws/';
|
|
183
|
+
let resultData = await LamdaServiceCall( LamdaURL, reqestData );
|
|
184
|
+
if ( resultData ) {
|
|
185
|
+
if ( resultData.status_code == '200' ) {
|
|
186
|
+
return res.sendSuccess( resultData );
|
|
187
|
+
} else {
|
|
188
|
+
return res.sendError( 'No Content', 204 );
|
|
189
|
+
}
|
|
190
|
+
} else {
|
|
191
|
+
return res.sendError( 'No Content', 204 );
|
|
192
|
+
}
|
|
193
|
+
} catch ( error ) {
|
|
194
|
+
logger.error( { error: error, message: req.query, function: 'cardsFunnelV1' } );
|
|
195
|
+
return res.sendError( { error: error }, 500 );
|
|
196
|
+
}
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
export const demographicChartV1 = async ( req, res ) => {
|
|
200
|
+
try {
|
|
201
|
+
let reqestData = req.body;
|
|
202
|
+
let LamdaURL = 'https://7mslk3sde3m663mrgmwlc4rfou0alphk.lambda-url.ap-south-1.on.aws/';
|
|
203
|
+
let resultData = await LamdaServiceCall( LamdaURL, reqestData );
|
|
204
|
+
if ( resultData ) {
|
|
205
|
+
if ( resultData.status_code == '200' ) {
|
|
206
|
+
return res.sendSuccess( resultData );
|
|
207
|
+
} else {
|
|
208
|
+
return res.sendError( 'No Content', 204 );
|
|
209
|
+
}
|
|
210
|
+
} else {
|
|
211
|
+
return res.sendError( 'No Content', 204 );
|
|
212
|
+
}
|
|
213
|
+
} catch ( error ) {
|
|
214
|
+
logger.error( { error: error, message: req.query, function: 'cardsFunnelV1' } );
|
|
215
|
+
return res.sendError( { error: error }, 500 );
|
|
216
|
+
}
|
|
217
|
+
};
|
|
218
|
+
|
|
219
|
+
export const buyerChartV1 = async ( req, res ) => {
|
|
220
|
+
try {
|
|
221
|
+
let reqestData = req.body;
|
|
222
|
+
let LamdaURL = 'https://2i76wug6swytd7fej3d2fvd2xa0ujxhz.lambda-url.ap-south-1.on.aws/';
|
|
223
|
+
let resultData = await LamdaServiceCall( LamdaURL, reqestData );
|
|
224
|
+
if ( resultData ) {
|
|
225
|
+
if ( resultData.status_code == '200' ) {
|
|
226
|
+
return res.sendSuccess( resultData );
|
|
227
|
+
} else {
|
|
228
|
+
return res.sendError( 'No Content', 204 );
|
|
229
|
+
}
|
|
230
|
+
} else {
|
|
231
|
+
return res.sendError( 'No Content', 204 );
|
|
232
|
+
}
|
|
233
|
+
} catch ( error ) {
|
|
234
|
+
logger.error( { error: error, message: req.query, function: 'cardsFunnelV1' } );
|
|
235
|
+
return res.sendError( { error: error }, 500 );
|
|
236
|
+
}
|
|
237
|
+
};
|
|
238
|
+
|
|
239
|
+
export const footfallDirectoryFoldersV1 = async ( req, res ) => {
|
|
240
|
+
try {
|
|
241
|
+
let reqestData = req.body;
|
|
242
|
+
let LamdaURL = 'https://waxlhd7lfdlmyrkrdyv77najka0ayihq.lambda-url.ap-south-1.on.aws/';
|
|
243
|
+
let resultData = await LamdaServiceCall( LamdaURL, reqestData );
|
|
244
|
+
if ( resultData ) {
|
|
245
|
+
if ( resultData.status_code == '200' ) {
|
|
246
|
+
return res.sendSuccess( resultData );
|
|
247
|
+
} else {
|
|
248
|
+
return res.sendError( 'No Content', 204 );
|
|
249
|
+
}
|
|
250
|
+
} else {
|
|
251
|
+
return res.sendError( 'No Content', 204 );
|
|
252
|
+
}
|
|
253
|
+
} catch ( error ) {
|
|
254
|
+
logger.error( { error: error, message: req.query, function: 'cardsFunnelV1' } );
|
|
255
|
+
return res.sendError( { error: error }, 500 );
|
|
256
|
+
}
|
|
257
|
+
};
|
|
258
|
+
|
|
259
|
+
export const footfallDirectoryV1 = async ( req, res ) => {
|
|
260
|
+
try {
|
|
261
|
+
let reqestData = req.body;
|
|
262
|
+
let LamdaURL = 'https://waxlhd7lfdlmyrkrdyv77najka0ayihq.lambda-url.ap-south-1.on.aws/';
|
|
263
|
+
let resultData = await LamdaServiceCall( LamdaURL, reqestData );
|
|
264
|
+
if ( resultData ) {
|
|
265
|
+
if ( resultData.status_code == '200' ) {
|
|
266
|
+
return res.sendSuccess( resultData );
|
|
267
|
+
} else {
|
|
268
|
+
return res.sendError( 'No Content', 204 );
|
|
269
|
+
}
|
|
270
|
+
} else {
|
|
271
|
+
return res.sendError( 'No Content', 204 );
|
|
272
|
+
}
|
|
273
|
+
} catch ( error ) {
|
|
274
|
+
logger.error( { error: error, message: req.query, function: 'cardsFunnelV1' } );
|
|
275
|
+
return res.sendError( { error: error }, 500 );
|
|
276
|
+
}
|
|
277
|
+
};
|
|
278
|
+
|
|
279
|
+
export const summaryTableV1 = async ( req, res ) => {
|
|
280
|
+
try {
|
|
281
|
+
let reqestData = req.body;
|
|
282
|
+
let LamdaURL = 'https://fpmvquqpi7xw6hpaalxa5oa6pi0cvbql.lambda-url.ap-south-1.on.aws/';
|
|
283
|
+
let resultData = await LamdaServiceCall( LamdaURL, reqestData );
|
|
284
|
+
if ( resultData ) {
|
|
285
|
+
if ( resultData.status_code == '200' ) {
|
|
286
|
+
return res.sendSuccess( resultData );
|
|
287
|
+
} else {
|
|
288
|
+
return res.sendError( 'No Content', 204 );
|
|
289
|
+
}
|
|
290
|
+
} else {
|
|
291
|
+
return res.sendError( 'No Content', 204 );
|
|
292
|
+
}
|
|
293
|
+
} catch ( error ) {
|
|
294
|
+
logger.error( { error: error, message: req.query, function: 'cardsFunnelV1' } );
|
|
295
|
+
return res.sendError( { error: error }, 500 );
|
|
296
|
+
}
|
|
297
|
+
};
|
|
298
|
+
|
|
299
|
+
export const footfallTrendV1 = async ( req, res ) => {
|
|
300
|
+
try {
|
|
301
|
+
let reqestData = req.body;
|
|
302
|
+
let LamdaURL = 'https://l5uacls7kfyham5d7pd3i6c6pq0pflem.lambda-url.ap-south-1.on.aws/';
|
|
303
|
+
let resultData = await LamdaServiceCall( LamdaURL, reqestData );
|
|
304
|
+
if ( resultData ) {
|
|
305
|
+
if ( resultData.status_code == '200' ) {
|
|
306
|
+
return res.sendSuccess( resultData );
|
|
307
|
+
} else {
|
|
308
|
+
return res.sendError( 'No Content', 204 );
|
|
309
|
+
}
|
|
310
|
+
} else {
|
|
311
|
+
return res.sendError( 'No Content', 204 );
|
|
312
|
+
}
|
|
313
|
+
} catch ( error ) {
|
|
314
|
+
logger.error( { error: error, message: req.query, function: 'cardsFunnelV1' } );
|
|
315
|
+
return res.sendError( { error: error }, 500 );
|
|
316
|
+
}
|
|
317
|
+
};
|
|
318
|
+
|
|
319
|
+
export const storeOperationV1 = async ( req, res ) => {
|
|
320
|
+
try {
|
|
321
|
+
let reqestData = req.body;
|
|
322
|
+
let LamdaURL = 'https://mezoc2jy25t73v6eit57s3px6i0hxkwk.lambda-url.ap-south-1.on.aws/';
|
|
323
|
+
let resultData = await LamdaServiceCall( LamdaURL, reqestData );
|
|
324
|
+
if ( resultData ) {
|
|
325
|
+
if ( resultData.status_code == '200' ) {
|
|
326
|
+
return res.sendSuccess( resultData );
|
|
327
|
+
} else {
|
|
328
|
+
return res.sendError( 'No Content', 204 );
|
|
329
|
+
}
|
|
330
|
+
} else {
|
|
331
|
+
return res.sendError( 'No Content', 204 );
|
|
332
|
+
}
|
|
333
|
+
} catch ( error ) {
|
|
334
|
+
logger.error( { error: error, message: req.query, function: 'cardsFunnelV1' } );
|
|
335
|
+
return res.sendError( { error: error }, 500 );
|
|
336
|
+
}
|
|
337
|
+
};
|
|
338
|
+
|
|
339
|
+
export const storesMapV1 = async ( req, res ) => {
|
|
340
|
+
try {
|
|
341
|
+
let reqestData = req.body;
|
|
342
|
+
const storeList = [
|
|
343
|
+
{
|
|
344
|
+
$match: {
|
|
345
|
+
clientId: { $eq: '11' },
|
|
346
|
+
storeId: { $in: reqestData.storeId },
|
|
347
|
+
$and: [
|
|
348
|
+
{ 'storeProfile.latitude': { $exists: true } },
|
|
349
|
+
{ 'storeProfile.latitude': { $gt: 0 } },
|
|
350
|
+
{ 'storeProfile.latitude': { $exists: true } },
|
|
351
|
+
{ 'storeProfile.latitude': { $gt: 0 } },
|
|
352
|
+
],
|
|
353
|
+
},
|
|
354
|
+
},
|
|
355
|
+
{
|
|
356
|
+
$project: {
|
|
357
|
+
'_id': 0,
|
|
358
|
+
'storeId': 1,
|
|
359
|
+
'storeName': 1,
|
|
360
|
+
'appId': 1,
|
|
361
|
+
'storeProfile': 1,
|
|
362
|
+
'spocDetails': { $arrayElemAt: [ '$spocDetails', 0 ] },
|
|
363
|
+
'businessType': 1,
|
|
364
|
+
'storeType': 1,
|
|
365
|
+
'avgFootfall': '100',
|
|
366
|
+
'avgWeekdayFootfall': '30',
|
|
367
|
+
'avgWeekendFootfall': '60',
|
|
368
|
+
'hourlyFootfall': '60',
|
|
369
|
+
'openTime': '10:31 AM',
|
|
370
|
+
'closeTime': '10:31 PM',
|
|
371
|
+
},
|
|
372
|
+
},
|
|
373
|
+
{
|
|
374
|
+
$sort: {
|
|
375
|
+
createdAt: -1,
|
|
376
|
+
},
|
|
377
|
+
},
|
|
378
|
+
];
|
|
379
|
+
const getStores = await aggregateStore( storeList );
|
|
380
|
+
// Call Lamba and Get Footfall Count for each stores
|
|
381
|
+
|
|
382
|
+
if ( !getStores ) {
|
|
383
|
+
return res.sendError( 'No Content', 204 );
|
|
384
|
+
} else {
|
|
385
|
+
// let LamdaURL = 'https://luliwxsfuncjvpjbbilhwxp52q0zacvc.lambda-url.ap-south-1.on.aws/';
|
|
386
|
+
// let resultData = await LamdaServiceCall( LamdaURL, reqestData );
|
|
387
|
+
// if ( resultData ) {
|
|
388
|
+
// if ( resultData.status_code == '200' ) {
|
|
389
|
+
// return res.sendSuccess( resultData );
|
|
390
|
+
// } else {
|
|
391
|
+
// return res.sendError( 'No Content', 204 );
|
|
392
|
+
// }
|
|
393
|
+
// } else {
|
|
394
|
+
// return res.sendError( 'No Content', 204 );
|
|
395
|
+
// }
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
return res.sendSuccess( getStores );
|
|
399
|
+
} catch ( error ) {
|
|
400
|
+
console.log( 'error =>', error );
|
|
401
|
+
logger.error( { error: error, message: req.query, function: 'trafficCards' } );
|
|
402
|
+
return res.sendError( { error: error }, 500 );
|
|
403
|
+
}
|
|
404
|
+
};
|
|
405
|
+
|
|
406
|
+
export const headerLocationsV1 = async ( req, res ) => {
|
|
407
|
+
try {
|
|
408
|
+
let reqestData = req.body;
|
|
409
|
+
let getUserEmail = req.user.email;
|
|
410
|
+
let getClientId = reqestData.clientId;
|
|
411
|
+
let totalStores = await getAllStores( getUserEmail, getClientId );
|
|
412
|
+
if ( totalStores && totalStores.length>0 ) {
|
|
413
|
+
let storeQuery = [
|
|
414
|
+
{
|
|
415
|
+
$match: {
|
|
416
|
+
$and: [
|
|
417
|
+
{ storeId: { $in: totalStores } },
|
|
418
|
+
],
|
|
419
|
+
},
|
|
420
|
+
},
|
|
421
|
+
{
|
|
422
|
+
$group: {
|
|
423
|
+
_id: '$storeProfile.city',
|
|
424
|
+
},
|
|
425
|
+
},
|
|
426
|
+
{
|
|
427
|
+
$project: {
|
|
428
|
+
_id: 0,
|
|
429
|
+
city: '$_id',
|
|
430
|
+
},
|
|
431
|
+
},
|
|
432
|
+
{
|
|
433
|
+
$sort: {
|
|
434
|
+
city: -1,
|
|
435
|
+
},
|
|
436
|
+
},
|
|
437
|
+
];
|
|
438
|
+
const cityList = await aggregateStore( storeQuery );
|
|
439
|
+
if ( cityList && cityList.length > 0 ) {
|
|
440
|
+
return res.sendSuccess( { locationData: cityList } );
|
|
441
|
+
} else {
|
|
442
|
+
return res.sendError( 'No City', 400 );
|
|
443
|
+
}
|
|
444
|
+
} else {
|
|
445
|
+
return res.sendError( 'No stores', 400 );
|
|
446
|
+
}
|
|
447
|
+
} catch ( error ) {
|
|
448
|
+
logger.error( { error: error, message: req.query, function: 'trafficCards' } );
|
|
449
|
+
return res.sendError( { error: error }, 500 );
|
|
450
|
+
}
|
|
451
|
+
};
|
|
452
|
+
|
|
453
|
+
export const headerGroupsV1 = async ( req, res ) => {
|
|
454
|
+
try {
|
|
455
|
+
// let requestData = req.body;
|
|
456
|
+
let getUserEmail = req.user.email;
|
|
457
|
+
let groupQuery = [
|
|
458
|
+
{
|
|
459
|
+
$match: {
|
|
460
|
+
$and: [
|
|
461
|
+
{ userEmail: { $eq: getUserEmail } },
|
|
462
|
+
{ assignedType: { $eq: 'group' } },
|
|
463
|
+
],
|
|
464
|
+
},
|
|
465
|
+
},
|
|
466
|
+
{
|
|
467
|
+
$group: {
|
|
468
|
+
_id: '$assignedValue',
|
|
469
|
+
},
|
|
470
|
+
},
|
|
471
|
+
{
|
|
472
|
+
$lookup: {
|
|
473
|
+
from: 'groups',
|
|
474
|
+
let: { groupId: { $toObjectId: '$_id' } },
|
|
475
|
+
pipeline: [
|
|
476
|
+
{
|
|
477
|
+
$match: {
|
|
478
|
+
$expr: {
|
|
479
|
+
$eq: [ '$_id', '$$groupId' ],
|
|
480
|
+
},
|
|
481
|
+
},
|
|
482
|
+
},
|
|
483
|
+
{
|
|
484
|
+
$project: {
|
|
485
|
+
_id: 0,
|
|
486
|
+
groupName: 1,
|
|
487
|
+
},
|
|
488
|
+
},
|
|
489
|
+
], as: 'groups',
|
|
490
|
+
},
|
|
491
|
+
},
|
|
492
|
+
{
|
|
493
|
+
$unwind: {
|
|
494
|
+
path: '$groups', preserveNullAndEmptyArrays: true,
|
|
495
|
+
},
|
|
496
|
+
},
|
|
497
|
+
{
|
|
498
|
+
$unwind: {
|
|
499
|
+
path: '$groups.groupName', preserveNullAndEmptyArrays: true,
|
|
500
|
+
},
|
|
501
|
+
},
|
|
502
|
+
{
|
|
503
|
+
$project: {
|
|
504
|
+
_id: 0,
|
|
505
|
+
groupName: '$groups.groupName',
|
|
506
|
+
},
|
|
507
|
+
},
|
|
508
|
+
];
|
|
509
|
+
const groupList = await aggregateUserAssignedStore( groupQuery );
|
|
510
|
+
if ( groupList && groupList.length > 0 ) {
|
|
511
|
+
return res.sendSuccess( { groupData: groupList } );
|
|
512
|
+
} else {
|
|
513
|
+
return res.sendError( 'No Group', 400 );
|
|
514
|
+
}
|
|
515
|
+
} catch ( error ) {
|
|
516
|
+
logger.error( { error: error, message: req.query, function: 'trafficCards' } );
|
|
517
|
+
return res.sendError( { error: error }, 500 );
|
|
518
|
+
}
|
|
519
|
+
};
|
|
520
|
+
|
|
521
|
+
export const headerStoresV1 = async ( req, res ) => {
|
|
522
|
+
try {
|
|
523
|
+
let reqestData = req.body;
|
|
524
|
+
let getUserEmail = req.user.email;
|
|
525
|
+
let getClientId = reqestData.clientId;
|
|
526
|
+
let totalStores = await getAllStores( getUserEmail, getClientId );
|
|
527
|
+
if ( totalStores && totalStores.length>0 ) {
|
|
528
|
+
let storeQuery = [];
|
|
529
|
+
if ( reqestData.city.length>0 && reqestData.group.length>0 ) {
|
|
530
|
+
let unqueCityStores = await getLocationStores( getClientId, reqestData.city );
|
|
531
|
+
let unqueGroupStores = await getGroupStores( getClientId, reqestData.group );
|
|
532
|
+
storeQuery = [
|
|
533
|
+
{
|
|
534
|
+
$match: {
|
|
535
|
+
$and: [
|
|
536
|
+
{ storeId: { $in: totalStores } },
|
|
537
|
+
{ storeId: { $in: unqueCityStores } },
|
|
538
|
+
{ storeId: { $in: unqueGroupStores } },
|
|
539
|
+
],
|
|
540
|
+
},
|
|
541
|
+
},
|
|
542
|
+
{
|
|
543
|
+
$project: {
|
|
544
|
+
_id: 0,
|
|
545
|
+
storeId: '$storeId',
|
|
546
|
+
storeName: '$storeName',
|
|
547
|
+
},
|
|
548
|
+
},
|
|
549
|
+
];
|
|
550
|
+
} else if ( reqestData.city.length>0 ) {
|
|
551
|
+
let unqueCityStores = await getLocationStores( getClientId, reqestData.city );
|
|
552
|
+
storeQuery = [
|
|
553
|
+
{
|
|
554
|
+
$match: {
|
|
555
|
+
$and: [
|
|
556
|
+
{ storeId: { $in: totalStores } },
|
|
557
|
+
{ storeId: { $in: unqueCityStores } },
|
|
558
|
+
],
|
|
559
|
+
},
|
|
560
|
+
},
|
|
561
|
+
{
|
|
562
|
+
$project: {
|
|
563
|
+
_id: 0,
|
|
564
|
+
storeId: '$storeId',
|
|
565
|
+
storeName: '$storeName',
|
|
566
|
+
},
|
|
567
|
+
},
|
|
568
|
+
];
|
|
569
|
+
} else if ( reqestData.group.length>0 ) {
|
|
570
|
+
let unqueGroupStores = await getGroupStores( getClientId, reqestData.group );
|
|
571
|
+
storeQuery = [
|
|
572
|
+
{
|
|
573
|
+
$match: {
|
|
574
|
+
$and: [
|
|
575
|
+
{ storeId: { $in: totalStores } },
|
|
576
|
+
{ storeId: { $in: unqueGroupStores } },
|
|
577
|
+
],
|
|
578
|
+
},
|
|
579
|
+
},
|
|
580
|
+
{
|
|
581
|
+
$project: {
|
|
582
|
+
_id: 0,
|
|
583
|
+
storeId: '$storeId',
|
|
584
|
+
storeName: '$storeName',
|
|
585
|
+
},
|
|
586
|
+
},
|
|
587
|
+
];
|
|
588
|
+
} else {
|
|
589
|
+
let totalStores = await getAllStores( getUserEmail, getClientId );
|
|
590
|
+
storeQuery = [
|
|
591
|
+
{
|
|
592
|
+
$match: {
|
|
593
|
+
$and: [
|
|
594
|
+
{ storeId: { $in: totalStores } },
|
|
595
|
+
],
|
|
596
|
+
},
|
|
597
|
+
},
|
|
598
|
+
{
|
|
599
|
+
$project: {
|
|
600
|
+
_id: 0,
|
|
601
|
+
storeId: '$storeId',
|
|
602
|
+
storeName: '$storeName',
|
|
603
|
+
},
|
|
604
|
+
},
|
|
605
|
+
];
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
const storeList = await aggregateStore( storeQuery );
|
|
609
|
+
if ( storeList && storeList.length > 0 ) {
|
|
610
|
+
return res.sendSuccess( storeList );
|
|
611
|
+
} else {
|
|
612
|
+
return res.sendError( 'No Stores', 400 );
|
|
613
|
+
}
|
|
614
|
+
} else {
|
|
615
|
+
return res.sendError( 'No Stores', 400 );
|
|
616
|
+
}
|
|
617
|
+
} catch ( error ) {
|
|
618
|
+
console.log( 'headerStoresV1 =>', error );
|
|
619
|
+
logger.error( { error: error, message: req.query, function: 'headerStoresV1' } );
|
|
620
|
+
return res.sendError( { error: error }, 500 );
|
|
621
|
+
}
|
|
622
|
+
};
|
|
623
|
+
|
|
624
|
+
async function getAllStores( getUserEmail, getClientId ) {
|
|
625
|
+
try {
|
|
626
|
+
if ( getUserEmail && getUserEmail !='' ) {
|
|
627
|
+
const assignedQuery = {
|
|
628
|
+
clientId: getClientId,
|
|
629
|
+
userEmail: getUserEmail,
|
|
630
|
+
};
|
|
631
|
+
const getAssignedType = await findOneUserAssignedStore( assignedQuery );
|
|
632
|
+
if ( getAssignedType ) {
|
|
633
|
+
if ( getAssignedType.userType == 'client' ) {
|
|
634
|
+
if ( getAssignedType.assignedType && getAssignedType.assignedType !='' ) {
|
|
635
|
+
let overAllStores = [];
|
|
636
|
+
switch ( getAssignedType.assignedType ) {
|
|
637
|
+
case 'store':
|
|
638
|
+
let getAS = await getAssignedStores( getClientId, getUserEmail, 'store' );
|
|
639
|
+
if ( getAS && getAS.length >0 ) {
|
|
640
|
+
overAllStores = getAS;
|
|
641
|
+
}
|
|
642
|
+
break;
|
|
643
|
+
case 'group':
|
|
644
|
+
let getAGS = await getAssignedGroupStores( getClientId, getUserEmail, 'group' );
|
|
645
|
+
if ( getAGS && getAGS.length >0 ) {
|
|
646
|
+
overAllStores = getAGS;
|
|
647
|
+
}
|
|
648
|
+
break;
|
|
649
|
+
case 'allstores':
|
|
650
|
+
let getAllS = await getAssignedAllStores( getClientId );
|
|
651
|
+
if ( getAllS && getAllS.length >0 ) {
|
|
652
|
+
overAllStores = getAllS;
|
|
653
|
+
}
|
|
654
|
+
break;
|
|
655
|
+
default:
|
|
656
|
+
break;
|
|
657
|
+
}
|
|
658
|
+
return overAllStores;
|
|
659
|
+
} else {
|
|
660
|
+
return false;
|
|
661
|
+
}
|
|
662
|
+
} else if ( getAssignedType.userType == 'tango' ) {
|
|
663
|
+
if ( getAssignedType.assignedType && getAssignedType.assignedType !='' ) {
|
|
664
|
+
let overAllStores = [];
|
|
665
|
+
let getAllS = await getAssignedAllStores( getClientId );
|
|
666
|
+
if ( getAllS && getAllS.length >0 ) {
|
|
667
|
+
overAllStores = getAllS;
|
|
668
|
+
}
|
|
669
|
+
return overAllStores;
|
|
670
|
+
} else {
|
|
671
|
+
return false;
|
|
672
|
+
}
|
|
673
|
+
} else {
|
|
674
|
+
return false;
|
|
675
|
+
}
|
|
676
|
+
} else {
|
|
677
|
+
return false;
|
|
678
|
+
}
|
|
679
|
+
} else {
|
|
680
|
+
return false;
|
|
681
|
+
}
|
|
682
|
+
} catch ( error ) {
|
|
683
|
+
console.log( 'getAllStores =>', error );
|
|
684
|
+
logger.error( { error: error, message: req.query, function: 'getAllStores' } );
|
|
685
|
+
}
|
|
686
|
+
};
|
|
687
|
+
|
|
688
|
+
async function getAssignedStores( clientId, userEmail, assignedType ) {
|
|
689
|
+
try {
|
|
690
|
+
if ( clientId && clientId !='' && userEmail && userEmail !='' && assignedType && assignedType !='' ) {
|
|
691
|
+
let storeQuery = [
|
|
692
|
+
{
|
|
693
|
+
$match: {
|
|
694
|
+
$and: [
|
|
695
|
+
{ clientId: { $eq: clientId } },
|
|
696
|
+
{ userEmail: { $eq: userEmail } },
|
|
697
|
+
{ assignedType: { $eq: assignedType } },
|
|
698
|
+
],
|
|
699
|
+
},
|
|
700
|
+
},
|
|
701
|
+
{
|
|
702
|
+
$group: {
|
|
703
|
+
_id: null,
|
|
704
|
+
stores: { $push: '$assignedValue' },
|
|
705
|
+
},
|
|
706
|
+
},
|
|
707
|
+
];
|
|
708
|
+
const storeList = await aggregateUserAssignedStore( storeQuery );
|
|
709
|
+
if ( storeList && storeList.length>0 && storeList[0]?.stores.length > 0 ) {
|
|
710
|
+
let uniqueStores = [ ...new Set( storeList[0].stores ) ];
|
|
711
|
+
return uniqueStores;
|
|
712
|
+
} else {
|
|
713
|
+
return false;
|
|
714
|
+
}
|
|
715
|
+
} else {
|
|
716
|
+
return false;
|
|
717
|
+
}
|
|
718
|
+
} catch ( error ) {
|
|
719
|
+
console.log( 'getAssignedStores error =>', error );
|
|
720
|
+
logger.error( { error: error, message: data, function: 'getAssignedStores' } );
|
|
721
|
+
}
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
async function getAssignedGroupStores( clientId, userEmail, assignedType ) {
|
|
725
|
+
try {
|
|
726
|
+
if ( clientId && clientId !='' && userEmail && userEmail !='' && assignedType && assignedType !='' ) {
|
|
727
|
+
const groupQuery = [
|
|
728
|
+
{
|
|
729
|
+
$match: {
|
|
730
|
+
$and: [
|
|
731
|
+
{ clientId: { $eq: clientId } },
|
|
732
|
+
{ userEmail: { $eq: userEmail } },
|
|
733
|
+
{ assignedType: { $eq: assignedType } },
|
|
734
|
+
],
|
|
735
|
+
},
|
|
736
|
+
},
|
|
737
|
+
{
|
|
738
|
+
$lookup: {
|
|
739
|
+
from: 'groups',
|
|
740
|
+
let: { groupId: { $toObjectId: '$assignedValue' } },
|
|
741
|
+
pipeline: [
|
|
742
|
+
{
|
|
743
|
+
$match: {
|
|
744
|
+
$expr: {
|
|
745
|
+
$eq: [ '$_id', '$$groupId' ],
|
|
746
|
+
},
|
|
747
|
+
},
|
|
748
|
+
},
|
|
749
|
+
{
|
|
750
|
+
$project: {
|
|
751
|
+
_id: 0,
|
|
752
|
+
storeList: 1,
|
|
753
|
+
},
|
|
754
|
+
},
|
|
755
|
+
], as: 'groups',
|
|
756
|
+
},
|
|
757
|
+
},
|
|
758
|
+
{
|
|
759
|
+
$unwind: {
|
|
760
|
+
path: '$groups', preserveNullAndEmptyArrays: true,
|
|
761
|
+
},
|
|
762
|
+
},
|
|
763
|
+
{
|
|
764
|
+
$unwind: {
|
|
765
|
+
path: '$groups.storeList', preserveNullAndEmptyArrays: true,
|
|
766
|
+
},
|
|
767
|
+
},
|
|
768
|
+
{
|
|
769
|
+
$group: {
|
|
770
|
+
_id: null,
|
|
771
|
+
stores: { $push: '$groups.storeList' },
|
|
772
|
+
},
|
|
773
|
+
},
|
|
774
|
+
];
|
|
775
|
+
const groupStoreList = await aggregateUserAssignedStore( groupQuery );
|
|
776
|
+
if ( groupStoreList && groupStoreList.length>0 && groupStoreList[0]?.stores.length > 0 ) {
|
|
777
|
+
let uniqueStores = [ ...new Set( groupStoreList[0].stores ) ];
|
|
778
|
+
return uniqueStores;
|
|
779
|
+
} else {
|
|
780
|
+
return false;
|
|
781
|
+
}
|
|
782
|
+
} else {
|
|
783
|
+
return false;
|
|
784
|
+
}
|
|
785
|
+
} catch ( error ) {
|
|
786
|
+
console.log( 'getAssignedGroupStores error =>', error );
|
|
787
|
+
logger.error( { error: error, message: data, function: 'getAssignedGroupStores' } );
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
async function getAssignedAllStores( userClientId ) {
|
|
792
|
+
try {
|
|
793
|
+
if ( userClientId && userClientId !='' ) {
|
|
794
|
+
let storeQuery = [
|
|
795
|
+
{
|
|
796
|
+
$match: {
|
|
797
|
+
$and: [
|
|
798
|
+
{ clientId: { $eq: userClientId } },
|
|
799
|
+
{ status: { $eq: 'active' } },
|
|
800
|
+
],
|
|
801
|
+
},
|
|
802
|
+
},
|
|
803
|
+
{
|
|
804
|
+
$group: {
|
|
805
|
+
_id: null,
|
|
806
|
+
stores: { $push: '$storeId' },
|
|
807
|
+
},
|
|
808
|
+
},
|
|
809
|
+
];
|
|
810
|
+
const storeList = await aggregateStore( storeQuery );
|
|
811
|
+
if ( storeList && storeList.length>0 && storeList[0]?.stores.length > 0 ) {
|
|
812
|
+
let uniqueStores = [ ...new Set( storeList[0].stores ) ];
|
|
813
|
+
return uniqueStores;
|
|
814
|
+
} else {
|
|
815
|
+
return false;
|
|
816
|
+
}
|
|
817
|
+
} else {
|
|
818
|
+
return false;
|
|
819
|
+
}
|
|
820
|
+
} catch ( error ) {
|
|
821
|
+
console.log( 'getAssignedAllStores error =>', error );
|
|
822
|
+
logger.error( { error: error, message: data, function: 'getAssignedAllStores' } );
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
|
|
826
|
+
async function getGroupStores( userClientId, groupList ) {
|
|
827
|
+
try {
|
|
828
|
+
if ( userClientId && userClientId !='' && groupList && groupList.length >0 ) {
|
|
829
|
+
let groupQuery = [
|
|
830
|
+
{
|
|
831
|
+
$match: {
|
|
832
|
+
$and: [
|
|
833
|
+
{ clientId: { $eq: userClientId } },
|
|
834
|
+
{ groupName: { $in: groupList } },
|
|
835
|
+
],
|
|
836
|
+
},
|
|
837
|
+
},
|
|
838
|
+
{
|
|
839
|
+
$unwind: {
|
|
840
|
+
path: '$storeList', preserveNullAndEmptyArrays: true,
|
|
841
|
+
},
|
|
842
|
+
},
|
|
843
|
+
{
|
|
844
|
+
$group: {
|
|
845
|
+
_id: null,
|
|
846
|
+
stores: { $push: '$storeList' },
|
|
847
|
+
},
|
|
848
|
+
},
|
|
849
|
+
];
|
|
850
|
+
const groupStoreList = await aggregateGroup( groupQuery );
|
|
851
|
+
if ( groupStoreList && groupStoreList.length>0 && groupStoreList[0]?.stores.length > 0 ) {
|
|
852
|
+
let uniqueStores = [ ...new Set( groupStoreList[0].stores ) ];
|
|
853
|
+
return uniqueStores;
|
|
854
|
+
} else {
|
|
855
|
+
return false;
|
|
856
|
+
}
|
|
857
|
+
} else {
|
|
858
|
+
return false;
|
|
859
|
+
}
|
|
860
|
+
} catch ( error ) {
|
|
861
|
+
console.log( 'getGroupStores error =>', error );
|
|
862
|
+
logger.error( { error: error, message: data, function: 'getGroupStores' } );
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
async function getLocationStores( userClientId, cityList ) {
|
|
867
|
+
try {
|
|
868
|
+
if ( userClientId && userClientId !='' && cityList && cityList.length >0 ) {
|
|
869
|
+
let storeQuery = [
|
|
870
|
+
{
|
|
871
|
+
$match: {
|
|
872
|
+
$and: [
|
|
873
|
+
{ clientId: { $eq: userClientId } },
|
|
874
|
+
{ 'storeProfile.city': { $in: cityList } },
|
|
875
|
+
],
|
|
876
|
+
},
|
|
877
|
+
},
|
|
878
|
+
{
|
|
879
|
+
$project: {
|
|
880
|
+
_id: 0,
|
|
881
|
+
storeId: '$storeId',
|
|
882
|
+
},
|
|
883
|
+
},
|
|
884
|
+
{
|
|
885
|
+
$group: {
|
|
886
|
+
_id: null,
|
|
887
|
+
stores: { $push: '$storeId' },
|
|
888
|
+
},
|
|
889
|
+
},
|
|
890
|
+
];
|
|
891
|
+
const cityStoreList = await aggregateStore( storeQuery );
|
|
892
|
+
if ( cityStoreList && cityStoreList.length>0 && cityStoreList[0]?.stores.length > 0 ) {
|
|
893
|
+
let uniqueStores = [ ...new Set( cityStoreList[0].stores ) ];
|
|
894
|
+
return uniqueStores;
|
|
895
|
+
} else {
|
|
896
|
+
return false;
|
|
897
|
+
}
|
|
898
|
+
} else {
|
|
899
|
+
return false;
|
|
900
|
+
}
|
|
901
|
+
} catch ( error ) {
|
|
902
|
+
console.log( 'getLocationStores error =>', error );
|
|
903
|
+
logger.error( { error: error, message: data, function: 'getLocationStores' } );
|
|
904
|
+
}
|
|
905
|
+
}
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
import joi from 'joi';
|
|
2
|
+
|
|
3
|
+
// Base schema
|
|
4
|
+
const baseSchema = {
|
|
5
|
+
clientId: joi.string().required(),
|
|
6
|
+
storeId: joi.array().required().empty(),
|
|
7
|
+
fromDate: joi.string().required(),
|
|
8
|
+
toDate: joi.string().required(),
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
// Schema for Card Funnel
|
|
12
|
+
export const validateCardFunnelSchema = joi.object( {
|
|
13
|
+
...baseSchema,
|
|
14
|
+
valueType: joi.string().required(),
|
|
15
|
+
} );
|
|
16
|
+
|
|
17
|
+
export const validateCardFunnelParams = {
|
|
18
|
+
body: validateCardFunnelSchema,
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
// Schema for Card Graph (extends baseSchema with additional fields)
|
|
22
|
+
export const validateCardGraphSchema = joi.object( {
|
|
23
|
+
...baseSchema,
|
|
24
|
+
valueType: joi.string().required(),
|
|
25
|
+
dateType: joi.string().required(),
|
|
26
|
+
} );
|
|
27
|
+
|
|
28
|
+
export const validateCardGraphParams = {
|
|
29
|
+
body: validateCardGraphSchema,
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export const validateRecapVideoSchema = joi.object( {
|
|
33
|
+
clientId: joi.string().required(),
|
|
34
|
+
storeId: joi.array().required().empty(),
|
|
35
|
+
recapVideoDate: joi.string().required(),
|
|
36
|
+
} );
|
|
37
|
+
|
|
38
|
+
export const validateRecapVideoParams = {
|
|
39
|
+
body: validateRecapVideoSchema,
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export const validateDensityDwellSchema = joi.object( {
|
|
43
|
+
...baseSchema,
|
|
44
|
+
dateRange: joi.number().required(),
|
|
45
|
+
hourFormat: joi.number().required(),
|
|
46
|
+
} );
|
|
47
|
+
|
|
48
|
+
export const validateDensityDwellParams = {
|
|
49
|
+
body: validateDensityDwellSchema,
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
export const validateOverallCardsSchema = joi.object( {
|
|
53
|
+
...baseSchema,
|
|
54
|
+
revenue: joi.number().required(),
|
|
55
|
+
processType: joi.string().required(),
|
|
56
|
+
} );
|
|
57
|
+
|
|
58
|
+
export const validateOverallCardsParams = {
|
|
59
|
+
body: validateOverallCardsSchema,
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
export const validateOverallHourlyChartSchema = joi.object( {
|
|
63
|
+
...baseSchema,
|
|
64
|
+
hourFormat: joi.number().required(),
|
|
65
|
+
processType: joi.string().required(),
|
|
66
|
+
} );
|
|
67
|
+
|
|
68
|
+
export const validateOverallHourlyChartParams = {
|
|
69
|
+
body: validateOverallHourlyChartSchema,
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
export const validateOverallCharSchema = joi.object( {
|
|
73
|
+
...baseSchema,
|
|
74
|
+
limit: joi.number().required(),
|
|
75
|
+
offset: joi.number().required(),
|
|
76
|
+
processType: joi.string().required(),
|
|
77
|
+
} );
|
|
78
|
+
|
|
79
|
+
export const validateOverallCharParams = {
|
|
80
|
+
body: validateOverallCharSchema,
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
export const validateSingleStoreChartSchema = joi.object( {
|
|
84
|
+
...baseSchema,
|
|
85
|
+
limit: joi.number().required(),
|
|
86
|
+
offset: joi.number().required(),
|
|
87
|
+
processType: joi.string().required(),
|
|
88
|
+
} );
|
|
89
|
+
|
|
90
|
+
export const validateSingleStoreChartParams = {
|
|
91
|
+
body: validateSingleStoreChartSchema,
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
export const validateDemographicChartSchema = joi.object( {
|
|
95
|
+
...baseSchema,
|
|
96
|
+
processType: joi.string().required(),
|
|
97
|
+
} );
|
|
98
|
+
|
|
99
|
+
export const validateDemographicChartParams = {
|
|
100
|
+
body: validateDemographicChartSchema,
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
export const validateBuyerChartSchema = joi.object( {
|
|
104
|
+
...baseSchema,
|
|
105
|
+
processType: joi.string().required(),
|
|
106
|
+
} );
|
|
107
|
+
|
|
108
|
+
export const validateBuyerChartParams = {
|
|
109
|
+
body: validateBuyerChartSchema,
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
export const validateFootfallDirectoryFoldersSchema = joi.object( {
|
|
113
|
+
clientId: joi.string().required(),
|
|
114
|
+
storeId: joi.array().required().empty(),
|
|
115
|
+
footfallDate: joi.string().required(),
|
|
116
|
+
processType: joi.string().required(),
|
|
117
|
+
} );
|
|
118
|
+
|
|
119
|
+
export const validateFootfallDirectoryFoldersParams = {
|
|
120
|
+
body: validateFootfallDirectoryFoldersSchema,
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
export const validateFootfallDirectorySchema = joi.object( {
|
|
124
|
+
clientId: joi.string().required(),
|
|
125
|
+
storeId: joi.array().required().empty(),
|
|
126
|
+
footfallDate: joi.string().required(),
|
|
127
|
+
processType: joi.string().required(),
|
|
128
|
+
folderName: joi.string().required(),
|
|
129
|
+
} );
|
|
130
|
+
|
|
131
|
+
export const validateFootfallDirectoryParams = {
|
|
132
|
+
body: validateFootfallDirectorySchema,
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
export const validateSummaryTableSchema = joi.object( {
|
|
136
|
+
...baseSchema,
|
|
137
|
+
valueType: joi.string().required(),
|
|
138
|
+
search: joi.string().optional(),
|
|
139
|
+
sortBy: joi.number().optional(),
|
|
140
|
+
sort: joi.string().optional(),
|
|
141
|
+
limit: joi.number().required(),
|
|
142
|
+
offset: joi.number().required(),
|
|
143
|
+
} );
|
|
144
|
+
|
|
145
|
+
export const validateSummaryTableParams = {
|
|
146
|
+
body: validateSummaryTableSchema,
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
export const validateFootfallTrendSchema = joi.object( {
|
|
150
|
+
...baseSchema,
|
|
151
|
+
filterBy: joi.string().required(),
|
|
152
|
+
processType: joi.string().required(),
|
|
153
|
+
forecast: joi.boolean().optional(),
|
|
154
|
+
limit: joi.number().required(),
|
|
155
|
+
offset: joi.number().required(),
|
|
156
|
+
} );
|
|
157
|
+
|
|
158
|
+
export const validateFootfallTrendParams = {
|
|
159
|
+
body: validateFootfallTrendSchema,
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
export const validateStoreOperationSchema = joi.object( {
|
|
163
|
+
...baseSchema,
|
|
164
|
+
} );
|
|
165
|
+
|
|
166
|
+
export const validateStoreOperationParams = {
|
|
167
|
+
body: validateStoreOperationSchema,
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
export const validateStoresMapSchema = joi.object( {
|
|
171
|
+
clientId: joi.string().required(),
|
|
172
|
+
storeId: joi.array().required().empty(),
|
|
173
|
+
storeDate: joi.string().required(),
|
|
174
|
+
} );
|
|
175
|
+
|
|
176
|
+
export const validateStoresMapParams = {
|
|
177
|
+
body: validateStoresMapSchema,
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
export const validateHeaderSchema = joi.object( {
|
|
181
|
+
clientId: joi.string().required(),
|
|
182
|
+
city: joi.array().required(),
|
|
183
|
+
group: joi.array().required(),
|
|
184
|
+
} );
|
|
185
|
+
|
|
186
|
+
export const validateHeaderParams = {
|
|
187
|
+
body: validateHeaderSchema,
|
|
188
|
+
};
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import express from 'express';
|
|
2
|
+
import { validate, isAllowedSessionHandler, authorize } from 'tango-app-api-middleware';
|
|
3
|
+
import * as validationDtos from '../dtos/validation.dtos.js';
|
|
2
4
|
|
|
3
5
|
export const analysisTrafficRouter = express.Router();
|
|
4
6
|
|
|
@@ -6,6 +8,7 @@ import {
|
|
|
6
8
|
welcome,
|
|
7
9
|
cardsFunnel,
|
|
8
10
|
cardsGraphs,
|
|
11
|
+
recapVideo,
|
|
9
12
|
densityDwell,
|
|
10
13
|
overallCards,
|
|
11
14
|
overallHourlyChart,
|
|
@@ -17,21 +20,43 @@ import {
|
|
|
17
20
|
footfallDirectory,
|
|
18
21
|
summaryTable,
|
|
19
22
|
footfallTrend,
|
|
20
|
-
recapVideo,
|
|
21
|
-
storesMap,
|
|
22
|
-
performanceMatrix,
|
|
23
23
|
zoneDwellTimeSplit,
|
|
24
24
|
storeOperation,
|
|
25
|
+
performanceMatrix,
|
|
26
|
+
storesMap,
|
|
25
27
|
headerLocations,
|
|
26
28
|
headerGroups,
|
|
27
29
|
headerStores,
|
|
28
30
|
} from '../controllers/tangoTraffic.controllers.js';
|
|
29
31
|
|
|
32
|
+
import {
|
|
33
|
+
cardsFunnelV1,
|
|
34
|
+
cardsGraphsV1,
|
|
35
|
+
recapVideoV1,
|
|
36
|
+
densityDwellV1,
|
|
37
|
+
overallCardsV1,
|
|
38
|
+
overallHourlyChartV1,
|
|
39
|
+
overallChartV1,
|
|
40
|
+
singleStoreChartV1,
|
|
41
|
+
demographicChartV1,
|
|
42
|
+
buyerChartV1,
|
|
43
|
+
footfallDirectoryFoldersV1,
|
|
44
|
+
footfallDirectoryV1,
|
|
45
|
+
summaryTableV1,
|
|
46
|
+
footfallTrendV1,
|
|
47
|
+
storeOperationV1,
|
|
48
|
+
storesMapV1,
|
|
49
|
+
headerStoresV1,
|
|
50
|
+
headerLocationsV1,
|
|
51
|
+
headerGroupsV1,
|
|
52
|
+
} from '../controllers/tangoTrafficV1.controllers.js';
|
|
53
|
+
|
|
30
54
|
|
|
31
55
|
analysisTrafficRouter
|
|
32
56
|
.get( '/welcome', welcome )
|
|
33
57
|
.post( '/cardsFunnel', cardsFunnel )
|
|
34
58
|
.post( '/cardsGraphs', cardsGraphs )
|
|
59
|
+
.post( '/recapVideo', recapVideo )
|
|
35
60
|
.post( '/densityDwell', densityDwell )
|
|
36
61
|
.post( '/overallCards', overallCards )
|
|
37
62
|
.post( '/overallHourlyChart', overallHourlyChart )
|
|
@@ -43,13 +68,107 @@ analysisTrafficRouter
|
|
|
43
68
|
.post( '/footfallDirectoryFolders', footfallDirectoryFolders )
|
|
44
69
|
.post( '/summaryTable', summaryTable )
|
|
45
70
|
.post( '/footfallTrend', footfallTrend )
|
|
46
|
-
.post( '/recapVideo', recapVideo )
|
|
47
|
-
.post( '/storesMap', storesMap )
|
|
48
|
-
.post( '/performanceMatrix', performanceMatrix )
|
|
49
71
|
.post( '/zoneDwellTimeSplit', zoneDwellTimeSplit )
|
|
50
72
|
.post( '/storeOperation', storeOperation )
|
|
73
|
+
.post( '/performanceMatrix', performanceMatrix )
|
|
74
|
+
.post( '/storesMap', storesMap )
|
|
51
75
|
.post( '/headerLocations', headerLocations )
|
|
52
76
|
.post( '/headerGroups', headerGroups )
|
|
53
|
-
.post( '/headerStores', headerStores )
|
|
77
|
+
.post( '/headerStores', headerStores )
|
|
78
|
+
.post( '/cardsFunnel_v1', isAllowedSessionHandler, authorize( {
|
|
79
|
+
userType: [ 'tango', 'client' ], access: [
|
|
80
|
+
{ featureName: 'analytics', name: 'tangoTraffic', permissions: [ 'isView' ] },
|
|
81
|
+
],
|
|
82
|
+
} ), validate( validationDtos.validateCardFunnelParams ), cardsFunnelV1 )
|
|
83
|
+
.post( '/cardsGraphs_v1', isAllowedSessionHandler, authorize( {
|
|
84
|
+
userType: [ 'tango', 'client' ], access: [
|
|
85
|
+
{ featureName: 'analytics', name: 'tangoTraffic', permissions: [ 'isView' ] },
|
|
86
|
+
],
|
|
87
|
+
} ), validate( validationDtos.validateCardGraphParams ), cardsGraphsV1 )
|
|
88
|
+
.post( '/recapVideo_v1', isAllowedSessionHandler, authorize( {
|
|
89
|
+
userType: [ 'tango', 'client' ], access: [
|
|
90
|
+
{ featureName: 'analytics', name: 'tangoTraffic', permissions: [ 'isView' ] },
|
|
91
|
+
],
|
|
92
|
+
} ), validate( validationDtos.validateRecapVideoParams ), recapVideoV1 )
|
|
93
|
+
.post( '/densityDwell_v1', isAllowedSessionHandler, authorize( {
|
|
94
|
+
userType: [ 'tango', 'client' ], access: [
|
|
95
|
+
{ featureName: 'analytics', name: 'tangoTraffic', permissions: [ 'isView' ] },
|
|
96
|
+
],
|
|
97
|
+
} ), validate( validationDtos.validateDensityDwellParams ), densityDwellV1 )
|
|
98
|
+
.post( '/overallCards_v1', isAllowedSessionHandler, authorize( {
|
|
99
|
+
userType: [ 'tango', 'client' ], access: [
|
|
100
|
+
{ featureName: 'analytics', name: 'tangoTraffic', permissions: [ 'isView' ] },
|
|
101
|
+
],
|
|
102
|
+
} ), validate( validationDtos.validateOverallCardsParams ), overallCardsV1 )
|
|
103
|
+
.post( '/overallHourlyChart_v1', isAllowedSessionHandler, authorize( {
|
|
104
|
+
userType: [ 'tango', 'client' ], access: [
|
|
105
|
+
{ featureName: 'analytics', name: 'tangoTraffic', permissions: [ 'isView' ] },
|
|
106
|
+
],
|
|
107
|
+
} ), validate( validationDtos.validateOverallHourlyChartParams ), overallHourlyChartV1 )
|
|
108
|
+
.post( '/overallChart_v1', isAllowedSessionHandler, authorize( {
|
|
109
|
+
userType: [ 'tango', 'client' ], access: [
|
|
110
|
+
{ featureName: 'analytics', name: 'tangoTraffic', permissions: [ 'isView' ] },
|
|
111
|
+
],
|
|
112
|
+
} ), validate( validationDtos.validateOverallCharParams ), overallChartV1 )
|
|
113
|
+
.post( '/singleStoreChart_v1', isAllowedSessionHandler, authorize( {
|
|
114
|
+
userType: [ 'tango', 'client' ], access: [
|
|
115
|
+
{ featureName: 'analytics', name: 'tangoTraffic', permissions: [ 'isView' ] },
|
|
116
|
+
],
|
|
117
|
+
} ), validate( validationDtos.validateSingleStoreChartParams ), singleStoreChartV1 )
|
|
118
|
+
.post( '/demographicChart_v1', isAllowedSessionHandler, authorize( {
|
|
119
|
+
userType: [ 'tango', 'client' ], access: [
|
|
120
|
+
{ featureName: 'analytics', name: 'tangoTraffic', permissions: [ 'isView' ] },
|
|
121
|
+
],
|
|
122
|
+
} ), validate( validationDtos.validateDemographicChartParams ), demographicChartV1 )
|
|
123
|
+
.post( '/buyerChart_v1', isAllowedSessionHandler, authorize( {
|
|
124
|
+
userType: [ 'tango', 'client' ], access: [
|
|
125
|
+
{ featureName: 'analytics', name: 'tangoTraffic', permissions: [ 'isView' ] },
|
|
126
|
+
],
|
|
127
|
+
} ), validate( validationDtos.validateBuyerChartParams ), buyerChartV1 )
|
|
128
|
+
.post( '/footfallDirectoryFolders_v1', isAllowedSessionHandler, authorize( {
|
|
129
|
+
userType: [ 'tango', 'client' ], access: [
|
|
130
|
+
{ featureName: 'analytics', name: 'tangoTraffic', permissions: [ 'isView' ] },
|
|
131
|
+
],
|
|
132
|
+
} ), validate( validationDtos.validateFootfallDirectoryFoldersParams ), footfallDirectoryFoldersV1 )
|
|
133
|
+
.post( '/footfallDirectory_v1', isAllowedSessionHandler, authorize( {
|
|
134
|
+
userType: [ 'tango', 'client' ], access: [
|
|
135
|
+
{ featureName: 'analytics', name: 'tangoTraffic', permissions: [ 'isView' ] },
|
|
136
|
+
],
|
|
137
|
+
} ), validate( validationDtos.validateFootfallDirectoryParams ), footfallDirectoryV1 )
|
|
138
|
+
.post( '/summaryTable_v1', isAllowedSessionHandler, authorize( {
|
|
139
|
+
userType: [ 'tango', 'client' ], access: [
|
|
140
|
+
{ featureName: 'analytics', name: 'tangoTraffic', permissions: [ 'isView' ] },
|
|
141
|
+
],
|
|
142
|
+
} ), validate( validationDtos.validateSummaryTableParams ), summaryTableV1 )
|
|
143
|
+
.post( '/footfallTrend_v1', isAllowedSessionHandler, authorize( {
|
|
144
|
+
userType: [ 'tango', 'client' ], access: [
|
|
145
|
+
{ featureName: 'analytics', name: 'tangoTraffic', permissions: [ 'isView' ] },
|
|
146
|
+
],
|
|
147
|
+
} ), validate( validationDtos.validateFootfallTrendParams ), footfallTrendV1 )
|
|
148
|
+
.post( '/storeOperation_v1', isAllowedSessionHandler, authorize( {
|
|
149
|
+
userType: [ 'tango', 'client' ], access: [
|
|
150
|
+
{ featureName: 'analytics', name: 'tangoTraffic', permissions: [ 'isView' ] },
|
|
151
|
+
],
|
|
152
|
+
} ), validate( validationDtos.validateStoreOperationParams ), storeOperationV1 )
|
|
153
|
+
.post( '/storesMap_v1', isAllowedSessionHandler, authorize( {
|
|
154
|
+
userType: [ 'tango', 'client' ], access: [
|
|
155
|
+
{ featureName: 'analytics', name: 'tangoTraffic', permissions: [ 'isView' ] },
|
|
156
|
+
],
|
|
157
|
+
} ), validate( validationDtos.validateStoresMapParams ), storesMapV1 )
|
|
158
|
+
.post( '/headerLocations_v1', isAllowedSessionHandler, authorize( {
|
|
159
|
+
userType: [ 'tango', 'client' ], access: [
|
|
160
|
+
{ featureName: 'analytics', name: 'tangoTraffic', permissions: [ 'isView' ] },
|
|
161
|
+
],
|
|
162
|
+
} ), validate( validationDtos.validateHeaderParams ), headerLocationsV1 )
|
|
163
|
+
.post( '/headerGroups_v1', isAllowedSessionHandler, authorize( {
|
|
164
|
+
userType: [ 'tango', 'client' ], access: [
|
|
165
|
+
{ featureName: 'analytics', name: 'tangoTraffic', permissions: [ 'isView' ] },
|
|
166
|
+
],
|
|
167
|
+
} ), validate( validationDtos.validateHeaderParams ), headerGroupsV1 )
|
|
168
|
+
.post( '/headerStores_v1', isAllowedSessionHandler, authorize( {
|
|
169
|
+
userType: [ 'tango', 'client' ], access: [
|
|
170
|
+
{ featureName: 'analytics', name: 'tangoTraffic', permissions: [ 'isView' ] },
|
|
171
|
+
],
|
|
172
|
+
} ), validate( validationDtos.validateHeaderParams ), headerStoresV1 );
|
|
54
173
|
|
|
55
174
|
export default analysisTrafficRouter;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import model from 'tango-api-schema';
|
|
2
|
+
|
|
3
|
+
export const find = ( query = {}, record = {} ) => {
|
|
4
|
+
return model.clientModel.find( query, record );
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
export const findOne = ( query = {}, record = {} ) => {
|
|
8
|
+
return model.clientModel.findOne( query, record ).sort( { updatedAt: -1 } );
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export const updateOne = ( query = {}, record = {} ) => {
|
|
12
|
+
return model.clientModel.updateOne( query, { $set: record } );
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export const aggregate = ( query = [] ) => {
|
|
16
|
+
return model.clientModel.aggregate( query );
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export const getClientCount = ( query = {} ) => {
|
|
20
|
+
return model.clientModel.count( query );
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import userAssignedStoreModel from 'tango-api-schema/schema/userAssignedStore.model.js';
|
|
2
|
+
|
|
3
|
+
export async function aggregateUserAssignedStore( query ) {
|
|
4
|
+
return await userAssignedStoreModel.aggregate( query );
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
export async function findOneUserAssignedStore( query, record ) {
|
|
8
|
+
return await userAssignedStoreModel.findOne( query, record );
|
|
9
|
+
};
|
package/app.js
DELETED
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import express from 'express';
|
|
2
|
-
import { analysisTrafficRouter } from './index.js';
|
|
3
|
-
|
|
4
|
-
import dotenv from 'dotenv';
|
|
5
|
-
import pkg from 'body-parser';
|
|
6
|
-
const { json, urlencoded } = pkg;
|
|
7
|
-
import { logger } from 'tango-app-api-middleware';
|
|
8
|
-
import { connectdb } from './config/database/database.js';
|
|
9
|
-
import responseMiddleware from './config/response/response.js';
|
|
10
|
-
import errorMiddleware from './config/response/error.js';
|
|
11
|
-
import cors from 'cors';
|
|
12
|
-
|
|
13
|
-
const env=dotenv.config();
|
|
14
|
-
|
|
15
|
-
const app = express();
|
|
16
|
-
const PORT = process.env.PORT || 3000;
|
|
17
|
-
app.use( cors() );
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
if ( env.error ) {
|
|
21
|
-
logger.error( '.env not found' );
|
|
22
|
-
process.exit( 1 );
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
app.use( json( { limit: '500mb' } ) );
|
|
26
|
-
app.use(
|
|
27
|
-
urlencoded( {
|
|
28
|
-
extended: true,
|
|
29
|
-
} ),
|
|
30
|
-
);
|
|
31
|
-
|
|
32
|
-
app.use( responseMiddleware );
|
|
33
|
-
app.use( errorMiddleware );
|
|
34
|
-
|
|
35
|
-
app.use( '/api', analysisTrafficRouter );
|
|
36
|
-
|
|
37
|
-
app.listen( PORT, () => {
|
|
38
|
-
console.log( `server is running on port= ${PORT} ` );
|
|
39
|
-
connectdb();
|
|
40
|
-
} );
|