tango-app-api-analysis-traffic 3.0.0-alpha.54 → 3.0.0-alpha.56

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tango-app-api-analysis-traffic",
3
- "version": "3.0.0-alpha.54",
3
+ "version": "3.0.0-alpha.56",
4
4
  "description": "Traffic Analysis",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -24,7 +24,7 @@
24
24
  "nodemon": "^3.1.4",
25
25
  "swagger-ui-express": "^5.0.1",
26
26
  "tango-api-schema": "^2.2.7",
27
- "tango-app-api-middleware": "3.1.43-alpha.10",
27
+ "tango-app-api-middleware": "^3.1.46",
28
28
  "winston": "^3.13.1",
29
29
  "winston-daily-rotate-file": "^5.0.0"
30
30
  },
@@ -10,7 +10,6 @@ export async function storeList( req, res ) {
10
10
  if ( req.user.userType !== 'superadmin' && inputData?.assignedStores?.length == 0 ) {
11
11
  return res.sendSuccess( { result: [] } );
12
12
  }
13
- logger.info( { assignedStores: inputData?.assignedStores } );
14
13
  let filter = [
15
14
  {
16
15
  clientId: { $eq: req.clientId },
@@ -61,101 +60,26 @@ export async function storeList( req, res ) {
61
60
  export async function addBills( req, res ) {
62
61
  try {
63
62
  let resData=[];
64
- let errorData=[];
65
63
  const openSearch = JSON.parse( process.env.OPENSEARCH );
66
- const inputData = req.body;
67
- if ( req.user.role !== 'superadmin' && req.user.userType !== 'tango'&& ( inputData?.assignedStores?.length == 0 || !inputData?.assignedStores ) ) {
68
- return res.sendError( 'access forbidden', 403 );
69
- }
70
-
71
- let filter = [];
72
- if ( req.user.role !== 'superadmin' && req.user.userType !== 'tango' ) {
73
- filter.push( { storeId: { $in: inputData?.assignedStores } } );
74
- }
75
- for ( let i=0; i<inputData?.bills?.length; i++ ) {
76
- filter = [
77
- { clientId: { $eq: req.clientId } },
78
- { status: { $eq: 'active' } },
79
- {
80
- $or: [
81
- { 'storeId': inputData?.bills[i]?.storeCode },
82
- { 'storeProfile.storeCode': inputData?.bills[i]?.storeCode },
83
- ],
84
- },
85
- ];
86
- const query = [
87
- {
88
- $match: {
89
- $and: filter,
90
- },
91
- },
92
- {
93
- $project: {
94
- storeId: 1,
95
- storeName: 1,
96
- storeCode: '$storeProfile.storeCode',
64
+ const inputData = req.tempInserData;
97
65
 
98
- },
99
- },
100
- ];
101
- let storeData= await aggregateStore( query );
102
- if ( storeData?.length ==0 ) {
103
- errorData.push( { code: 400, storeId: inputData?.bills[i]?.storeId, message: 'Store code is not mapped with tango' } );
66
+ for ( let i=0; i<inputData?.length; i++ ) {
67
+ await updateOneNobBilling( inputData[i]?.query, inputData[i]?.data );
68
+ const getData = await findOneNobBilling( inputData[i]?.query, { _id: 0 } );
69
+ if ( !inputData[i]?.isUpdated ) {
70
+ await insertOpenSearchData( openSearch.nob, getData );
71
+ resData.push( { code: 200, tangoCode: getData.storeId, storeId: inputData[i]?.data?.storeCode, message: 'Data Inserted Successfully' } );
104
72
  } else {
105
- let searchQuery={
106
- 'size': 1,
107
- 'query': {
108
- 'bool': {
109
- 'must': [
110
- {
111
- 'term': {
112
- 'storeId.keyword': storeData[0].storeId,
113
- },
114
- },
115
- {
116
- 'term': {
117
- 'nobDate': dayjs( inputData?.bills[i]?.nobDate ).format( 'YYYY-MM-DD' ),
118
- },
119
- },
120
- ],
121
- },
122
- },
123
- };
124
- let searchData=await getOpenSearchData( openSearch.nob, searchQuery );
125
- let nobDateIso =new Date( inputData?.bills[i]?.nobDate );
126
- nobDateIso.setUTCHours( 0, 0, 0, 0 );
127
- const storeName = storeData[0]?.storeName;
128
- let inserData={
129
- clientId: req?.clientId,
130
- storeId: storeData[0]?.storeId,
131
- storeCode: storeData[0]?.storeCode,
132
- storeName: storeName.toLowerCase(),
133
- nobDate: nobDateIso,
134
- nobCount: inputData?.bills[i]?.nobCount,
135
- dateString: inputData?.bills[i]?.nobDate,
136
- nobAmount: 1.0,
137
- };
138
- const query ={ storeId: storeData[0]?.storeId, nobDate: inputData?.bills[i]?.nobDate };
139
- await updateOneNobBilling( query, inserData );
140
- const getData = await findOneNobBilling( query, { _id: 0 } );
141
- if ( searchData?.body?.hits?.hits?.length==0 || searchData?.body==undefined ) {
142
- await insertOpenSearchData( openSearch.nob, getData );
143
- resData.push( { code: 200, tangoCode: getData.storeId, storeId: inputData?.bills[i]?.storeCode, message: 'Data Inserted Successfully' } );
144
- } else {
145
- await updateOpenSearchData( openSearch.nob, searchData.body.hits.hits[0]._id, { doc: getData } );
146
- resData.push( { code: 200, tangoCode: storeData[0]?.storeId, storeId: inputData?.bills[i]?.storeCode, message: 'Data Updated Successfully' } );
147
- }
73
+ await updateOpenSearchData( openSearch.nob, inputData[i]?.searchData?.body?.hits?.hits[0]?._id, { doc: getData } );
74
+ resData.push( { code: 200, tangoCode: inputData[i]?.data?.storeId, storeId: inputData[i]?.data?.storeCode, message: 'Data Updated Successfully' } );
148
75
  }
149
76
  }
150
- if ( errorData.length > 0 && resData.length == 0 ) {
151
- return res.sendError( 'Store code is not mapped with tango', 400 );
152
- }
153
- logger.info( { resData: resData, errorData: errorData, function: 'nob-addbills' } );
154
- return res.sendSuccess( 'Data has been inserted successfully' );
77
+ logger.info( { resData: resData } );
78
+ return res.sendSuccess( 'Data has been inserted/updated successfully' );
155
79
  } catch ( error ) {
156
80
  const err= error.message || 'Internal Server Error';
157
81
  logger.error( { error: error, message: req.body, function: 'nob-addBills' } );
158
- res.sendError( err, 500 );
82
+ return res.sendError( err, 500 );
159
83
  }
160
84
  }
161
85
 
@@ -193,7 +117,6 @@ export async function getNobData( req, res ) {
193
117
 
194
118
  if ( inputData.searchValue && inputData.searchValue!== '' ) {
195
119
  const searchValue = escapeSpecialChars( inputData.searchValue );
196
- logger.info( { searchvalue: inputData.searchValue, searchValue: searchValue } );
197
120
  temp = {
198
121
 
199
122
  'must': filter,
@@ -238,24 +161,7 @@ export async function getNobData( req, res ) {
238
161
  const getNobData=await getOpenSearchData( openSearch.nob, nobQuery );
239
162
  const nobData = getNobData?.body?.hits?.hits;
240
163
  if ( !nobData ||nobData?.length == 0 ) {
241
- const initialQuery={
242
- 'size': 1,
243
- 'query': {
244
- 'bool': {
245
- 'must': [
246
- {
247
- 'term': {
248
- 'clientId.keyword': req.clientId,
249
- },
250
- },
251
- ],
252
- },
253
- },
254
- };
255
- const getInitialData=await getOpenSearchData( openSearch.nob, initialQuery );
256
- const initialData = getInitialData?.body?.hits?.hits;
257
- logger.info( { initialData: initialData, clientId: req.clientId } );
258
- if ( initialData && initialData.length > 0 ) {
164
+ if ( inputData.searchValue && inputData.searchValue!== '' ) {
259
165
  return res.sendError( 'No Data Found', 204 );
260
166
  } else {
261
167
  return res.sendSuccess( { initialInsert: false } );
@@ -263,6 +169,9 @@ export async function getNobData( req, res ) {
263
169
  }
264
170
 
265
171
  const footfallQuery={
172
+ '_source': {
173
+ 'exclude': [ 'storeName' ], // Specify the fields to exclude
174
+ },
266
175
  'query': {
267
176
  'bool': {
268
177
  'must': [
@@ -282,18 +191,19 @@ export async function getNobData( req, res ) {
282
191
 
283
192
  const getFootfall= await getOpenSearchData( openSearch.footfall, footfallQuery );
284
193
  const footfall = getFootfall?.body?.hits?.hits;
194
+ logger.info( { footfall: footfall, nobData: nobData } );
285
195
  let result=[];
286
196
  nobData.map( ( data ) => {
287
197
  let count = 0;
288
198
  let temp=[];
289
199
 
290
200
  data._source.isUpdated = data._source.createdAt !== data._source.updatedAt? true : false;
291
-
292
201
  footfall.filter( ( item ) => {
293
- if ( ( data._source.date_string === item._source.dateString ) && ( data._source.store_id === item._source.storeId ) ) {
202
+ if ( ( data._source.dateString === item._source.date_string ) && ( data._source.storeId === item._source.store_id ) ) {
294
203
  count = 1;
295
204
  temp =[
296
205
  {
206
+ storeName: item._source.store_name,
297
207
  footfallCount: item._source.footfall_count,
298
208
  engagersCount: item._source.engagers_count,
299
209
  potentialBuyers: item._source.potentialBuyers_count,
@@ -303,7 +213,6 @@ export async function getNobData( req, res ) {
303
213
  } );
304
214
 
305
215
  let conversionCount;
306
-
307
216
  if ( count === 1 ) {
308
217
  switch ( getClient?.featureConfigs?.conversionCalculation ) {
309
218
  case 'footfall-count':
@@ -321,10 +230,11 @@ export async function getNobData( req, res ) {
321
230
  result.push( {
322
231
 
323
232
  ...data._source,
233
+ storeName: temp[0]?.storeName,
324
234
  footfallCount: temp[0]?.footfallCount || null,
325
235
  engagersCount: temp[0]?.engagersCount || null,
326
236
  potentialBuyers: temp[0]?.potentialBuyers || null,
327
- conversionRate: conversionCount == null ? null :Math.round( conversionCount ),
237
+ conversionRate: conversionCount == null ? null :`${Math.round( conversionCount )} %`,
328
238
 
329
239
  } );
330
240
  } else {
@@ -368,7 +278,6 @@ export async function getNobData( req, res ) {
368
278
  }
369
279
  element.conversionRate? exportData[index]['Conversion Rate'] = `${element.conversionRate} %` : exportData[index]['Conversion Rate'] =null;
370
280
  } );
371
- logger.info( { exportData: exportData } );
372
281
  return exportData;
373
282
  } );
374
283
  const mappedArrays = await Promise.all( promises );
@@ -7,15 +7,15 @@ export const addBillsSchema = joi.object( {
7
7
  storeCode: joi.string().required().messages( {
8
8
  'string.empty': 'Please enter a valid Store ID',
9
9
  'any.required': 'Store ID is required',
10
- } ),
10
+ } ).allow( null ),
11
11
  nobDate: joi.string().required().messages( {
12
12
  'string.empty': 'Please enter a valid NOB Date',
13
13
  'any.required': 'NOB Date is required',
14
- } ),
14
+ } ).allow( null ),
15
15
  nobCount: joi.number().required().messages( {
16
16
  'string.empty': 'Please enter a valid NOB Count',
17
17
  'any.required': 'NOB Count is required',
18
- } ),
18
+ } ).allow( null ),
19
19
  } ),
20
20
  ).required(),
21
21
 
@@ -1,15 +1,15 @@
1
1
  import express from 'express';
2
- import { accessVerification, getAssinedStore, isAllowedSessionHandler, validate } from 'tango-app-api-middleware';
2
+ import { accessVerification, bulkValidate, getAssinedStore, isAllowedSessionHandler, validate } from 'tango-app-api-middleware';
3
3
  import { addBillsValid, getNobDataValid, storeListValid } from '../dtos/nob.dtos.js';
4
4
  import { addBills, getNobData, storeList } from '../controllers/nob.controllers.js';
5
- import { clientValidations, roleVerification } from '../validations/nob.validations.js';
5
+ import { clientValidations, fieldValidation, roleVerification } from '../validations/nob.validations.js';
6
6
 
7
7
  const nobRouter=express.Router();
8
8
 
9
9
  // store list
10
10
  nobRouter.get( '/store-list', isAllowedSessionHandler, accessVerification( { userType: [ 'client', 'tango' ] } ), validate( storeListValid ), clientValidations, getAssinedStore, storeList );
11
11
 
12
- nobRouter.post( '/add-bills', isAllowedSessionHandler, accessVerification( { userType: [ 'client', 'tango' ] } ), validate( addBillsValid ), roleVerification, clientValidations, getAssinedStore, addBills );
12
+ nobRouter.post( '/add-bills', isAllowedSessionHandler, accessVerification( { userType: [ 'client', 'tango' ] } ), bulkValidate( addBillsValid ), roleVerification, clientValidations, fieldValidation, getAssinedStore, addBills );
13
13
 
14
14
  nobRouter.post( '/get-nob-data', isAllowedSessionHandler, accessVerification( { userType: [ 'client', 'tango' ] } ), validate( getNobDataValid ), clientValidations, getAssinedStore, getNobData );
15
15
 
@@ -1,3 +1,7 @@
1
+ import { getOpenSearchData, logger } from 'tango-app-api-middleware';
2
+ import dayjs from 'dayjs';
3
+ import { aggregateStore } from '../services/stores.service.js';
4
+
1
5
  export async function clientValidations( req, res, next ) {
2
6
  try {
3
7
  const inputData = req.method === 'POST' ? req.body : req.query;
@@ -26,3 +30,115 @@ export async function roleVerification( req, res, next ) {
26
30
  return res.sendError( error, 500 );
27
31
  }
28
32
  }
33
+
34
+ export async function fieldValidation( req, res, next ) {
35
+ try {
36
+ const openSearch = JSON.parse( process.env.OPENSEARCH );
37
+ const inputData = req.body;
38
+ if ( req.user.role !== 'superadmin' && req.user.userType !== 'tango'&& ( inputData?.assignedStores?.length == 0 || !inputData?.assignedStores ) ) {
39
+ return res.sendError( 'access forbidden', 403 );
40
+ }
41
+ let tempInserData = [];
42
+ let filter = [];
43
+ if ( req.user.role !== 'superadmin' && req.user.userType !== 'tango' ) {
44
+ filter.push( { storeId: { $in: inputData?.assignedStores } } );
45
+ }
46
+ const inputFilter = inputData?.bills.filter( ( item ) => ( item.storeCode !== null && item.nobDate !== null && item.nobCount !== null ) );
47
+ // Find duplicates
48
+ const seen = new Map();
49
+ logger.info( { inputFilter: inputFilter } );
50
+ for ( let i=0; i<inputFilter?.length&& inputFilter.length !== 0; i++ ) {
51
+ const key = `${inputFilter[i]?.storeCode}-${inputFilter[i]?.nobDate}`; // Combine `storeCode` and `nobDate` to create a unique key
52
+ logger.info( { key: key } );
53
+ if ( seen.has( key ) ) {
54
+ logger.info( { message: 'deupkilicare' } );
55
+ // duplicates.push( { ...item, originalIndex: index } );
56
+ return res.sendError( `Error in index ${i+1}: Dublicate record exist`, 403 );
57
+ } else {
58
+ seen.set( key, i );
59
+ }
60
+ filter = [
61
+ { clientId: { $eq: req.clientId } },
62
+ { status: { $eq: 'active' } },
63
+ {
64
+ $or: [
65
+ { 'storeId': inputFilter[i]?.storeCode },
66
+ { 'storeProfile.storeCode': inputFilter[i]?.storeCode },
67
+ ],
68
+ },
69
+ ];
70
+ const query = [
71
+ {
72
+ $match: {
73
+ $and: filter,
74
+ },
75
+ },
76
+ {
77
+ $project: {
78
+ storeId: 1,
79
+ storeName: 1,
80
+ storeCode: '$storeProfile.storeCode',
81
+
82
+ },
83
+ },
84
+ ];
85
+ let storeData= await aggregateStore( query );
86
+ if ( storeData?.length ==0 ) {
87
+ return res.sendError( `Error in index ${i+1}: Store code is not mapped with tango`, 403 );
88
+ } else {
89
+ let searchQuery={
90
+ 'size': 1,
91
+ 'query': {
92
+ 'bool': {
93
+ 'must': [
94
+ {
95
+ 'term': {
96
+ 'storeId.keyword': storeData[0].storeId,
97
+ },
98
+ },
99
+ {
100
+ 'term': {
101
+ 'nobDate': dayjs( inputFilter[i]?.nobDate ).format( 'YYYY-MM-DD' ),
102
+ },
103
+ },
104
+ ],
105
+ },
106
+ },
107
+ };
108
+ let searchData=await getOpenSearchData( openSearch.nob, searchQuery );
109
+ if ( searchData?.body?.hits?.hits.length > 0 && searchData?.body?.hits?.hits[0]?._source?.createdAt !== searchData?.body?.hits?.hits[0]?._source?.updatedAt ) {
110
+ return res.sendError( `Error in index ${i+1}: Access Forbidden.You are trying to re-upload/edit more than one time `, 403 );
111
+ }
112
+
113
+ let nobDateIso =new Date( inputFilter[i]?.nobDate );
114
+ nobDateIso.setUTCHours( 0, 0, 0, 0 );
115
+ const storeName = storeData[0]?.storeName;
116
+ let inserData={
117
+ clientId: req?.clientId,
118
+ storeId: storeData[0]?.storeId,
119
+ storeCode: storeData[0]?.storeCode,
120
+ storeName: storeName.toLowerCase(),
121
+ nobDate: nobDateIso,
122
+ nobCount: inputFilter[i]?.nobCount,
123
+ dateString: inputFilter[i]?.nobDate,
124
+ };
125
+ const query ={ storeId: storeData[0]?.storeId, nobDate: inputFilter[i]?.nobDate };
126
+
127
+ tempInserData.push( { data: inserData, query: query, searchData: searchData, isUpdated: ( searchData?.body?.hits?.hits?.length==0 || searchData?.body==undefined )? false:true } );
128
+ }
129
+
130
+ if ( inputData?.bills?.length == i+1 ) {
131
+ if ( tempInserData.length > 0 ) {
132
+ req.tempInserData = tempInserData;
133
+ return next();
134
+ } else {
135
+ return res.sendError( 'Bad Request', 400 );
136
+ }
137
+ }
138
+ }
139
+ return res.sendError( 'Bad Request', 400 );
140
+ } catch ( error ) {
141
+ logger.error( { error: error, message: req.body, function: 'nob-roleVerification' } );
142
+ return res.sendError( error, 500 );
143
+ }
144
+ }