tango-app-api-store-zone 3.3.1 → 3.3.3
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/.eslintrc.cjs +1 -1
- package/package.json +8 -5
- package/src/controllers/zoneTagging.controller.js +2087 -57
- package/src/dtos/validation.dtos.js +146 -1
- package/src/routes/zoneTagging.routes.js +30 -11
- package/src/services/checklistconfig.services.js +31 -0
- package/src/services/customzonegrouping.service.js +55 -0
- package/src/services/customzonetag.service.js +56 -0
- package/src/services/tagging.service.js +3 -0
- package/src/validations/zone.validations.js +110 -0
|
@@ -1,22 +1,30 @@
|
|
|
1
1
|
import { logger } from 'tango-app-api-middleware';
|
|
2
2
|
import * as cameraService from '../services/camera.service.js';
|
|
3
3
|
import * as taggingService from '../services/tagging.service.js';
|
|
4
|
+
import * as customZoneTagService from '../services/customzonetag.service.js';
|
|
5
|
+
import * as customzonegrouping from '../services/customzonegrouping.service.js';
|
|
4
6
|
import * as storeService from '../services/store.service.js';
|
|
5
7
|
import * as clientService from '../services/client.service.js';
|
|
6
8
|
import * as externalService from '../services/external.service.js';
|
|
7
|
-
import { signedUrl, listFileByPath, fileUpload, insertOpenSearchData } from 'tango-app-api-middleware';
|
|
9
|
+
import { signedUrl, listFileByPath, fileUpload, insertOpenSearchData, download } from 'tango-app-api-middleware';
|
|
10
|
+
import * as checklistconfigService from '../services/checklistconfig.services.js';
|
|
8
11
|
import axios from 'axios';
|
|
12
|
+
import _ from 'lodash';
|
|
13
|
+
|
|
9
14
|
export const addCustomTag = async ( req, res ) => {
|
|
10
15
|
try {
|
|
11
16
|
let inputData = req.body;
|
|
12
17
|
let taggingDetails = await taggingService.findOne( { clientId: inputData.clientId, storeId: inputData.storeId, tagName: inputData.tagName, isDeleted: false } );
|
|
18
|
+
let findgroup = await customZoneTagService.findOne( { clientId: inputData.clientId, storeId: inputData.storeId, tagName: inputData.tagName } );
|
|
13
19
|
if ( !taggingDetails ) {
|
|
14
20
|
let data = {
|
|
15
21
|
clientId: inputData.clientId,
|
|
16
22
|
storeId: inputData.storeId,
|
|
17
23
|
tagName: inputData.tagName,
|
|
18
24
|
rgbColor: inputData.rgbColor,
|
|
25
|
+
productName: inputData.productName,
|
|
19
26
|
rgbBorderColor: inputData.rgbBorderColor,
|
|
27
|
+
groupName: findgroup.groupName,
|
|
20
28
|
};
|
|
21
29
|
await taggingService.deleteMany( { clientId: inputData.clientId, storeId: inputData.storeId, tagName: inputData.tagName, isDeleted: true } );
|
|
22
30
|
await taggingService.create( data );
|
|
@@ -30,8 +38,8 @@ export const addCustomTag = async ( req, res ) => {
|
|
|
30
38
|
date: new Date(),
|
|
31
39
|
logType: 'zone',
|
|
32
40
|
logSubType: 'addCustomTag',
|
|
33
|
-
changes: [ `${inputData.tagName}
|
|
34
|
-
eventType: '',
|
|
41
|
+
changes: [ `${inputData.tagName} custom tag` ],
|
|
42
|
+
eventType: 'create',
|
|
35
43
|
showTo: [ 'client', 'tango' ],
|
|
36
44
|
};
|
|
37
45
|
insertOpenSearchData( JSON.parse( process.env.OPENSEARCH )?.activityLog, logObj );
|
|
@@ -44,7 +52,7 @@ export const addCustomTag = async ( req, res ) => {
|
|
|
44
52
|
|
|
45
53
|
export const customTagList = async ( req, res ) => {
|
|
46
54
|
try {
|
|
47
|
-
let customTagList = [ 'Front', 'Back' ];
|
|
55
|
+
let customTagList = [ 'Front', 'Back', 'Tracker-in', 'Tracker-out' ];
|
|
48
56
|
let storeDetails = await storeService.findOne( { storeId: req.query.storeId }, { product: 1 } );
|
|
49
57
|
let clientDetails = await clientService.findOne( { clientId: req.query.clientId }, { featureConfigs: 1 } );
|
|
50
58
|
if ( clientDetails && clientDetails?.featureConfigs?.isExcludedArea ) {
|
|
@@ -149,6 +157,14 @@ export const customTagList = async ( req, res ) => {
|
|
|
149
157
|
i.rgbColor = 'rgba(193, 214, 114, 0.5)';
|
|
150
158
|
i.rgbBorderColor = 'rgb(203, 224, 124)';
|
|
151
159
|
}
|
|
160
|
+
if ( i.tagName === 'Tracker-in' ) {
|
|
161
|
+
i.rgbColor = 'rgba(123, 95, 105, 0.5)';
|
|
162
|
+
i.rgbBorderColor = 'rgb(133, 105, 115)';
|
|
163
|
+
}
|
|
164
|
+
if ( i.tagName === 'Tracker-out' ) {
|
|
165
|
+
i.rgbColor = 'rgba(12, 195, 111, 0.5)';
|
|
166
|
+
i.rgbBorderColor = 'rgb(22, 205, 125)';
|
|
167
|
+
}
|
|
152
168
|
} );
|
|
153
169
|
|
|
154
170
|
return res.sendSuccess( customTagList );
|
|
@@ -157,6 +173,526 @@ export const customTagList = async ( req, res ) => {
|
|
|
157
173
|
return res.sendError( e, 500 );
|
|
158
174
|
}
|
|
159
175
|
};
|
|
176
|
+
export const customTagListv2 = async ( req, res ) => {
|
|
177
|
+
try {
|
|
178
|
+
// Step 1: Base tag list
|
|
179
|
+
let customTagList = [
|
|
180
|
+
{ tagName: 'Front', productName: 'tangoTraffic' },
|
|
181
|
+
{ tagName: 'Back', productName: 'tangoTraffic' },
|
|
182
|
+
{ tagName: 'Tracker-in', productName: 'tangoTracker' },
|
|
183
|
+
{ tagName: 'Tracker-out', productName: 'tangoTracker' },
|
|
184
|
+
];
|
|
185
|
+
|
|
186
|
+
// Step 2: Fetch store & client details in parallel
|
|
187
|
+
const [ storeDetails, clientDetails, camList ] = await Promise.all( [
|
|
188
|
+
storeService.findOne(
|
|
189
|
+
{ storeId: req.query.storeId },
|
|
190
|
+
{ product: 1 },
|
|
191
|
+
),
|
|
192
|
+
clientService.findOne(
|
|
193
|
+
{ clientId: req.query.clientId },
|
|
194
|
+
{ featureConfigs: 1 },
|
|
195
|
+
),
|
|
196
|
+
cameraService.find(
|
|
197
|
+
{ storeId: req.query.storeId, isActivated: true, isUp: true },
|
|
198
|
+
{ streamName: 1 },
|
|
199
|
+
),
|
|
200
|
+
] );
|
|
201
|
+
|
|
202
|
+
// Step 3: Add conditional tags
|
|
203
|
+
if ( clientDetails?.featureConfigs?.isExcludedArea ) {
|
|
204
|
+
customTagList.push( { tagName: 'Excluded Area', productName: 'tangoTraffic' } );
|
|
205
|
+
}
|
|
206
|
+
if ( clientDetails?.featureConfigs?.isPasserByData ) {
|
|
207
|
+
customTagList.push( { tagName: 'Passer By', productName: 'tangoTraffic' } );
|
|
208
|
+
}
|
|
209
|
+
if ( storeDetails?.product?.includes( 'tangoZone' ) ) {
|
|
210
|
+
customTagList.push(
|
|
211
|
+
{ tagName: 'Entry/Exit', productName: 'tangoZone' },
|
|
212
|
+
{ tagName: 'Billing', productName: 'tangoZone' },
|
|
213
|
+
);
|
|
214
|
+
}
|
|
215
|
+
if ( req.query.selectedProduct && req.query.selectedProduct != '' ) {
|
|
216
|
+
customTagList = customTagList.filter( ( ele ) => ele.productName === req.query.selectedProduct );
|
|
217
|
+
}
|
|
218
|
+
// Step 4: Fetch existing tags
|
|
219
|
+
const tagInfo = await taggingService.find(
|
|
220
|
+
{ clientId: req.query.clientId, productName: req.query.selectedProduct },
|
|
221
|
+
{ tagName: 1, rgbColor: 1, rgbBorderColor: 1, productName: 1, groupName: 1 },
|
|
222
|
+
);
|
|
223
|
+
|
|
224
|
+
// Merge all tags
|
|
225
|
+
const mergedTags = [ ...customTagList, ...tagInfo ];
|
|
226
|
+
|
|
227
|
+
// Step 5: Build unique tag list
|
|
228
|
+
const uniqueTags = [ ...new Map( mergedTags.map( ( tag ) => [ tag.tagName, tag ] ) ).values() ];
|
|
229
|
+
|
|
230
|
+
// Step 6: Query counts per tag
|
|
231
|
+
const tagNames = uniqueTags.map( ( t ) => t.tagName );
|
|
232
|
+
let activeCam = camList.map( ( data ) => data.streamName );
|
|
233
|
+
const taggingDetails = await taggingService.aggregate( [
|
|
234
|
+
{
|
|
235
|
+
$match: {
|
|
236
|
+
productName: req.query.selectedProduct,
|
|
237
|
+
clientId: req.query.clientId,
|
|
238
|
+
storeId: req.query.storeId,
|
|
239
|
+
coordinates: { $exists: true, $ne: [] },
|
|
240
|
+
tagName: { $in: tagNames },
|
|
241
|
+
streamName: { $in: activeCam },
|
|
242
|
+
},
|
|
243
|
+
},
|
|
244
|
+
{ $group: { _id: '$tagName', count: { $sum: 1 } } },
|
|
245
|
+
] );
|
|
246
|
+
|
|
247
|
+
const countMap = new Map( taggingDetails.map( ( t ) => [ t._id, t.count ] ) );
|
|
248
|
+
|
|
249
|
+
// Step 7: Tag colors config (instead of many ifs)
|
|
250
|
+
const tagColors = {
|
|
251
|
+
'Front': { rgbColor: 'rgba(89, 80, 5, 0.5)', rgbBorderColor: 'rgb(99, 90, 15)' },
|
|
252
|
+
'Back': { rgbColor: 'rgba(94, 60, 107, 0.5)', rgbBorderColor: 'rgb(104, 70, 117)' },
|
|
253
|
+
'Excluded Area': { rgbColor: 'rgba(186, 60, 214, 0.5)', rgbBorderColor: 'rgb(196, 70, 224)' },
|
|
254
|
+
'Passer By': { rgbColor: 'rgba(81, 153, 247, 0.5)', rgbBorderColor: 'rgb(91, 163, 257)' },
|
|
255
|
+
'Entry/Exit': { rgbColor: 'rgba(224, 43, 170, 0.5)', rgbBorderColor: 'rgb(234, 53, 180)' },
|
|
256
|
+
'Billing': { rgbColor: 'rgba(193, 214, 114, 0.5)', rgbBorderColor: 'rgb(203, 224, 124)' },
|
|
257
|
+
'Tracker-in': { rgbColor: 'rgba(123, 95, 105, 0.5)', rgbBorderColor: 'rgb(133, 105, 115)' },
|
|
258
|
+
'Tracker-out': { rgbColor: 'rgba(12, 195, 111, 0.5)', rgbBorderColor: 'rgb(22, 205, 125)' },
|
|
259
|
+
};
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
// Step 8: Final processing
|
|
263
|
+
const finalTags = uniqueTags.map( ( tag ) => ( {
|
|
264
|
+
tagName: tag.tagName,
|
|
265
|
+
productName: tag.productName,
|
|
266
|
+
groupName: tag.groupName ? tag.groupName : '',
|
|
267
|
+
count: countMap.get( tag.tagName ) || 0,
|
|
268
|
+
rgbColor: tag.rgbColor || tagColors[tag.tagName]?.rgbColor,
|
|
269
|
+
rgbBorderColor: tag.rgbBorderColor || tagColors[tag.tagName]?.rgbBorderColor,
|
|
270
|
+
} ) );
|
|
271
|
+
const groupedData = finalTags.reduce( ( acc, item ) => {
|
|
272
|
+
if ( item.groupName === '' ) {
|
|
273
|
+
item.isGroup = false;
|
|
274
|
+
// If groupName is empty, push the item as a standalone object
|
|
275
|
+
acc.push( item );
|
|
276
|
+
} else {
|
|
277
|
+
// If groupName exists, find if we already started a group for it
|
|
278
|
+
let group = acc.find( ( i ) => i.isGroup && i.groupName === item.groupName );
|
|
279
|
+
|
|
280
|
+
if ( !group ) {
|
|
281
|
+
// Create a new group container
|
|
282
|
+
group = {
|
|
283
|
+
groupName: item.groupName,
|
|
284
|
+
isGroup: true, // flag to identify this is a group
|
|
285
|
+
children: [],
|
|
286
|
+
};
|
|
287
|
+
acc.push( group );
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// Push the current item into the group's children
|
|
291
|
+
group.children.push( item );
|
|
292
|
+
}
|
|
293
|
+
return acc;
|
|
294
|
+
}, [] );
|
|
295
|
+
|
|
296
|
+
|
|
297
|
+
// Step 9: Sort by count (desc)
|
|
298
|
+
groupedData.sort( ( a, b ) => b.count - a.count );
|
|
299
|
+
|
|
300
|
+
if ( req.query.selectedProduct && req.query.selectedProduct === 'tangoTrax' ) {
|
|
301
|
+
let Query = [ {
|
|
302
|
+
$match: {
|
|
303
|
+
client_id: req.query.clientId,
|
|
304
|
+
publish: true,
|
|
305
|
+
checkListType: { $ne: 'custom' },
|
|
306
|
+
},
|
|
307
|
+
}, {
|
|
308
|
+
$group: {
|
|
309
|
+
_id: '$_id',
|
|
310
|
+
sourceCheckList_id: { $last: '$_id' },
|
|
311
|
+
tagName: { $last: '$checkListName' },
|
|
312
|
+
},
|
|
313
|
+
},
|
|
314
|
+
{
|
|
315
|
+
$lookup: {
|
|
316
|
+
from: 'cameras',
|
|
317
|
+
let: { checkListName: '$tagName' },
|
|
318
|
+
pipeline: [
|
|
319
|
+
{
|
|
320
|
+
$match: {
|
|
321
|
+
$expr: {
|
|
322
|
+
$anyElementTrue: {
|
|
323
|
+
$map: {
|
|
324
|
+
input: { $ifNull: [ '$taggedChecklist', [] ] }, // ✅ default to empty array
|
|
325
|
+
as: 'tc',
|
|
326
|
+
in: { $eq: [ '$$tc.checkListName', '$$checkListName' ] },
|
|
327
|
+
},
|
|
328
|
+
},
|
|
329
|
+
},
|
|
330
|
+
},
|
|
331
|
+
},
|
|
332
|
+
{
|
|
333
|
+
$project: {
|
|
334
|
+
storeId: 1,
|
|
335
|
+
},
|
|
336
|
+
},
|
|
337
|
+
], as: 'cameraList',
|
|
338
|
+
},
|
|
339
|
+
},
|
|
340
|
+
{
|
|
341
|
+
$project: {
|
|
342
|
+
tagName: 1,
|
|
343
|
+
type: 'checklist',
|
|
344
|
+
cameraList: {
|
|
345
|
+
$filter: {
|
|
346
|
+
input: '$cameraList',
|
|
347
|
+
as: 'item',
|
|
348
|
+
cond: {
|
|
349
|
+
$eq: [ '$$item.storeId', req.query.storeId ],
|
|
350
|
+
},
|
|
351
|
+
},
|
|
352
|
+
},
|
|
353
|
+
},
|
|
354
|
+
},
|
|
355
|
+
{
|
|
356
|
+
$project: {
|
|
357
|
+
tagName: 1,
|
|
358
|
+
type: 1,
|
|
359
|
+
count: { $size: '$cameraList' },
|
|
360
|
+
},
|
|
361
|
+
},
|
|
362
|
+
{
|
|
363
|
+
$sort: {
|
|
364
|
+
count: -1,
|
|
365
|
+
},
|
|
366
|
+
},
|
|
367
|
+
|
|
368
|
+
];
|
|
369
|
+
let getChecklistData = await checklistconfigService.aggregate( Query );
|
|
370
|
+
if ( finalTags && finalTags.length > 0 ) {
|
|
371
|
+
let merged = getChecklistData.map( ( item ) => {
|
|
372
|
+
let match = finalTags.find( ( a ) => a.tagName === item.tagName );
|
|
373
|
+
if ( match ) {
|
|
374
|
+
return { ...item, ...match, count: item.count + match.count }; // add counts
|
|
375
|
+
}
|
|
376
|
+
return item;
|
|
377
|
+
} );
|
|
378
|
+
// also include any arr1 items not in arr2
|
|
379
|
+
finalTags.forEach( ( a ) => {
|
|
380
|
+
if ( !merged.find( ( m ) => m.tagName === a.tagName ) ) {
|
|
381
|
+
merged.push( a );
|
|
382
|
+
}
|
|
383
|
+
} );
|
|
384
|
+
merged.sort( ( a, b ) => b.count - a.count );
|
|
385
|
+
return res.sendSuccess( merged );
|
|
386
|
+
} else {
|
|
387
|
+
return res.sendSuccess( getChecklistData );
|
|
388
|
+
}
|
|
389
|
+
} else {
|
|
390
|
+
return res.sendSuccess( groupedData );
|
|
391
|
+
}
|
|
392
|
+
} catch ( e ) {
|
|
393
|
+
logger.error( { error: e, function: 'customTagList' } );
|
|
394
|
+
return res.sendError( e, 500 );
|
|
395
|
+
}
|
|
396
|
+
};
|
|
397
|
+
export const customTagListv3 = async ( req, res ) => {
|
|
398
|
+
try {
|
|
399
|
+
// Step 1: Base tag list
|
|
400
|
+
let customTagList = [
|
|
401
|
+
{ tagName: 'Front', productName: 'tangoTraffic' },
|
|
402
|
+
{ tagName: 'Back', productName: 'tangoTraffic' },
|
|
403
|
+
{ tagName: 'Tracker-in', productName: 'tangoTracker' },
|
|
404
|
+
{ tagName: 'Tracker-out', productName: 'tangoTracker' },
|
|
405
|
+
];
|
|
406
|
+
|
|
407
|
+
// Step 2: Fetch store & client details in parallel
|
|
408
|
+
const [ storeDetails, clientDetails, camList ] = await Promise.all( [
|
|
409
|
+
storeService.findOne(
|
|
410
|
+
{ storeId: req.query.storeId },
|
|
411
|
+
{ product: 1 },
|
|
412
|
+
),
|
|
413
|
+
clientService.findOne(
|
|
414
|
+
{ clientId: req.query.clientId },
|
|
415
|
+
{ featureConfigs: 1 },
|
|
416
|
+
),
|
|
417
|
+
cameraService.find(
|
|
418
|
+
{ storeId: req.query.storeId, isActivated: true, isUp: true },
|
|
419
|
+
{ streamName: 1 },
|
|
420
|
+
),
|
|
421
|
+
] );
|
|
422
|
+
|
|
423
|
+
// Step 3: Add conditional tags
|
|
424
|
+
if ( clientDetails?.featureConfigs?.isExcludedArea ) {
|
|
425
|
+
customTagList.push( { tagName: 'Excluded Area', productName: 'tangoTraffic' } );
|
|
426
|
+
}
|
|
427
|
+
if ( clientDetails?.featureConfigs?.isPasserByData ) {
|
|
428
|
+
customTagList.push( { tagName: 'Passer By', productName: 'tangoTraffic' } );
|
|
429
|
+
}
|
|
430
|
+
if ( storeDetails?.product?.includes( 'tangoZone' ) ) {
|
|
431
|
+
customTagList.push(
|
|
432
|
+
{ tagName: 'Entry/Exit', productName: 'tangoZone' },
|
|
433
|
+
{ tagName: 'Billing', productName: 'tangoZone' },
|
|
434
|
+
);
|
|
435
|
+
}
|
|
436
|
+
if ( req.query.selectedProduct && req.query.selectedProduct != '' ) {
|
|
437
|
+
customTagList = customTagList.filter( ( ele ) => ele.productName === req.query.selectedProduct );
|
|
438
|
+
}
|
|
439
|
+
// Step 4: Fetch existing tags
|
|
440
|
+
const tagInfo = await taggingService.find(
|
|
441
|
+
{ clientId: req.query.clientId, productName: req.query.selectedProduct },
|
|
442
|
+
{ tagName: 1, rgbColor: 1, rgbBorderColor: 1, productName: 1, groupName: 1 },
|
|
443
|
+
);
|
|
444
|
+
|
|
445
|
+
// Merge all tags
|
|
446
|
+
const mergedTags = [ ...customTagList, ...tagInfo ];
|
|
447
|
+
const uniqueTags = [ ...new Map( mergedTags.map( ( tag ) => [ tag.tagName, tag ] ) ).values() ];
|
|
448
|
+
|
|
449
|
+
// Step 6: Query counts per tag
|
|
450
|
+
const tagNames = uniqueTags.map( ( t ) => t.tagName );
|
|
451
|
+
let activeCam = camList.map( ( data ) => data.streamName );
|
|
452
|
+
const taggingDetails = await taggingService.aggregate( [
|
|
453
|
+
{
|
|
454
|
+
$match: {
|
|
455
|
+
productName: req.query.selectedProduct,
|
|
456
|
+
clientId: req.query.clientId,
|
|
457
|
+
storeId: req.query.storeId,
|
|
458
|
+
coordinates: { $exists: true, $ne: [] },
|
|
459
|
+
tagName: { $in: tagNames },
|
|
460
|
+
streamName: { $in: activeCam },
|
|
461
|
+
},
|
|
462
|
+
},
|
|
463
|
+
{ $group: { _id: '$tagName', count: { $sum: 1 } } },
|
|
464
|
+
] );
|
|
465
|
+
|
|
466
|
+
const countMap = new Map( taggingDetails.map( ( t ) => [ t._id, t.count ] ) );
|
|
467
|
+
|
|
468
|
+
// Step 7: Tag colors config (instead of many ifs)
|
|
469
|
+
const tagColors = {
|
|
470
|
+
'Front': { rgbColor: 'rgba(89, 80, 5, 0.5)', rgbBorderColor: 'rgb(99, 90, 15)' },
|
|
471
|
+
'Back': { rgbColor: 'rgba(94, 60, 107, 0.5)', rgbBorderColor: 'rgb(104, 70, 117)' },
|
|
472
|
+
'Excluded Area': { rgbColor: 'rgba(186, 60, 214, 0.5)', rgbBorderColor: 'rgb(196, 70, 224)' },
|
|
473
|
+
'Passer By': { rgbColor: 'rgba(81, 153, 247, 0.5)', rgbBorderColor: 'rgb(91, 163, 257)' },
|
|
474
|
+
'Entry/Exit': { rgbColor: 'rgba(224, 43, 170, 0.5)', rgbBorderColor: 'rgb(234, 53, 180)' },
|
|
475
|
+
'Billing': { rgbColor: 'rgba(193, 214, 114, 0.5)', rgbBorderColor: 'rgb(203, 224, 124)' },
|
|
476
|
+
'Tracker-in': { rgbColor: 'rgba(123, 95, 105, 0.5)', rgbBorderColor: 'rgb(133, 105, 115)' },
|
|
477
|
+
'Tracker-out': { rgbColor: 'rgba(12, 195, 111, 0.5)', rgbBorderColor: 'rgb(22, 205, 125)' },
|
|
478
|
+
};
|
|
479
|
+
|
|
480
|
+
|
|
481
|
+
// Step 8: Final processing
|
|
482
|
+
const finalTags = uniqueTags.map( ( tag ) => ( {
|
|
483
|
+
tagName: tag.tagName,
|
|
484
|
+
productName: tag.productName,
|
|
485
|
+
groupName: tag.groupName ? tag.groupName : '',
|
|
486
|
+
count: countMap.get( tag.tagName ) || 0,
|
|
487
|
+
rgbColor: tag.rgbColor || tagColors[tag.tagName]?.rgbColor,
|
|
488
|
+
rgbBorderColor: tag.rgbBorderColor || tagColors[tag.tagName]?.rgbBorderColor,
|
|
489
|
+
} ) );
|
|
490
|
+
|
|
491
|
+
|
|
492
|
+
console.log( '🚀 ~ customTagListv3 ~ mergedTags:', mergedTags );
|
|
493
|
+
|
|
494
|
+
if ( req.query.selectedProduct === 'tangoZone' ) {
|
|
495
|
+
const zonefinalTags = await customZoneTagService.aggregate( [
|
|
496
|
+
{
|
|
497
|
+
$match: {
|
|
498
|
+
clientId: req.query.clientId,
|
|
499
|
+
productName: req.query.selectedProduct,
|
|
500
|
+
},
|
|
501
|
+
},
|
|
502
|
+
|
|
503
|
+
// Count lookup
|
|
504
|
+
{
|
|
505
|
+
$lookup: {
|
|
506
|
+
from: 'taggings',
|
|
507
|
+
let: { tagName: '$tagName', clientId: '$clientId' },
|
|
508
|
+
pipeline: [
|
|
509
|
+
{
|
|
510
|
+
$match: {
|
|
511
|
+
$expr: {
|
|
512
|
+
$and: [
|
|
513
|
+
{ $eq: [ '$tagName', '$$tagName' ] },
|
|
514
|
+
{ $eq: [ '$clientId', '$$clientId' ] },
|
|
515
|
+
{ $in: [ '$streamName', activeCam ] },
|
|
516
|
+
{ $ne: [ '$coordinates', [] ] },
|
|
517
|
+
],
|
|
518
|
+
},
|
|
519
|
+
},
|
|
520
|
+
},
|
|
521
|
+
{ $count: 'count' },
|
|
522
|
+
],
|
|
523
|
+
as: 'tagsCount',
|
|
524
|
+
},
|
|
525
|
+
},
|
|
526
|
+
|
|
527
|
+
// Add count
|
|
528
|
+
{
|
|
529
|
+
$addFields: {
|
|
530
|
+
count: {
|
|
531
|
+
$ifNull: [ { $arrayElemAt: [ '$tagsCount.count', 0 ] }, 0 ],
|
|
532
|
+
},
|
|
533
|
+
},
|
|
534
|
+
},
|
|
535
|
+
|
|
536
|
+
{
|
|
537
|
+
$project: {
|
|
538
|
+
_id: 0,
|
|
539
|
+
tagName: 1,
|
|
540
|
+
productName: 1,
|
|
541
|
+
groupName: 1,
|
|
542
|
+
rgbColor: 1,
|
|
543
|
+
rgbBorderColor: 1,
|
|
544
|
+
count: 1,
|
|
545
|
+
},
|
|
546
|
+
},
|
|
547
|
+
{
|
|
548
|
+
$sort: {
|
|
549
|
+
count: -1,
|
|
550
|
+
},
|
|
551
|
+
},
|
|
552
|
+
|
|
553
|
+
// Split grouped vs ungrouped
|
|
554
|
+
{
|
|
555
|
+
$facet: {
|
|
556
|
+
grouped: [
|
|
557
|
+
{ $match: { groupName: { $nin: [ null, '' ] } } },
|
|
558
|
+
{
|
|
559
|
+
$group: {
|
|
560
|
+
_id: '$groupName',
|
|
561
|
+
children: { $push: '$$ROOT' },
|
|
562
|
+
},
|
|
563
|
+
},
|
|
564
|
+
{
|
|
565
|
+
$project: {
|
|
566
|
+
_id: 0,
|
|
567
|
+
groupName: '$_id',
|
|
568
|
+
isGroup: { $literal: true },
|
|
569
|
+
children: 1,
|
|
570
|
+
},
|
|
571
|
+
},
|
|
572
|
+
],
|
|
573
|
+
|
|
574
|
+
ungrouped: [
|
|
575
|
+
{ $match: { groupName: { $in: [ null, '' ] } } },
|
|
576
|
+
{
|
|
577
|
+
$addFields: { isGroup: { $literal: false } },
|
|
578
|
+
},
|
|
579
|
+
],
|
|
580
|
+
},
|
|
581
|
+
},
|
|
582
|
+
|
|
583
|
+
// Merge results
|
|
584
|
+
{
|
|
585
|
+
$project: {
|
|
586
|
+
result: { $concatArrays: [ '$ungrouped', '$grouped' ] },
|
|
587
|
+
},
|
|
588
|
+
},
|
|
589
|
+
|
|
590
|
+
{ $unwind: '$result' },
|
|
591
|
+
{ $replaceRoot: { newRoot: '$result' } },
|
|
592
|
+
] );
|
|
593
|
+
|
|
594
|
+
|
|
595
|
+
return res.sendSuccess( zonefinalTags );
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
|
|
599
|
+
if ( req.query.selectedProduct && req.query.selectedProduct === 'tangoTrax' ) {
|
|
600
|
+
let Query = [ {
|
|
601
|
+
$match: {
|
|
602
|
+
client_id: req.query.clientId,
|
|
603
|
+
publish: true,
|
|
604
|
+
checkListType: { $ne: 'custom' },
|
|
605
|
+
},
|
|
606
|
+
}, {
|
|
607
|
+
$group: {
|
|
608
|
+
_id: '$_id',
|
|
609
|
+
sourceCheckList_id: { $last: '$_id' },
|
|
610
|
+
tagName: { $last: '$checkListName' },
|
|
611
|
+
},
|
|
612
|
+
},
|
|
613
|
+
{
|
|
614
|
+
$lookup: {
|
|
615
|
+
from: 'cameras',
|
|
616
|
+
let: { checkListName: '$tagName' },
|
|
617
|
+
pipeline: [
|
|
618
|
+
{
|
|
619
|
+
$match: {
|
|
620
|
+
$expr: {
|
|
621
|
+
$anyElementTrue: {
|
|
622
|
+
$map: {
|
|
623
|
+
input: { $ifNull: [ '$taggedChecklist', [] ] }, // ✅ default to empty array
|
|
624
|
+
as: 'tc',
|
|
625
|
+
in: { $eq: [ '$$tc.checkListName', '$$checkListName' ] },
|
|
626
|
+
},
|
|
627
|
+
},
|
|
628
|
+
},
|
|
629
|
+
},
|
|
630
|
+
},
|
|
631
|
+
{
|
|
632
|
+
$project: {
|
|
633
|
+
storeId: 1,
|
|
634
|
+
},
|
|
635
|
+
},
|
|
636
|
+
], as: 'cameraList',
|
|
637
|
+
},
|
|
638
|
+
},
|
|
639
|
+
{
|
|
640
|
+
$project: {
|
|
641
|
+
tagName: 1,
|
|
642
|
+
type: 'checklist',
|
|
643
|
+
cameraList: {
|
|
644
|
+
$filter: {
|
|
645
|
+
input: '$cameraList',
|
|
646
|
+
as: 'item',
|
|
647
|
+
cond: {
|
|
648
|
+
$eq: [ '$$item.storeId', req.query.storeId ],
|
|
649
|
+
},
|
|
650
|
+
},
|
|
651
|
+
},
|
|
652
|
+
},
|
|
653
|
+
},
|
|
654
|
+
{
|
|
655
|
+
$project: {
|
|
656
|
+
tagName: 1,
|
|
657
|
+
type: 1,
|
|
658
|
+
count: { $size: '$cameraList' },
|
|
659
|
+
},
|
|
660
|
+
},
|
|
661
|
+
{
|
|
662
|
+
$sort: {
|
|
663
|
+
count: -1,
|
|
664
|
+
},
|
|
665
|
+
},
|
|
666
|
+
|
|
667
|
+
];
|
|
668
|
+
let getChecklistData = await checklistconfigService.aggregate( Query );
|
|
669
|
+
if ( finalTags && finalTags.length > 0 ) {
|
|
670
|
+
let merged = getChecklistData.map( ( item ) => {
|
|
671
|
+
let match = finalTags.find( ( a ) => a.tagName === item.tagName );
|
|
672
|
+
if ( match ) {
|
|
673
|
+
return { ...item, ...match, count: item.count + match.count }; // add counts
|
|
674
|
+
}
|
|
675
|
+
return item;
|
|
676
|
+
} );
|
|
677
|
+
// also include any arr1 items not in arr2
|
|
678
|
+
finalTags.forEach( ( a ) => {
|
|
679
|
+
if ( !merged.find( ( m ) => m.tagName === a.tagName ) ) {
|
|
680
|
+
merged.push( a );
|
|
681
|
+
}
|
|
682
|
+
} );
|
|
683
|
+
merged.sort( ( a, b ) => b.count - a.count );
|
|
684
|
+
return res.sendSuccess( merged );
|
|
685
|
+
} else {
|
|
686
|
+
return res.sendSuccess( getChecklistData );
|
|
687
|
+
}
|
|
688
|
+
} else {
|
|
689
|
+
return res.sendSuccess( finalTags );
|
|
690
|
+
}
|
|
691
|
+
} catch ( e ) {
|
|
692
|
+
logger.error( { error: e, function: 'customTagList' } );
|
|
693
|
+
return res.sendError( e, 500 );
|
|
694
|
+
}
|
|
695
|
+
};
|
|
160
696
|
|
|
161
697
|
export const tagging = async ( req, res ) => {
|
|
162
698
|
try {
|
|
@@ -182,8 +718,8 @@ export const tagging = async ( req, res ) => {
|
|
|
182
718
|
date: new Date(),
|
|
183
719
|
logType: 'zone',
|
|
184
720
|
logSubType: 'addZoneTagging',
|
|
185
|
-
changes: [ `${InputData.tagName} zone
|
|
186
|
-
eventType: '',
|
|
721
|
+
changes: [ `${InputData.tagName} zone tagging` ],
|
|
722
|
+
eventType: 'create',
|
|
187
723
|
showTo: [ 'client', 'tango' ],
|
|
188
724
|
};
|
|
189
725
|
insertOpenSearchData( JSON.parse( process.env.OPENSEARCH )?.activityLog, logObj );
|
|
@@ -206,12 +742,33 @@ export const tagging = async ( req, res ) => {
|
|
|
206
742
|
date: new Date(),
|
|
207
743
|
logType: 'zone',
|
|
208
744
|
logSubType: 'updateZoneTagging',
|
|
209
|
-
changes: [ `${InputData.tagName} zone
|
|
210
|
-
eventType: '',
|
|
745
|
+
changes: [ `${InputData.tagName} zone tagging` ],
|
|
746
|
+
eventType: 'update',
|
|
747
|
+
taggingId: taggingDetails._id,
|
|
748
|
+
previous: [
|
|
749
|
+
JSON.parse( JSON.stringify( taggingDetails ) ),
|
|
750
|
+
],
|
|
751
|
+
current: {
|
|
752
|
+
...taggingDetails.toObject(),
|
|
753
|
+
},
|
|
754
|
+
oldData: {
|
|
755
|
+
TagName: taggingDetails.tagName,
|
|
756
|
+
StreamName: taggingDetails.streamName,
|
|
757
|
+
},
|
|
758
|
+
newData: {
|
|
759
|
+
TagName: taggingDetails.tagName,
|
|
760
|
+
StreamName: taggingDetails.streamName,
|
|
761
|
+
},
|
|
762
|
+
showTo: [ 'tango', 'client' ],
|
|
211
763
|
};
|
|
764
|
+
|
|
765
|
+
delete logObj?.current?.coordinates;
|
|
766
|
+
|
|
212
767
|
insertOpenSearchData( JSON.parse( process.env.OPENSEARCH )?.activityLog, logObj );
|
|
213
768
|
taggingDetails.cameraId = InputData.cameraId;
|
|
214
769
|
taggingDetails.streamName = InputData.streamName;
|
|
770
|
+
taggingDetails.checkListName = InputData.checkListName;
|
|
771
|
+
taggingDetails.productName = InputData.productName;
|
|
215
772
|
// if ( req.body?.redoPoint ) {
|
|
216
773
|
// taggingDetails.coordinates = InputData.coordinates[0];
|
|
217
774
|
// taggingDetails.save();
|
|
@@ -244,7 +801,7 @@ export const tagging = async ( req, res ) => {
|
|
|
244
801
|
let externalDetails = await externalService.findOne( { storeId: InputData.storeId, zoneName: InputData.tagName, clientId: InputData.clientId } );
|
|
245
802
|
|
|
246
803
|
if ( !externalDetails ) {
|
|
247
|
-
let data ={
|
|
804
|
+
let data = {
|
|
248
805
|
'storeId': InputData.storeId,
|
|
249
806
|
'clientId': InputData.clientId,
|
|
250
807
|
'productName': 'tangoZone',
|
|
@@ -278,7 +835,7 @@ export const tagging = async ( req, res ) => {
|
|
|
278
835
|
'reidParameterType': 'default',
|
|
279
836
|
'reidParameterValue': [ 0.6, 1, 0.6 ],
|
|
280
837
|
'reidSuggesteParameter': false,
|
|
281
|
-
'reidSuggestedReduction':
|
|
838
|
+
'reidSuggestedReduction': true,
|
|
282
839
|
'reidThresholdHoliday': 0,
|
|
283
840
|
'reidThresholdWeekDay': 0,
|
|
284
841
|
'stage': '',
|
|
@@ -290,11 +847,12 @@ export const tagging = async ( req, res ) => {
|
|
|
290
847
|
'walkInTimeSpent': 3,
|
|
291
848
|
'isWalkInOneImg': false,
|
|
292
849
|
'isAudit': true,
|
|
293
|
-
'isForceCombine':
|
|
850
|
+
'isForceCombine': true,
|
|
294
851
|
};
|
|
295
852
|
|
|
296
853
|
await externalService.create( data );
|
|
297
854
|
}
|
|
855
|
+
|
|
298
856
|
await updatezoneTagging( req, res );
|
|
299
857
|
} catch ( e ) {
|
|
300
858
|
logger.error( { error: e, function: 'tagging' } );
|
|
@@ -308,11 +866,13 @@ export const getCameraList = async ( req, res ) => {
|
|
|
308
866
|
if ( !cameraDetails.length ) {
|
|
309
867
|
return res.sendError( 'no data found', 204 );
|
|
310
868
|
}
|
|
311
|
-
const folderPath = {
|
|
869
|
+
const folderPath = {
|
|
870
|
+
file_path: `${req.query.storeId}/zone_base_images/`,
|
|
312
871
|
Bucket: JSON.parse( process.env.BUCKET ).zoneBaseImage, MaxKeys: 1000,
|
|
313
872
|
};
|
|
314
873
|
let fileList = await listFileByPath( folderPath );
|
|
315
|
-
const TaggedfolderPath = {
|
|
874
|
+
const TaggedfolderPath = {
|
|
875
|
+
file_path: `${req.query.storeId}/zone_tagged_image/`,
|
|
316
876
|
Bucket: JSON.parse( process.env.BUCKET ).zoneTaggingImage, MaxKeys: 1000,
|
|
317
877
|
};
|
|
318
878
|
let tagFileList = await listFileByPath( TaggedfolderPath );
|
|
@@ -338,7 +898,7 @@ export const getCameraList = async ( req, res ) => {
|
|
|
338
898
|
tagFileList.data.forEach( ( item ) => {
|
|
339
899
|
if ( item.Key.length > 1 ) {
|
|
340
900
|
let splitStream = item.Key.split( '/' );
|
|
341
|
-
let getStream = splitStream[splitStream.length -1].split( '.' );
|
|
901
|
+
let getStream = splitStream[splitStream.length - 1].split( '.' );
|
|
342
902
|
|
|
343
903
|
if ( getStream && getStream[0] == `${req.query.storeId}_${camera.streamName}` ) {
|
|
344
904
|
tagPath = item.Key;
|
|
@@ -349,21 +909,23 @@ export const getCameraList = async ( req, res ) => {
|
|
|
349
909
|
if ( fileList?.data.length ) {
|
|
350
910
|
fileList.data.forEach( ( ele ) => {
|
|
351
911
|
let splitStream = ele.Key.split( '/' );
|
|
352
|
-
let getStream = splitStream[splitStream.length -1].split( '.' );
|
|
912
|
+
let getStream = splitStream[splitStream.length - 1].split( '.' );
|
|
353
913
|
if ( getStream && getStream[0] == `${req.query.storeId}_${camera.streamName}` ) {
|
|
354
914
|
imgPath = ele.Key;
|
|
355
915
|
}
|
|
356
916
|
} );
|
|
357
917
|
}
|
|
358
918
|
if ( tagPath ) {
|
|
359
|
-
const params = {
|
|
919
|
+
const params = {
|
|
920
|
+
file_path: tagPath,
|
|
360
921
|
Bucket: JSON.parse( process.env.BUCKET ).zoneTaggingImage,
|
|
361
922
|
};
|
|
362
923
|
const cameraTagImage = await signedUrl( params );
|
|
363
924
|
camera.tagImg = cameraTagImage;
|
|
364
925
|
}
|
|
365
926
|
if ( imgPath ) {
|
|
366
|
-
const baseParams = {
|
|
927
|
+
const baseParams = {
|
|
928
|
+
file_path: imgPath,
|
|
367
929
|
Bucket: JSON.parse( process.env.BUCKET ).zoneBaseImage,
|
|
368
930
|
};
|
|
369
931
|
const cameraBaseImage = await signedUrl( baseParams );
|
|
@@ -379,6 +941,87 @@ export const getCameraList = async ( req, res ) => {
|
|
|
379
941
|
return res.sendError( e, 500 );
|
|
380
942
|
}
|
|
381
943
|
};
|
|
944
|
+
export const getCameraListv2 = async ( req, res ) => {
|
|
945
|
+
try {
|
|
946
|
+
let cameraDetails = await cameraService.find( { clientId: req.body.clientId, storeId: req.body.storeId, isActivated: true, isUp: true }, { cameraNumber: 1, streamName: 1, isActivated: 1, isUp: 1, cameraName: 1, taggedChecklist: 1 } );
|
|
947
|
+
if ( !cameraDetails.length ) {
|
|
948
|
+
return res.sendError( 'no data found', 204 );
|
|
949
|
+
}
|
|
950
|
+
const folderPath = {
|
|
951
|
+
file_path: `${req.body.storeId}/zone_base_images/`,
|
|
952
|
+
Bucket: JSON.parse( process.env.BUCKET ).zoneBaseImage, MaxKeys: 1000,
|
|
953
|
+
};
|
|
954
|
+
let fileList = await listFileByPath( folderPath );
|
|
955
|
+
const TaggedfolderPath = {
|
|
956
|
+
file_path: `${req.body.storeId}/zone_tagged_image/`,
|
|
957
|
+
Bucket: JSON.parse( process.env.BUCKET ).zoneTaggingImage, MaxKeys: 1000,
|
|
958
|
+
};
|
|
959
|
+
let tagFileList = await listFileByPath( TaggedfolderPath );
|
|
960
|
+
for ( let [ index, camera ] of cameraDetails.entries() ) {
|
|
961
|
+
let tagList = [];
|
|
962
|
+
let tagPath;
|
|
963
|
+
let imgPath;
|
|
964
|
+
camera = {
|
|
965
|
+
...camera._doc,
|
|
966
|
+
baseImg: '',
|
|
967
|
+
tagImg: '',
|
|
968
|
+
};
|
|
969
|
+
let taggingDetails = await taggingService.find( { cameraId: camera._id, streamName: camera.streamName, clientId: req.body.clientId, isDeleted: false }, { tagName: 1, coordinates: 1 } );
|
|
970
|
+
if ( taggingDetails.length ) {
|
|
971
|
+
tagList = taggingDetails.filter( ( item ) => item.coordinates.length ).map( ( item ) => {
|
|
972
|
+
if ( item.coordinates.length ) {
|
|
973
|
+
return { tagName: item.tagName, color: item.coordinates[0].color };
|
|
974
|
+
}
|
|
975
|
+
} );
|
|
976
|
+
}
|
|
977
|
+
if ( tagFileList.data.length ) {
|
|
978
|
+
tagFileList.data.forEach( ( item ) => {
|
|
979
|
+
if ( item.Key.length > 1 ) {
|
|
980
|
+
let splitStream = item.Key.split( '/' );
|
|
981
|
+
let getStream = splitStream[splitStream.length - 1].split( '.' );
|
|
982
|
+
|
|
983
|
+
if ( getStream && getStream[0] == `${req.body.storeId}_${camera.streamName}` ) {
|
|
984
|
+
tagPath = item.Key;
|
|
985
|
+
}
|
|
986
|
+
}
|
|
987
|
+
} );
|
|
988
|
+
}
|
|
989
|
+
if ( fileList?.data.length ) {
|
|
990
|
+
fileList.data.forEach( ( ele ) => {
|
|
991
|
+
let splitStream = ele.Key.split( '/' );
|
|
992
|
+
let getStream = splitStream[splitStream.length - 1].split( '.' );
|
|
993
|
+
if ( getStream && getStream[0] == `${req.body.storeId}_${camera.streamName}` ) {
|
|
994
|
+
imgPath = ele.Key;
|
|
995
|
+
}
|
|
996
|
+
} );
|
|
997
|
+
}
|
|
998
|
+
if ( tagPath ) {
|
|
999
|
+
const params = {
|
|
1000
|
+
file_path: tagPath,
|
|
1001
|
+
Bucket: JSON.parse( process.env.BUCKET ).zoneTaggingImage,
|
|
1002
|
+
};
|
|
1003
|
+
const cameraTagImage = await signedUrl( params );
|
|
1004
|
+
camera.tagImg = cameraTagImage;
|
|
1005
|
+
}
|
|
1006
|
+
if ( imgPath ) {
|
|
1007
|
+
const baseParams = {
|
|
1008
|
+
file_path: imgPath,
|
|
1009
|
+
Bucket: JSON.parse( process.env.BUCKET ).zoneBaseImage,
|
|
1010
|
+
};
|
|
1011
|
+
const cameraBaseImage = await signedUrl( baseParams );
|
|
1012
|
+
camera.baseImg = cameraBaseImage;
|
|
1013
|
+
}
|
|
1014
|
+
camera.tagging = tagList;
|
|
1015
|
+
camera.taggedCount = tagList.filter( ( ele ) => ele.tagName === req.body.tagName );
|
|
1016
|
+
cameraDetails[index] = camera;
|
|
1017
|
+
}
|
|
1018
|
+
cameraDetails.sort( ( a, b ) => a.tagging?.length > b.tagging?.length ? -1 : 1 );
|
|
1019
|
+
return res.sendSuccess( cameraDetails );
|
|
1020
|
+
} catch ( e ) {
|
|
1021
|
+
logger.error( { error: e, function: 'getCameraListv2' } );
|
|
1022
|
+
return res.sendError( e, 500 );
|
|
1023
|
+
}
|
|
1024
|
+
};
|
|
382
1025
|
|
|
383
1026
|
export const updateTag = async ( req, res ) => {
|
|
384
1027
|
try {
|
|
@@ -416,9 +1059,21 @@ export const updateTag = async ( req, res ) => {
|
|
|
416
1059
|
date: new Date(),
|
|
417
1060
|
logType: 'zone',
|
|
418
1061
|
logSubType: 'updateCustomTag',
|
|
419
|
-
changes: [ `${req.body.tagName} tagName
|
|
420
|
-
eventType: '',
|
|
421
|
-
|
|
1062
|
+
changes: [ `${req.body.tagName} tagName` ],
|
|
1063
|
+
eventType: 'update',
|
|
1064
|
+
previous: {
|
|
1065
|
+
tagName: req.body.existTag,
|
|
1066
|
+
},
|
|
1067
|
+
current: {
|
|
1068
|
+
tagName: req.body.tagName,
|
|
1069
|
+
},
|
|
1070
|
+
oldData: {
|
|
1071
|
+
TagName: req.body.existTag,
|
|
1072
|
+
},
|
|
1073
|
+
newData: {
|
|
1074
|
+
TagName: req.body.tagName,
|
|
1075
|
+
},
|
|
1076
|
+
showTo: [ 'tango', 'client' ],
|
|
422
1077
|
};
|
|
423
1078
|
insertOpenSearchData( JSON.parse( process.env.OPENSEARCH )?.activityLog, logObj );
|
|
424
1079
|
if ( tagUpdate.modifiedCount || tagUpdate.matchedCount ) {
|
|
@@ -442,19 +1097,24 @@ export const deleteTag = async ( req, res ) => {
|
|
|
442
1097
|
if ( !taggingDetails.length ) {
|
|
443
1098
|
return res.sendError( 'no data found', 204 );
|
|
444
1099
|
}
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
if ( permission.featureName == 'analytics' ) {
|
|
450
|
-
let product = permission.product.find( ( item ) => item.name == 'tangoZone' );
|
|
451
|
-
if ( product ) {
|
|
452
|
-
enableDelete = product.isDelete;
|
|
453
|
-
}
|
|
454
|
-
}
|
|
455
|
-
} );
|
|
1100
|
+
|
|
1101
|
+
let tagNameDetails = await taggingService.findOne( { clientId: req.body.clientId, tagName: req.body.tagName, $expr: { $ne: [ { $size: '$coordinates' }, 0 ] } } );
|
|
1102
|
+
if ( tagNameDetails ) {
|
|
1103
|
+
return res.sendError( 'This zone tag is already mapped to other stores.', 400 );
|
|
456
1104
|
}
|
|
457
|
-
|
|
1105
|
+
// let enableDelete = false;
|
|
1106
|
+
// if ( req.user.permission ) {
|
|
1107
|
+
// let permissions = req.user.permission;
|
|
1108
|
+
// permissions.forEach( ( permission ) => {
|
|
1109
|
+
// if ( permission.featureName == 'analytics' ) {
|
|
1110
|
+
// let product = permission.product.find( ( item ) => item.name == 'tangoZone' );
|
|
1111
|
+
// if ( product ) {
|
|
1112
|
+
// enableDelete = product.isDelete;
|
|
1113
|
+
// }
|
|
1114
|
+
// }
|
|
1115
|
+
// } );
|
|
1116
|
+
// }
|
|
1117
|
+
if ( req.user?.role == 'superadmin' || ( req.user?.role == 'admin' ) ) {
|
|
458
1118
|
await taggingService.deleteMany( { clientId: req.body.clientId, tagName: req.body.tagName } );
|
|
459
1119
|
await externalService.deleteMany( { zoneName: req.body.tagName, clientId: req.body.clientId } );
|
|
460
1120
|
} else {
|
|
@@ -484,9 +1144,9 @@ export const deleteTag = async ( req, res ) => {
|
|
|
484
1144
|
email: req.user?.email,
|
|
485
1145
|
date: new Date(),
|
|
486
1146
|
logType: 'zone',
|
|
487
|
-
logSubType: '
|
|
488
|
-
changes: [ `${req.body.tagName} tag
|
|
489
|
-
eventType: '',
|
|
1147
|
+
logSubType: 'deleteCustomTag',
|
|
1148
|
+
changes: [ `${req.body.tagName} tag` ],
|
|
1149
|
+
eventType: 'delete',
|
|
490
1150
|
showTo: [ 'client', 'tango' ],
|
|
491
1151
|
};
|
|
492
1152
|
insertOpenSearchData( JSON.parse( process.env.OPENSEARCH )?.activityLog, logObj );
|
|
@@ -642,36 +1302,65 @@ export const updatezoneTagging = async ( req, res ) => {
|
|
|
642
1302
|
|
|
643
1303
|
export const updateOldData = async ( req, res ) => {
|
|
644
1304
|
try {
|
|
645
|
-
let tagDetails = await taggingService.find( {}, { storeId: 1 } );
|
|
1305
|
+
let tagDetails = await taggingService.find( { clientId: req.body.clientId, tagName: req.body.tagName }, { storeId: 1 } );
|
|
1306
|
+
|
|
1307
|
+
|
|
646
1308
|
if ( tagDetails.length ) {
|
|
647
1309
|
for ( let [ index, item ] of tagDetails.entries() ) {
|
|
648
1310
|
req.body.storeId = item.storeId;
|
|
649
1311
|
let camDetails = await getCamTaggingDetails( req, res );
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
1312
|
+
console.log( '🚀 ~ updateOldData ~ camDetails:', camDetails );
|
|
1313
|
+
if ( camDetails ) {
|
|
1314
|
+
const response = await axios.post( JSON.parse( process.env.URL ).zoneTaggingLamdaUrl, camDetails );
|
|
1315
|
+
|
|
1316
|
+
if ( response?.data?.status && response?.data?.status == 'success' ) {
|
|
1317
|
+
let fileData = {
|
|
1318
|
+
Key: `${req.body.storeId}/zonetagging/`,
|
|
1319
|
+
fileName: `${req.body.storeId}_zonetagging.json`,
|
|
1320
|
+
Bucket: JSON.parse( process.env.BUCKET ).zoneTaggingImage,
|
|
1321
|
+
body: JSON.stringify( camDetails ),
|
|
1322
|
+
ContentType: 'application/json',
|
|
1323
|
+
};
|
|
1324
|
+
|
|
1325
|
+
let upload = await fileUpload( fileData );
|
|
1326
|
+
if ( !upload.Key ) {
|
|
1327
|
+
logger.error( { message: `JSON Upload Error`, store: item.storeId } );
|
|
1328
|
+
}
|
|
1329
|
+
} else {
|
|
1330
|
+
logger.error( { message: 'no data', store: item.storeId } );
|
|
1331
|
+
return res.sendError( 'something went wrong', 500 );
|
|
668
1332
|
}
|
|
669
|
-
}
|
|
670
|
-
|
|
671
|
-
|
|
1333
|
+
}
|
|
1334
|
+
if ( index == tagDetails.length-1 ) {
|
|
1335
|
+
res.sendSuccess( 'Zone Updated Successfully' );
|
|
672
1336
|
}
|
|
673
1337
|
}
|
|
1338
|
+
} else {
|
|
1339
|
+
res.sendSuccess( 'Zone Updated Successfully' );
|
|
674
1340
|
}
|
|
1341
|
+
} catch ( e ) {
|
|
1342
|
+
console.log( '🚀 ~ updateOldData ~ e:', e );
|
|
1343
|
+
logger.error( { error: e, function: 'updateOldData' } );
|
|
1344
|
+
return res.sendError( e, 500 );
|
|
1345
|
+
}
|
|
1346
|
+
};
|
|
1347
|
+
export async function updateCamera( req, res ) {
|
|
1348
|
+
try {
|
|
1349
|
+
let findoneCheckList = await checklistconfigService.findOne( {
|
|
1350
|
+
client_id: req.body.clientId, checkListName: req.body.selectedZone,
|
|
1351
|
+
} );
|
|
1352
|
+
|
|
1353
|
+
let updateData = {
|
|
1354
|
+
checkListName: req.body.selectedZone,
|
|
1355
|
+
sourceCheckList_id: findoneCheckList?.sourceCheckList_id,
|
|
1356
|
+
};
|
|
1357
|
+
if ( req.body.type === 'tagCamera' ) {
|
|
1358
|
+
await cameraService.updateOne( { _id: req.body._id }, { $push: { taggedChecklist: updateData } } );
|
|
1359
|
+
} else {
|
|
1360
|
+
await cameraService.updateOne( { _id: req.body._id }, { $pull: { taggedChecklist: updateData } } );
|
|
1361
|
+
}
|
|
1362
|
+
|
|
1363
|
+
res.sendSuccess( 'updated Sucessfully' );
|
|
675
1364
|
} catch ( e ) {
|
|
676
1365
|
logger.error( { error: e, function: 'updateOldData' } );
|
|
677
1366
|
return res.sendError( e, 500 );
|
|
@@ -715,3 +1404,1344 @@ async function updateJsonFile( req, res ) {
|
|
|
715
1404
|
}
|
|
716
1405
|
}
|
|
717
1406
|
};
|
|
1407
|
+
|
|
1408
|
+
export const getCameraStreamList = async ( req, res ) => {
|
|
1409
|
+
try {
|
|
1410
|
+
let cameraDetails = await cameraService.find( { clientId: req.body.clientId, storeId: req.body.storeId, streamName: req.body.streamName }, { cameraNumber: 1, streamName: 1, isActivated: 1, isUp: 1, cameraName: 1 } );
|
|
1411
|
+
if ( !cameraDetails.length ) {
|
|
1412
|
+
return res.sendError( 'no data found', 204 );
|
|
1413
|
+
}
|
|
1414
|
+
const folderPath = {
|
|
1415
|
+
file_path: `${req.body.storeId}/zone_base_images/`,
|
|
1416
|
+
Bucket: JSON.parse( process.env.BUCKET ).zoneBaseImage, MaxKeys: 1000,
|
|
1417
|
+
};
|
|
1418
|
+
let fileList = await listFileByPath( folderPath );
|
|
1419
|
+
const TaggedfolderPath = {
|
|
1420
|
+
file_path: `${req.body.storeId}/zone_tagged_image/`,
|
|
1421
|
+
Bucket: JSON.parse( process.env.BUCKET ).zoneTaggingImage, MaxKeys: 1000,
|
|
1422
|
+
};
|
|
1423
|
+
let tagFileList = await listFileByPath( TaggedfolderPath );
|
|
1424
|
+
for ( let [ index, camera ] of cameraDetails.entries() ) {
|
|
1425
|
+
let tagList = [];
|
|
1426
|
+
let tagPath;
|
|
1427
|
+
let imgPath;
|
|
1428
|
+
camera = {
|
|
1429
|
+
...camera._doc,
|
|
1430
|
+
baseImg: '',
|
|
1431
|
+
tagImg: '',
|
|
1432
|
+
};
|
|
1433
|
+
let taggingDetails = await taggingService.find( { cameraId: camera._id, streamName: camera.streamName, clientId: req.body.clientId, isDeleted: false }, { tagName: 1, coordinates: 1 } );
|
|
1434
|
+
if ( taggingDetails.length ) {
|
|
1435
|
+
tagList = taggingDetails.filter( ( item ) => item.coordinates.length ).map( ( item ) => {
|
|
1436
|
+
if ( item.coordinates.length ) {
|
|
1437
|
+
return { tagName: item.tagName, color: item.coordinates[0].color };
|
|
1438
|
+
}
|
|
1439
|
+
} );
|
|
1440
|
+
}
|
|
1441
|
+
|
|
1442
|
+
if ( tagFileList.data.length ) {
|
|
1443
|
+
tagFileList.data.forEach( ( item ) => {
|
|
1444
|
+
if ( item.Key.length > 1 ) {
|
|
1445
|
+
let splitStream = item.Key.split( '/' );
|
|
1446
|
+
let getStream = splitStream[splitStream.length - 1].split( '.' );
|
|
1447
|
+
|
|
1448
|
+
if ( getStream && getStream[0] == `${req.body.storeId}_${camera.streamName}` ) {
|
|
1449
|
+
tagPath = item.Key;
|
|
1450
|
+
}
|
|
1451
|
+
}
|
|
1452
|
+
} );
|
|
1453
|
+
}
|
|
1454
|
+
if ( fileList?.data.length ) {
|
|
1455
|
+
fileList.data.forEach( ( ele ) => {
|
|
1456
|
+
let splitStream = ele.Key.split( '/' );
|
|
1457
|
+
let getStream = splitStream[splitStream.length - 1].split( '.' );
|
|
1458
|
+
if ( getStream && getStream[0] == `${req.body.storeId}_${camera.streamName}` ) {
|
|
1459
|
+
imgPath = ele.Key;
|
|
1460
|
+
}
|
|
1461
|
+
} );
|
|
1462
|
+
}
|
|
1463
|
+
if ( tagPath ) {
|
|
1464
|
+
const params = {
|
|
1465
|
+
file_path: tagPath,
|
|
1466
|
+
Bucket: JSON.parse( process.env.BUCKET ).zoneTaggingImage,
|
|
1467
|
+
};
|
|
1468
|
+
const cameraTagImage = await signedUrl( params );
|
|
1469
|
+
camera.tagImg = cameraTagImage;
|
|
1470
|
+
}
|
|
1471
|
+
if ( imgPath ) {
|
|
1472
|
+
const baseParams = {
|
|
1473
|
+
file_path: imgPath,
|
|
1474
|
+
Bucket: JSON.parse( process.env.BUCKET ).zoneBaseImage,
|
|
1475
|
+
};
|
|
1476
|
+
const cameraBaseImage = await signedUrl( baseParams );
|
|
1477
|
+
camera.baseImg = cameraBaseImage;
|
|
1478
|
+
}
|
|
1479
|
+
camera.tagging = tagList;
|
|
1480
|
+
cameraDetails[index] = camera;
|
|
1481
|
+
}
|
|
1482
|
+
cameraDetails.sort( ( a, b ) => a.tagging?.length > b.tagging?.length ? -1 : 1 );
|
|
1483
|
+
return res.sendSuccess( cameraDetails );
|
|
1484
|
+
} catch ( e ) {
|
|
1485
|
+
logger.error( { error: e, function: 'getCameraList' } );
|
|
1486
|
+
return res.sendError( e, 500 );
|
|
1487
|
+
}
|
|
1488
|
+
};
|
|
1489
|
+
|
|
1490
|
+
|
|
1491
|
+
// setting config - zone - get tagging details
|
|
1492
|
+
export const getZoneTaggingDetails = async ( req, res ) => {
|
|
1493
|
+
try {
|
|
1494
|
+
const {
|
|
1495
|
+
clientId,
|
|
1496
|
+
searchValue,
|
|
1497
|
+
sortColumName,
|
|
1498
|
+
sortBy,
|
|
1499
|
+
limit,
|
|
1500
|
+
offset = 1,
|
|
1501
|
+
export: isExport,
|
|
1502
|
+
} = req.body;
|
|
1503
|
+
|
|
1504
|
+
if ( !clientId ) {
|
|
1505
|
+
return res.sendError( 'clientId is required', 400 );
|
|
1506
|
+
}
|
|
1507
|
+
|
|
1508
|
+
const matchStage = {
|
|
1509
|
+
clientId,
|
|
1510
|
+
};
|
|
1511
|
+
let customTags = await customZoneTagService.findOne( { clientId: clientId } );
|
|
1512
|
+
const alltotalGroupCount = await customzonegrouping.count( { clientId } );
|
|
1513
|
+
if ( !customTags ) {
|
|
1514
|
+
return res.sendSuccess( { result: [], totalGroupCount: alltotalGroupCount } );
|
|
1515
|
+
}
|
|
1516
|
+
const searchStage = searchValue ?
|
|
1517
|
+
{
|
|
1518
|
+
$or: [
|
|
1519
|
+
{ tagName: { $regex: searchValue, $options: 'i' } },
|
|
1520
|
+
{ groupName: { $regex: searchValue, $options: 'i' } },
|
|
1521
|
+
],
|
|
1522
|
+
} :
|
|
1523
|
+
null;
|
|
1524
|
+
|
|
1525
|
+
const pipeline = [
|
|
1526
|
+
{ $match: matchStage },
|
|
1527
|
+
{
|
|
1528
|
+
$lookup: {
|
|
1529
|
+
from: 'taggings',
|
|
1530
|
+
let: { tagName: '$tagName' },
|
|
1531
|
+
pipeline: [
|
|
1532
|
+
{
|
|
1533
|
+
$match: {
|
|
1534
|
+
$expr: {
|
|
1535
|
+
$and: [
|
|
1536
|
+
{ $eq: [ '$tagName', '$$tagName' ] },
|
|
1537
|
+
{ $eq: [ '$clientId', clientId ] },
|
|
1538
|
+
{ $ne: [ '$coordinates', [] ] },
|
|
1539
|
+
],
|
|
1540
|
+
},
|
|
1541
|
+
},
|
|
1542
|
+
},
|
|
1543
|
+
{
|
|
1544
|
+
$group: {
|
|
1545
|
+
_id: '$storeId',
|
|
1546
|
+
count: { $sum: 1 },
|
|
1547
|
+
},
|
|
1548
|
+
},
|
|
1549
|
+
],
|
|
1550
|
+
as: 'tagsCount',
|
|
1551
|
+
},
|
|
1552
|
+
},
|
|
1553
|
+
{
|
|
1554
|
+
$project: {
|
|
1555
|
+
clientId: 1,
|
|
1556
|
+
tagName: 1,
|
|
1557
|
+
groupName: 1,
|
|
1558
|
+
storesTaggedCount: { $size: '$tagsCount' },
|
|
1559
|
+
},
|
|
1560
|
+
},
|
|
1561
|
+
{
|
|
1562
|
+
$facet: {
|
|
1563
|
+
totalCount: [
|
|
1564
|
+
...( searchStage ? [ { $match: searchStage } ] : [] ),
|
|
1565
|
+
{ $count: 'count' },
|
|
1566
|
+
],
|
|
1567
|
+
|
|
1568
|
+
filteredData: [
|
|
1569
|
+
...( searchStage ? [ { $match: searchStage } ] : [] ),
|
|
1570
|
+
...( sortColumName && sortBy ?
|
|
1571
|
+
[ { $sort: { [sortColumName]: sortBy } } ] :
|
|
1572
|
+
[] ),
|
|
1573
|
+
...( isExport ?
|
|
1574
|
+
[] :
|
|
1575
|
+
limit ? [
|
|
1576
|
+
{ $skip: ( offset - 1 ) * limit },
|
|
1577
|
+
{ $limit: Number( limit ) },
|
|
1578
|
+
] : [] ),
|
|
1579
|
+
],
|
|
1580
|
+
},
|
|
1581
|
+
},
|
|
1582
|
+
];
|
|
1583
|
+
|
|
1584
|
+
const [ result ] = await customZoneTagService.aggregate( pipeline );
|
|
1585
|
+
|
|
1586
|
+
const totalCount = result.totalCount[0]?.count || 0;
|
|
1587
|
+
if ( totalCount === 0 ) {
|
|
1588
|
+
return res.sendError( 'No data', 204 );
|
|
1589
|
+
}
|
|
1590
|
+
const zoneList = result.filteredData;
|
|
1591
|
+
|
|
1592
|
+
// Get total count of groups for tabs (actual count, not filtered by search)
|
|
1593
|
+
const totalGroupCount = await customzonegrouping.count( { clientId } );
|
|
1594
|
+
|
|
1595
|
+
if ( isExport && zoneList.length ) {
|
|
1596
|
+
const exportdata = zoneList.map( ( z ) => ( {
|
|
1597
|
+
'Zone Name': z.tagName || '--',
|
|
1598
|
+
'Zone Groups': z.groupName || '--',
|
|
1599
|
+
'Stores tagged Count': z.storesTaggedCount || 0,
|
|
1600
|
+
} ) );
|
|
1601
|
+
await download( exportdata, res );
|
|
1602
|
+
return;
|
|
1603
|
+
}
|
|
1604
|
+
|
|
1605
|
+
return res.sendSuccess( {
|
|
1606
|
+
result: zoneList,
|
|
1607
|
+
totalCount,
|
|
1608
|
+
totalGroupCount,
|
|
1609
|
+
} );
|
|
1610
|
+
} catch ( e ) {
|
|
1611
|
+
logger.error( { error: e, function: 'getZoneTaggingDetails' } );
|
|
1612
|
+
return res.sendError( e, 500 );
|
|
1613
|
+
}
|
|
1614
|
+
// try {
|
|
1615
|
+
// const inputData = req.body;
|
|
1616
|
+
// let Query = [
|
|
1617
|
+
// {
|
|
1618
|
+
// $match: {
|
|
1619
|
+
// clientId: inputData.clientId,
|
|
1620
|
+
// },
|
|
1621
|
+
// },
|
|
1622
|
+
// {
|
|
1623
|
+
// $lookup: {
|
|
1624
|
+
// from: 'taggings',
|
|
1625
|
+
// let: { tagName: '$tagName' },
|
|
1626
|
+
// pipeline: [
|
|
1627
|
+
// {
|
|
1628
|
+
// $match: {
|
|
1629
|
+
// $expr: {
|
|
1630
|
+
// $and: [
|
|
1631
|
+
// {
|
|
1632
|
+
// $eq: [ '$tagName', '$$tagName' ],
|
|
1633
|
+
// },
|
|
1634
|
+
// {
|
|
1635
|
+
// $eq: [ '$clientId', inputData.clientId ],
|
|
1636
|
+
// },
|
|
1637
|
+
// ],
|
|
1638
|
+
// },
|
|
1639
|
+
// },
|
|
1640
|
+
// },
|
|
1641
|
+
// ], as: 'tagsCount',
|
|
1642
|
+
// },
|
|
1643
|
+
// },
|
|
1644
|
+
// {
|
|
1645
|
+
// $project: {
|
|
1646
|
+
// clientId: 1,
|
|
1647
|
+
// tagName: 1,
|
|
1648
|
+
// groupName: 1,
|
|
1649
|
+
// storesTaggedCount: { $size: '$tagsCount' },
|
|
1650
|
+
// },
|
|
1651
|
+
// },
|
|
1652
|
+
// ];
|
|
1653
|
+
// if ( req.body.searchValue && req.body.searchValue !== '' ) {
|
|
1654
|
+
// Query.push( {
|
|
1655
|
+
// $match: {
|
|
1656
|
+
// $or: [
|
|
1657
|
+
// { tagName: { $regex: req.body.searchValue, $options: 'i' } },
|
|
1658
|
+
// { groupName: { $regex: req.body.searchValue, $options: 'i' } },
|
|
1659
|
+
// ],
|
|
1660
|
+
// },
|
|
1661
|
+
// } );
|
|
1662
|
+
// }
|
|
1663
|
+
|
|
1664
|
+
// if ( req.body.sortColumName && req.body.sortColumName !== '' && req.body.sortBy ) {
|
|
1665
|
+
// Query.push( {
|
|
1666
|
+
// $sort: { [req.body.sortColumName]: req.body.sortBy },
|
|
1667
|
+
// } );
|
|
1668
|
+
// }
|
|
1669
|
+
|
|
1670
|
+
// const totalCount = await customZoneTagService.aggregate( Query );
|
|
1671
|
+
// if ( req.body.limit && req.body.offset && !req.body.export ) {
|
|
1672
|
+
// Query.push(
|
|
1673
|
+
// { $skip: ( req.body.offset - 1 ) * req.body.limit },
|
|
1674
|
+
// { $limit: Number( req.body.limit ) },
|
|
1675
|
+
// );
|
|
1676
|
+
// }
|
|
1677
|
+
// const zoneList = await customZoneTagService.aggregate( Query );
|
|
1678
|
+
|
|
1679
|
+
// if ( req.body.export && zoneList.length > 0 ) {
|
|
1680
|
+
// const exportdata = [];
|
|
1681
|
+
// zoneList.forEach( ( element ) => {
|
|
1682
|
+
// const data = {
|
|
1683
|
+
// 'Zone Name': element.tagName || '--',
|
|
1684
|
+
// 'Zone Groups': element.groupName || '--',
|
|
1685
|
+
// 'Stores tagged Count': element.storesTaggedCount || 0,
|
|
1686
|
+
// };
|
|
1687
|
+
// exportdata.push( data );
|
|
1688
|
+
// } );
|
|
1689
|
+
// await download( exportdata, res );
|
|
1690
|
+
// return;
|
|
1691
|
+
// }
|
|
1692
|
+
|
|
1693
|
+
// return res.sendSuccess( {
|
|
1694
|
+
// result: zoneList,
|
|
1695
|
+
// count: totalCount.length,
|
|
1696
|
+
// } );
|
|
1697
|
+
// } catch ( e ) {
|
|
1698
|
+
// logger.error( { error: e, function: 'getZoneTaggingDetails' } );
|
|
1699
|
+
// console.error( 'getZoneTaggingDetails error:', e );
|
|
1700
|
+
// return res.sendError( e, 500 );
|
|
1701
|
+
// }
|
|
1702
|
+
};
|
|
1703
|
+
|
|
1704
|
+
export const addZoneCustomTag = async ( req, res ) => {
|
|
1705
|
+
try {
|
|
1706
|
+
const {
|
|
1707
|
+
clientId,
|
|
1708
|
+
tagName,
|
|
1709
|
+
groupName,
|
|
1710
|
+
productName,
|
|
1711
|
+
isExistingGroup,
|
|
1712
|
+
rgbColor,
|
|
1713
|
+
rgbBorderColor,
|
|
1714
|
+
_id,
|
|
1715
|
+
} = req.body;
|
|
1716
|
+
|
|
1717
|
+
if ( !clientId || !tagName ) {
|
|
1718
|
+
return res.sendError( 'clientId and zoneName are required', 400 );
|
|
1719
|
+
}
|
|
1720
|
+
|
|
1721
|
+
// Escape regex special characters in tagName to avoid invalid regex patterns
|
|
1722
|
+
const escapedTagName = tagName.replace( /[.*+?^${}()|[\]\\]/g, '\\$&' );
|
|
1723
|
+
|
|
1724
|
+
// Case-insensitive check for existing tag
|
|
1725
|
+
const existingTag = await customZoneTagService.findOne(
|
|
1726
|
+
{ clientId, tagName: { $regex: `^${escapedTagName}$`, $options: 'i' }, _id: { $ne: _id } },
|
|
1727
|
+
);
|
|
1728
|
+
|
|
1729
|
+
if ( existingTag ) {
|
|
1730
|
+
return res.sendError( `zoneName "${tagName}" already exists for this client`, 409 );
|
|
1731
|
+
}
|
|
1732
|
+
|
|
1733
|
+
let groupDoc = null;
|
|
1734
|
+
if ( groupName ) {
|
|
1735
|
+
// Escape regex special characters in groupName to avoid invalid regex patterns
|
|
1736
|
+
const escapedGroupName = groupName.replace( /[.*+?^${}()|[\]\\]/g, '\\$&' );
|
|
1737
|
+
// Case-insensitive check for existing group
|
|
1738
|
+
groupDoc = await customzonegrouping.findOne( { clientId, groupName: { $regex: `^${escapedGroupName}$`, $options: 'i' } } );
|
|
1739
|
+
}
|
|
1740
|
+
|
|
1741
|
+
if ( !isExistingGroup && groupDoc ) {
|
|
1742
|
+
return res.sendError( `groupName "${groupName}" already exists for this client`, 409 );
|
|
1743
|
+
}
|
|
1744
|
+
|
|
1745
|
+
/** Create Zone Tag */
|
|
1746
|
+
await customZoneTagService.create(
|
|
1747
|
+
[ {
|
|
1748
|
+
clientId,
|
|
1749
|
+
tagName,
|
|
1750
|
+
rgbColor,
|
|
1751
|
+
rgbBorderColor,
|
|
1752
|
+
groupName: groupName || null,
|
|
1753
|
+
productName,
|
|
1754
|
+
} ],
|
|
1755
|
+
);
|
|
1756
|
+
|
|
1757
|
+
/** Create or Update Zone Group */
|
|
1758
|
+
if ( groupName ) {
|
|
1759
|
+
if ( !groupDoc && !isExistingGroup ) {
|
|
1760
|
+
await customzonegrouping.create(
|
|
1761
|
+
[ {
|
|
1762
|
+
clientId,
|
|
1763
|
+
groupName,
|
|
1764
|
+
productName,
|
|
1765
|
+
zonesTagged: [ tagName ],
|
|
1766
|
+
} ],
|
|
1767
|
+
);
|
|
1768
|
+
} else if ( groupDoc && isExistingGroup ) {
|
|
1769
|
+
await customzonegrouping.updateOne(
|
|
1770
|
+
{ _id: groupDoc._id },
|
|
1771
|
+
{ $addToSet: { zonesTagged: tagName } },
|
|
1772
|
+
);
|
|
1773
|
+
}
|
|
1774
|
+
}
|
|
1775
|
+
|
|
1776
|
+
await taggingService.updateMany( { clientId, tagName }, { $set: { groupName } } );
|
|
1777
|
+
|
|
1778
|
+
logger.info( 'Zone Tag Created Successfully' );
|
|
1779
|
+
const logObj = {
|
|
1780
|
+
clientId: clientId,
|
|
1781
|
+
userName: req.user?.userName,
|
|
1782
|
+
email: req.user?.email,
|
|
1783
|
+
date: new Date(),
|
|
1784
|
+
logType: 'zone',
|
|
1785
|
+
logSubType: 'addZoneCustomTag',
|
|
1786
|
+
changes: [ `${tagName} custom zone tag` ],
|
|
1787
|
+
eventType: 'create',
|
|
1788
|
+
showTo: [ 'client', 'tango' ],
|
|
1789
|
+
};
|
|
1790
|
+
insertOpenSearchData( JSON.parse( process.env.OPENSEARCH )?.activityLog, logObj );
|
|
1791
|
+
|
|
1792
|
+
return res.sendSuccess( 'Zone Tag Created Successfully' );
|
|
1793
|
+
} catch ( e ) {
|
|
1794
|
+
logger.error( { error: e, function: 'addZoneCustomTag' } );
|
|
1795
|
+
return res.sendError( e.message || e, 500 );
|
|
1796
|
+
}
|
|
1797
|
+
};
|
|
1798
|
+
|
|
1799
|
+
export const uploadBulkZoneTag = async ( req, res ) => {
|
|
1800
|
+
try {
|
|
1801
|
+
const zonesArray = req.body;
|
|
1802
|
+
const clientId = req.bulkZoneClientId;
|
|
1803
|
+
|
|
1804
|
+
if ( !zonesArray || !Array.isArray( zonesArray ) || zonesArray.length === 0 ) {
|
|
1805
|
+
return res.sendError( 'zone-config file must not be empty', 400 );
|
|
1806
|
+
}
|
|
1807
|
+
|
|
1808
|
+
if ( !clientId ) {
|
|
1809
|
+
return res.sendError( 'clientId is required', 400 );
|
|
1810
|
+
}
|
|
1811
|
+
|
|
1812
|
+
const zoneTagDataList = [];
|
|
1813
|
+
const zoneGroupDataMap = new Map();
|
|
1814
|
+
const createdTagNames = [];
|
|
1815
|
+
// Map to store user-provided groupName (any case) -> actual groupName from DB (preserving existing case)
|
|
1816
|
+
const groupNameMapping = new Map();
|
|
1817
|
+
|
|
1818
|
+
// First pass: Check for existing groups and create mapping
|
|
1819
|
+
const uniqueGroupNames = [ ...new Set( zonesArray.map( ( zone ) => zone.groupName ).filter( ( name ) => name && name !== '' && name !== null ) ) ];
|
|
1820
|
+
|
|
1821
|
+
for ( const userGroupName of uniqueGroupNames ) {
|
|
1822
|
+
// Escape regex special characters in group name to avoid invalid regex patterns
|
|
1823
|
+
const escapedUserGroupName = userGroupName.replace( /[.*+?^${}()|[\]\\]/g, '\\$&' );
|
|
1824
|
+
|
|
1825
|
+
// Case-insensitive check for existing group
|
|
1826
|
+
const existingGroupDoc = await customzonegrouping.findOne( {
|
|
1827
|
+
clientId,
|
|
1828
|
+
groupName: { $regex: `^${escapedUserGroupName}$`, $options: 'i' },
|
|
1829
|
+
} );
|
|
1830
|
+
|
|
1831
|
+
if ( existingGroupDoc ) {
|
|
1832
|
+
// Group exists - use the existing group's case
|
|
1833
|
+
groupNameMapping.set( userGroupName.toLowerCase(), existingGroupDoc.groupName );
|
|
1834
|
+
} else {
|
|
1835
|
+
// New group - use user-provided case
|
|
1836
|
+
groupNameMapping.set( userGroupName.toLowerCase(), userGroupName );
|
|
1837
|
+
}
|
|
1838
|
+
}
|
|
1839
|
+
|
|
1840
|
+
// Process each zone in the array
|
|
1841
|
+
for ( const zone of zonesArray ) {
|
|
1842
|
+
// Determine the correct groupName to use (existing case if group exists, user case if new)
|
|
1843
|
+
let normalizedGroupName = null;
|
|
1844
|
+
if ( zone.groupName && zone.groupName !== '' && zone.groupName !== null ) {
|
|
1845
|
+
normalizedGroupName = groupNameMapping.get( zone.groupName.toLowerCase() ) || zone.groupName;
|
|
1846
|
+
}
|
|
1847
|
+
|
|
1848
|
+
const zoneTagData = {
|
|
1849
|
+
clientId: clientId,
|
|
1850
|
+
tagName: zone.tagName,
|
|
1851
|
+
rgbColor: zone.rgbColor,
|
|
1852
|
+
rgbBorderColor: zone.rgbBorderColor,
|
|
1853
|
+
groupName: normalizedGroupName,
|
|
1854
|
+
productName: zone.productName,
|
|
1855
|
+
};
|
|
1856
|
+
zoneTagDataList.push( zoneTagData );
|
|
1857
|
+
createdTagNames.push( zone.tagName );
|
|
1858
|
+
|
|
1859
|
+
// Prepare zone group data if groupName is provided
|
|
1860
|
+
if ( normalizedGroupName ) {
|
|
1861
|
+
// Use normalized groupName (existing case or user case)
|
|
1862
|
+
if ( !zoneGroupDataMap.has( normalizedGroupName ) ) {
|
|
1863
|
+
zoneGroupDataMap.set( normalizedGroupName, {
|
|
1864
|
+
clientId: clientId,
|
|
1865
|
+
groupName: normalizedGroupName,
|
|
1866
|
+
productName: zone.productName,
|
|
1867
|
+
zonesTagged: new Set(),
|
|
1868
|
+
} );
|
|
1869
|
+
}
|
|
1870
|
+
|
|
1871
|
+
// Collect all tagNames for this group so that we can append them
|
|
1872
|
+
const groupEntry = zoneGroupDataMap.get( normalizedGroupName );
|
|
1873
|
+
groupEntry.zonesTagged.add( zone.tagName );
|
|
1874
|
+
}
|
|
1875
|
+
}
|
|
1876
|
+
|
|
1877
|
+
// Create all zone tags
|
|
1878
|
+
await customZoneTagService.create( zoneTagDataList );
|
|
1879
|
+
|
|
1880
|
+
// Create zone groups (convert Map values to array)
|
|
1881
|
+
// const zoneGroupDataList = Array.from( zoneGroupDataMap.values() );
|
|
1882
|
+
// console.log( 'zoneGroupDataList', zoneGroupDataList );
|
|
1883
|
+
// if ( zoneGroupDataList.length > 0 ) {
|
|
1884
|
+
// for ( const groupData of zoneGroupDataList ) {
|
|
1885
|
+
// await customzonegrouping.create( groupData );
|
|
1886
|
+
// }
|
|
1887
|
+
// }
|
|
1888
|
+
|
|
1889
|
+
// Create or Update Zone Groups (following addZoneCustomTag pattern)
|
|
1890
|
+
const zoneGroupDataList = Array.from( zoneGroupDataMap.values() ).map( ( groupData ) => ( {
|
|
1891
|
+
clientId: groupData.clientId,
|
|
1892
|
+
groupName: groupData.groupName,
|
|
1893
|
+
productName: groupData.productName,
|
|
1894
|
+
zonesTagged: Array.from( groupData.zonesTagged || [] ),
|
|
1895
|
+
} ) );
|
|
1896
|
+
|
|
1897
|
+
if ( zoneGroupDataList.length > 0 ) {
|
|
1898
|
+
for ( const groupData of zoneGroupDataList ) {
|
|
1899
|
+
// Case-insensitive check for existing group (same as addZoneCustomTag)
|
|
1900
|
+
const escapedGroupName = groupData.groupName.replace( /[.*+?^${}()|[\]\\]/g, '\\$&' );
|
|
1901
|
+
const groupDoc = await customzonegrouping.findOne( {
|
|
1902
|
+
clientId,
|
|
1903
|
+
groupName: { $regex: `^${escapedGroupName}$`, $options: 'i' },
|
|
1904
|
+
} );
|
|
1905
|
+
|
|
1906
|
+
if ( !groupDoc ) {
|
|
1907
|
+
// Create new group with all zones from the bulk file
|
|
1908
|
+
await customzonegrouping.create( [ {
|
|
1909
|
+
clientId: groupData.clientId,
|
|
1910
|
+
groupName: groupData.groupName,
|
|
1911
|
+
productName: groupData.productName,
|
|
1912
|
+
zonesTagged: groupData.zonesTagged,
|
|
1913
|
+
} ] );
|
|
1914
|
+
} else {
|
|
1915
|
+
// Group exists - add new zones to existing zonesTagged array (preserving existing mappings)
|
|
1916
|
+
await customzonegrouping.updateOne(
|
|
1917
|
+
{ _id: groupDoc._id },
|
|
1918
|
+
{ $addToSet: { zonesTagged: { $each: groupData.zonesTagged } } },
|
|
1919
|
+
);
|
|
1920
|
+
}
|
|
1921
|
+
}
|
|
1922
|
+
}
|
|
1923
|
+
|
|
1924
|
+
// Ensure tagging documents are also mapped to the correct groupName for newly uploaded zones
|
|
1925
|
+
// Use normalized groupName (existing case if group exists, user case if new)
|
|
1926
|
+
for ( const zone of zonesArray ) {
|
|
1927
|
+
if ( zone.groupName && zone.groupName !== '' && zone.groupName !== null ) {
|
|
1928
|
+
const normalizedGroupName = groupNameMapping.get( zone.groupName.toLowerCase() ) || zone.groupName;
|
|
1929
|
+
await taggingService.updateMany(
|
|
1930
|
+
{ clientId, tagName: zone.tagName },
|
|
1931
|
+
{ $set: { groupName: normalizedGroupName } },
|
|
1932
|
+
);
|
|
1933
|
+
}
|
|
1934
|
+
}
|
|
1935
|
+
|
|
1936
|
+
|
|
1937
|
+
logger.info( `Bulk Zone Tags Created Successfully: ${createdTagNames.length} tags` );
|
|
1938
|
+
const logObj = {
|
|
1939
|
+
clientId: clientId,
|
|
1940
|
+
userName: req.user?.userName,
|
|
1941
|
+
email: req.user?.email,
|
|
1942
|
+
date: new Date(),
|
|
1943
|
+
logType: 'zone',
|
|
1944
|
+
logSubType: 'addBulkZoneCustomTag',
|
|
1945
|
+
changes: createdTagNames.map( ( tagName ) => `${tagName} custom tag` ),
|
|
1946
|
+
eventType: 'create',
|
|
1947
|
+
showTo: [ 'client', 'tango' ],
|
|
1948
|
+
};
|
|
1949
|
+
insertOpenSearchData( JSON.parse( process.env.OPENSEARCH )?.activityLog, logObj );
|
|
1950
|
+
return res.sendSuccess( {
|
|
1951
|
+
message: `Zone Tags Created Successfully`,
|
|
1952
|
+
count: createdTagNames.length,
|
|
1953
|
+
} );
|
|
1954
|
+
} catch ( e ) {
|
|
1955
|
+
console.log( 'uploadBulkZoneTags error :', e );
|
|
1956
|
+
logger.error( { error: e, function: 'addBulkZoneCustomTag' } );
|
|
1957
|
+
return res.sendError( 'Failed to upload zone-config file', 500 );
|
|
1958
|
+
}
|
|
1959
|
+
};
|
|
1960
|
+
|
|
1961
|
+
export const updateZoneCustomTag = async ( req, res ) => {
|
|
1962
|
+
try {
|
|
1963
|
+
const { clientId, tagName, oldTag, isExistingGroup, oldGroupName, groupName } = req.body;
|
|
1964
|
+
|
|
1965
|
+
if ( !clientId || !tagName ) {
|
|
1966
|
+
return res.sendError( 'clientId, zoneName are required', 400 );
|
|
1967
|
+
}
|
|
1968
|
+
|
|
1969
|
+
let findQuery = {};
|
|
1970
|
+
|
|
1971
|
+
// Escape regex special characters in tag names to avoid invalid regex patterns
|
|
1972
|
+
const escapedTagName = tagName.replace( /[.*+?^${}()|[\]\\]/g, '\\$&' );
|
|
1973
|
+
const escapedOldTag = oldTag ? oldTag.replace( /[.*+?^${}()|[\]\\]/g, '\\$&' ) : null;
|
|
1974
|
+
if ( oldTag !== undefined && oldTag !== null && tagName !== oldTag ) {
|
|
1975
|
+
findQuery = {
|
|
1976
|
+
clientId,
|
|
1977
|
+
tagName: { $regex: `^${escapedOldTag}$`, $options: 'i' },
|
|
1978
|
+
};
|
|
1979
|
+
} else {
|
|
1980
|
+
findQuery = {
|
|
1981
|
+
clientId,
|
|
1982
|
+
tagName: { $regex: `^${escapedTagName}$`, $options: 'i' },
|
|
1983
|
+
};
|
|
1984
|
+
}
|
|
1985
|
+
await externalService.updateMany( { zoneName: oldTag, clientId: req.body.clientId }, { zoneName: req.body.tagName } );
|
|
1986
|
+
updateOldData( req, res );
|
|
1987
|
+
let customZoneTagDetails = await customZoneTagService.findOne( findQuery );
|
|
1988
|
+
|
|
1989
|
+
if ( !customZoneTagDetails ) {
|
|
1990
|
+
return res.sendError( 'No data found', 400 );
|
|
1991
|
+
}
|
|
1992
|
+
|
|
1993
|
+
// Get the actual oldGroupName from the database record
|
|
1994
|
+
const actualOldGroupName = customZoneTagDetails.groupName || null;
|
|
1995
|
+
// Get the actual tag name currently in the database
|
|
1996
|
+
const actualTagNameInDb = customZoneTagDetails.tagName;
|
|
1997
|
+
|
|
1998
|
+
// Validate that the new tagName does not already exist for this client (case-insensitive)
|
|
1999
|
+
// Check if tagName is being changed (either via oldTag or if it's different from DB value)
|
|
2000
|
+
const isTagNameChanging = ( oldTag && tagName.toLowerCase() !== oldTag.toLowerCase() ) || ( actualTagNameInDb && tagName.toLowerCase() !== actualTagNameInDb.toLowerCase() );
|
|
2001
|
+
|
|
2002
|
+
if ( isTagNameChanging ) {
|
|
2003
|
+
const existingTagWithNewName = await customZoneTagService.findOne( {
|
|
2004
|
+
clientId,
|
|
2005
|
+
tagName: { $regex: `^${escapedTagName}$`, $options: 'i' },
|
|
2006
|
+
} );
|
|
2007
|
+
|
|
2008
|
+
// If a tag with the new name exists and it's not the same record we're updating, return error
|
|
2009
|
+
if ( existingTagWithNewName && existingTagWithNewName._id.toString() !== customZoneTagDetails._id.toString() ) {
|
|
2010
|
+
return res.sendError( `zoneName "${tagName}" already exists for this client`, 409 );
|
|
2011
|
+
}
|
|
2012
|
+
}
|
|
2013
|
+
|
|
2014
|
+
let query = {
|
|
2015
|
+
clientId,
|
|
2016
|
+
};
|
|
2017
|
+
let payload = { tagName };
|
|
2018
|
+
if ( oldTag && tagName !== oldTag ) {
|
|
2019
|
+
query.tagName = oldTag;
|
|
2020
|
+
} else {
|
|
2021
|
+
query.tagName = tagName;
|
|
2022
|
+
}
|
|
2023
|
+
if ( groupName ) {
|
|
2024
|
+
payload.groupName = groupName || null;
|
|
2025
|
+
} else {
|
|
2026
|
+
payload.groupName = null;
|
|
2027
|
+
}
|
|
2028
|
+
|
|
2029
|
+
// Determine the tag name to use for group operations
|
|
2030
|
+
// Use the actual tag name from database for removing from old group
|
|
2031
|
+
const tagNameForRemoval = actualTagNameInDb;
|
|
2032
|
+
const newTagNameForGroup = tagName;
|
|
2033
|
+
|
|
2034
|
+
// Handle group changes: remove from old group and add to new group
|
|
2035
|
+
// Compare actual old group name from DB with new group name (case-insensitive)
|
|
2036
|
+
const oldGroupNameStr = actualOldGroupName ? String( actualOldGroupName ).trim().toLowerCase() : null;
|
|
2037
|
+
const newGroupNameStr = groupName ? String( groupName ).trim().toLowerCase() : null;
|
|
2038
|
+
|
|
2039
|
+
if ( oldGroupNameStr !== newGroupNameStr ) {
|
|
2040
|
+
// Remove tag from old group if oldGroupName exists
|
|
2041
|
+
if ( actualOldGroupName ) {
|
|
2042
|
+
// Escape regex special characters in actualOldGroupName
|
|
2043
|
+
const escapedActualOldGroupName = actualOldGroupName.replace( /[.*+?^${}()|[\]\\]/g, '\\$&' );
|
|
2044
|
+
const oldGroupDoc = await customzonegrouping.findOne(
|
|
2045
|
+
{ clientId, groupName: { $regex: `^${escapedActualOldGroupName}$`, $options: 'i' } },
|
|
2046
|
+
);
|
|
2047
|
+
if ( oldGroupDoc ) {
|
|
2048
|
+
await customzonegrouping.updateOne(
|
|
2049
|
+
{ _id: oldGroupDoc._id },
|
|
2050
|
+
{ $pull: { zonesTagged: tagNameForRemoval } },
|
|
2051
|
+
);
|
|
2052
|
+
} else {
|
|
2053
|
+
}
|
|
2054
|
+
}
|
|
2055
|
+
|
|
2056
|
+
// Add tag to new group if groupName exists
|
|
2057
|
+
if ( groupName ) {
|
|
2058
|
+
// Escape regex special characters in groupName
|
|
2059
|
+
const escapedGroupName = groupName.replace( /[.*+?^${}()|[\]\\]/g, '\\$&' );
|
|
2060
|
+
let newGroupDoc = await customzonegrouping.findOne(
|
|
2061
|
+
{ clientId, groupName: { $regex: `^${escapedGroupName}$`, $options: 'i' } },
|
|
2062
|
+
);
|
|
2063
|
+
|
|
2064
|
+
if ( !newGroupDoc && !isExistingGroup ) {
|
|
2065
|
+
await customzonegrouping.create(
|
|
2066
|
+
[ {
|
|
2067
|
+
clientId,
|
|
2068
|
+
groupName,
|
|
2069
|
+
productName: req.body.productName,
|
|
2070
|
+
zonesTagged: [ newTagNameForGroup ],
|
|
2071
|
+
} ],
|
|
2072
|
+
);
|
|
2073
|
+
} else if ( newGroupDoc ) {
|
|
2074
|
+
// Add to existing group
|
|
2075
|
+
await customzonegrouping.updateOne(
|
|
2076
|
+
{ _id: newGroupDoc._id },
|
|
2077
|
+
{ $addToSet: { zonesTagged: newTagNameForGroup } },
|
|
2078
|
+
);
|
|
2079
|
+
}
|
|
2080
|
+
}
|
|
2081
|
+
} else if ( groupName && oldGroupNameStr === newGroupNameStr && oldTag && tagName !== oldTag ) {
|
|
2082
|
+
// If groupName is same (and not null) but tagName changed, update the tagName in the group's zonesTagged
|
|
2083
|
+
console.log( 'Group name same but tagName changed, updating group zonesTagged' );
|
|
2084
|
+
// Escape regex special characters in groupName
|
|
2085
|
+
const escapedGroupName = groupName.replace( /[.*+?^${}()|[\]\\]/g, '\\$&' );
|
|
2086
|
+
const groupDoc = await customzonegrouping.findOne( { clientId, groupName: { $regex: `^${escapedGroupName}$`, $options: 'i' } } );
|
|
2087
|
+
if ( groupDoc ) {
|
|
2088
|
+
await customzonegrouping.updateOne(
|
|
2089
|
+
{ _id: groupDoc._id },
|
|
2090
|
+
[
|
|
2091
|
+
{
|
|
2092
|
+
$set: {
|
|
2093
|
+
zonesTagged: {
|
|
2094
|
+
$setUnion: [
|
|
2095
|
+
{
|
|
2096
|
+
$filter: {
|
|
2097
|
+
input: '$zonesTagged',
|
|
2098
|
+
cond: { $ne: [ '$$this', actualTagNameInDb ] },
|
|
2099
|
+
},
|
|
2100
|
+
},
|
|
2101
|
+
[ tagName ],
|
|
2102
|
+
],
|
|
2103
|
+
},
|
|
2104
|
+
},
|
|
2105
|
+
},
|
|
2106
|
+
],
|
|
2107
|
+
);
|
|
2108
|
+
}
|
|
2109
|
+
}
|
|
2110
|
+
// Update tagging collection if groupName changed or tagName changed (similar to addZoneCustomTag)
|
|
2111
|
+
if ( oldTag && tagName !== oldTag ) {
|
|
2112
|
+
// If tagName changed, update tagging collection records with oldTag to have new tagName and groupName
|
|
2113
|
+
await taggingService.updateMany(
|
|
2114
|
+
{ clientId, tagName: oldTag },
|
|
2115
|
+
{ $set: { tagName, 'groupName': groupName || null, 'coordinates.$[].zoneName': tagName } },
|
|
2116
|
+
);
|
|
2117
|
+
} else if ( groupName !== oldGroupName ) {
|
|
2118
|
+
// If only groupName changed, update tagging collection with new groupName
|
|
2119
|
+
await taggingService.updateMany(
|
|
2120
|
+
{ clientId, tagName },
|
|
2121
|
+
{ $set: { 'groupName': groupName || null } },
|
|
2122
|
+
);
|
|
2123
|
+
}
|
|
2124
|
+
|
|
2125
|
+
let zonetag = await customZoneTagService.updateOne( query, { $set: payload } );
|
|
2126
|
+
const logObj = {
|
|
2127
|
+
clientId: req.body.clientId,
|
|
2128
|
+
userName: req.user?.userName,
|
|
2129
|
+
email: req.user?.email,
|
|
2130
|
+
date: new Date(),
|
|
2131
|
+
logType: 'zone',
|
|
2132
|
+
logSubType: 'updateZoneCustomTag',
|
|
2133
|
+
changes: [ `zoneName changed from ${oldTag} to ${tagName}` ],
|
|
2134
|
+
eventType: 'update',
|
|
2135
|
+
previous: {
|
|
2136
|
+
tagName: oldTag,
|
|
2137
|
+
},
|
|
2138
|
+
current: {
|
|
2139
|
+
tagName: tagName,
|
|
2140
|
+
},
|
|
2141
|
+
oldData: {
|
|
2142
|
+
TagName: oldTag,
|
|
2143
|
+
},
|
|
2144
|
+
newData: {
|
|
2145
|
+
TagName: tagName,
|
|
2146
|
+
},
|
|
2147
|
+
showTo: [ 'tango', 'client' ],
|
|
2148
|
+
};
|
|
2149
|
+
insertOpenSearchData( JSON.parse( process.env.OPENSEARCH )?.activityLog, logObj );
|
|
2150
|
+
if ( zonetag.modifiedCount || zonetag.matchedCount ) {
|
|
2151
|
+
logger.info( 'ZoneName Updated Successfully' );
|
|
2152
|
+
res.sendSuccess( 'ZoneName Updated Successfully' );
|
|
2153
|
+
} else {
|
|
2154
|
+
logger.error( { error: 'something went wrong', function: 'updateZoneCustomTag' } );
|
|
2155
|
+
return res.sendError( 'something went wrong', 500 );
|
|
2156
|
+
}
|
|
2157
|
+
} catch ( error ) {
|
|
2158
|
+
logger.error( { error: error, function: 'updateZoneCustomTag' } );
|
|
2159
|
+
return res.sendError( error, 500 );
|
|
2160
|
+
}
|
|
2161
|
+
};
|
|
2162
|
+
|
|
2163
|
+
export const deleteZoneCustomTag = async ( req, res ) => {
|
|
2164
|
+
try {
|
|
2165
|
+
const { clientId, tagName } = req.body;
|
|
2166
|
+
if ( !clientId || !tagName ) {
|
|
2167
|
+
return res.sendError( 'clientId and zoneName are required', 400 );
|
|
2168
|
+
}
|
|
2169
|
+
|
|
2170
|
+
let zoneTagDetails = await customZoneTagService.findOne( { clientId, tagName } );
|
|
2171
|
+
if ( !zoneTagDetails || zoneTagDetails?.length == 0 ) {
|
|
2172
|
+
return res.sendError( 'No data found', 400 );
|
|
2173
|
+
}
|
|
2174
|
+
updateOldData( req, res );
|
|
2175
|
+
await customZoneTagService.deleteOne( { clientId, tagName } );
|
|
2176
|
+
await taggingService.deleteMany(
|
|
2177
|
+
{
|
|
2178
|
+
clientId,
|
|
2179
|
+
tagName: { $in: tagName },
|
|
2180
|
+
},
|
|
2181
|
+
);
|
|
2182
|
+
const logObj = {
|
|
2183
|
+
clientId: req.body?.clientId,
|
|
2184
|
+
userName: req.user?.userName,
|
|
2185
|
+
email: req.user?.email,
|
|
2186
|
+
date: new Date(),
|
|
2187
|
+
logType: 'zone',
|
|
2188
|
+
logSubType: 'deleteZoneCustomTag',
|
|
2189
|
+
changes: [ `${tagName} zone tag` ],
|
|
2190
|
+
eventType: 'delete',
|
|
2191
|
+
showTo: [ 'client', 'tango' ],
|
|
2192
|
+
};
|
|
2193
|
+
insertOpenSearchData( JSON.parse( process.env.OPENSEARCH )?.activityLog, logObj );
|
|
2194
|
+
// return res.sendSuccess( 'ZoneName Deleted Successfully' );
|
|
2195
|
+
} catch ( e ) {
|
|
2196
|
+
logger.error( { error: e, function: 'deleteZoneCustomTag' } );
|
|
2197
|
+
return res.sendError( e, 500 );
|
|
2198
|
+
}
|
|
2199
|
+
};
|
|
2200
|
+
|
|
2201
|
+
|
|
2202
|
+
// setting config - get zone grouping details
|
|
2203
|
+
export const getZoneGroupDetails = async ( req, res ) => {
|
|
2204
|
+
try {
|
|
2205
|
+
const {
|
|
2206
|
+
clientId,
|
|
2207
|
+
searchValue,
|
|
2208
|
+
sortColumName,
|
|
2209
|
+
sortBy,
|
|
2210
|
+
limit,
|
|
2211
|
+
offset = 1,
|
|
2212
|
+
export: isExport,
|
|
2213
|
+
} = req.body;
|
|
2214
|
+
|
|
2215
|
+
if ( !clientId ) {
|
|
2216
|
+
return res.sendError( 'clientId is required', 400 );
|
|
2217
|
+
}
|
|
2218
|
+
|
|
2219
|
+
|
|
2220
|
+
let groupData = await customzonegrouping.findOne( { clientId: clientId } );
|
|
2221
|
+
if ( !groupData ) {
|
|
2222
|
+
return res.sendSuccess( { result: [] } );
|
|
2223
|
+
}
|
|
2224
|
+
|
|
2225
|
+
const searchMatch = searchValue ?
|
|
2226
|
+
{ groupName: { $regex: searchValue, $options: 'i' } } :
|
|
2227
|
+
null;
|
|
2228
|
+
|
|
2229
|
+
const pipeline = [
|
|
2230
|
+
{ $match: { clientId } },
|
|
2231
|
+
|
|
2232
|
+
{
|
|
2233
|
+
$lookup: {
|
|
2234
|
+
from: 'taggings',
|
|
2235
|
+
let: { groupName: '$groupName' },
|
|
2236
|
+
pipeline: [
|
|
2237
|
+
{
|
|
2238
|
+
$match: {
|
|
2239
|
+
$expr: {
|
|
2240
|
+
$and: [
|
|
2241
|
+
{ $eq: [ '$groupName', '$$groupName' ] },
|
|
2242
|
+
{ $eq: [ '$clientId', clientId ] },
|
|
2243
|
+
],
|
|
2244
|
+
},
|
|
2245
|
+
},
|
|
2246
|
+
},
|
|
2247
|
+
],
|
|
2248
|
+
as: 'tagsCount',
|
|
2249
|
+
},
|
|
2250
|
+
},
|
|
2251
|
+
|
|
2252
|
+
{
|
|
2253
|
+
$lookup: {
|
|
2254
|
+
from: 'customzonetags',
|
|
2255
|
+
let: { groupName: '$groupName' },
|
|
2256
|
+
pipeline: [
|
|
2257
|
+
{
|
|
2258
|
+
$match: {
|
|
2259
|
+
$expr: {
|
|
2260
|
+
$and: [
|
|
2261
|
+
{ $eq: [ '$groupName', '$$groupName' ] },
|
|
2262
|
+
{ $eq: [ '$clientId', clientId ] },
|
|
2263
|
+
],
|
|
2264
|
+
},
|
|
2265
|
+
},
|
|
2266
|
+
},
|
|
2267
|
+
],
|
|
2268
|
+
as: 'zonesTagged',
|
|
2269
|
+
},
|
|
2270
|
+
},
|
|
2271
|
+
|
|
2272
|
+
{
|
|
2273
|
+
$project: {
|
|
2274
|
+
clientId: 1,
|
|
2275
|
+
groupName: 1,
|
|
2276
|
+
zonesTagged: 1,
|
|
2277
|
+
zonesCount: { $size: '$tagsCount' },
|
|
2278
|
+
createdAt: 1,
|
|
2279
|
+
},
|
|
2280
|
+
},
|
|
2281
|
+
|
|
2282
|
+
{
|
|
2283
|
+
$facet: {
|
|
2284
|
+
totalCount: [
|
|
2285
|
+
...( searchMatch ? [ { $match: searchMatch } ] : [] ),
|
|
2286
|
+
{ $count: 'count' },
|
|
2287
|
+
],
|
|
2288
|
+
|
|
2289
|
+
data: [
|
|
2290
|
+
...( searchMatch ? [ { $match: searchMatch } ] : [] ),
|
|
2291
|
+
|
|
2292
|
+
...( sortColumName && sortBy ?
|
|
2293
|
+
[ { $sort: { [sortColumName]: sortBy } } ] :
|
|
2294
|
+
[ { $sort: { createdAt: -1 } } ] ),
|
|
2295
|
+
|
|
2296
|
+
...( isExport ?
|
|
2297
|
+
[] :
|
|
2298
|
+
limit ? [
|
|
2299
|
+
{ $skip: ( offset - 1 ) * limit },
|
|
2300
|
+
{ $limit: Number( limit ) },
|
|
2301
|
+
] : [] ),
|
|
2302
|
+
],
|
|
2303
|
+
},
|
|
2304
|
+
},
|
|
2305
|
+
];
|
|
2306
|
+
|
|
2307
|
+
const [ result ] = await customzonegrouping.aggregate( pipeline );
|
|
2308
|
+
|
|
2309
|
+
const zoneGroupList = result.data || [];
|
|
2310
|
+
|
|
2311
|
+
const totalCount = result.totalCount[0]?.count || 0;
|
|
2312
|
+
if ( totalCount === 0 ) {
|
|
2313
|
+
return res.sendError( 'No data', 204 );
|
|
2314
|
+
}
|
|
2315
|
+
// Get total count of zones for tabs (actual count, not filtered by search)
|
|
2316
|
+
const totalZoneCount = await customZoneTagService.count( { clientId } );
|
|
2317
|
+
|
|
2318
|
+
if ( isExport && zoneGroupList.length ) {
|
|
2319
|
+
const exportdata = zoneGroupList.map( ( element ) => ( {
|
|
2320
|
+
'Zone Group Name': element.groupName || '--',
|
|
2321
|
+
'Zones tagged': Array.isArray( element.zonesTagged ) ?
|
|
2322
|
+
element.zonesTagged.map( ( zone ) => zone.tagName ).filter( Boolean ).
|
|
2323
|
+
join( ', ' ) : '--',
|
|
2324
|
+
'Zones tagged Count': element.zonesTagged.length || 0,
|
|
2325
|
+
} ) );
|
|
2326
|
+
await download( exportdata, res );
|
|
2327
|
+
return;
|
|
2328
|
+
}
|
|
2329
|
+
|
|
2330
|
+
return res.sendSuccess( {
|
|
2331
|
+
result: zoneGroupList,
|
|
2332
|
+
totalCount,
|
|
2333
|
+
totalZoneCount,
|
|
2334
|
+
} );
|
|
2335
|
+
} catch ( e ) {
|
|
2336
|
+
logger.error( { error: e, function: 'getZoneGroupDetails' } );
|
|
2337
|
+
return res.sendError( e, 500 );
|
|
2338
|
+
}
|
|
2339
|
+
// try {
|
|
2340
|
+
// const inputData = req.body;
|
|
2341
|
+
// if ( !inputData.clientId ) {
|
|
2342
|
+
// return res.sendError( 'clientId is required', 400 );
|
|
2343
|
+
// }
|
|
2344
|
+
|
|
2345
|
+
// let Query = [
|
|
2346
|
+
// {
|
|
2347
|
+
// $match: {
|
|
2348
|
+
// clientId: inputData.clientId,
|
|
2349
|
+
// },
|
|
2350
|
+
// },
|
|
2351
|
+
// {
|
|
2352
|
+
// $lookup: {
|
|
2353
|
+
// from: 'taggings',
|
|
2354
|
+
// let: { groupName: '$groupName' },
|
|
2355
|
+
// pipeline: [
|
|
2356
|
+
// {
|
|
2357
|
+
// $match: {
|
|
2358
|
+
// $expr: {
|
|
2359
|
+
// $and: [
|
|
2360
|
+
// {
|
|
2361
|
+
// $eq: [ '$groupName', '$$groupName' ],
|
|
2362
|
+
// },
|
|
2363
|
+
// {
|
|
2364
|
+
// $eq: [ '$clientId', inputData.clientId ],
|
|
2365
|
+
// },
|
|
2366
|
+
// ],
|
|
2367
|
+
// },
|
|
2368
|
+
// },
|
|
2369
|
+
// },
|
|
2370
|
+
// ], as: 'tagsCount',
|
|
2371
|
+
// },
|
|
2372
|
+
// },
|
|
2373
|
+
// {
|
|
2374
|
+
// $lookup: {
|
|
2375
|
+
// from: 'customzonetags',
|
|
2376
|
+
// let: { groupName: '$groupName' },
|
|
2377
|
+
// pipeline: [
|
|
2378
|
+
// {
|
|
2379
|
+
// $match: {
|
|
2380
|
+
// $expr: {
|
|
2381
|
+
// $and: [
|
|
2382
|
+
// {
|
|
2383
|
+
// $eq: [ '$groupName', '$$groupName' ],
|
|
2384
|
+
// },
|
|
2385
|
+
// {
|
|
2386
|
+
// $eq: [ '$clientId', inputData.clientId ],
|
|
2387
|
+
// },
|
|
2388
|
+
// ],
|
|
2389
|
+
// },
|
|
2390
|
+
// },
|
|
2391
|
+
// },
|
|
2392
|
+
// ], as: 'zonesTagged',
|
|
2393
|
+
// },
|
|
2394
|
+
// },
|
|
2395
|
+
// {
|
|
2396
|
+
// $project: {
|
|
2397
|
+
// clientId: 1,
|
|
2398
|
+
// groupName: 1,
|
|
2399
|
+
// zonesTagged: 1,
|
|
2400
|
+
// zonesCount: { $size: '$tagsCount' },
|
|
2401
|
+
// },
|
|
2402
|
+
// },
|
|
2403
|
+
// ];
|
|
2404
|
+
|
|
2405
|
+
// if ( req.body.searchValue && req.body.searchValue !== '' ) {
|
|
2406
|
+
// Query.push( {
|
|
2407
|
+
// $match: {
|
|
2408
|
+
// $or: [
|
|
2409
|
+
// { groupName: { $regex: req.body.searchValue, $options: 'i' } },
|
|
2410
|
+
// ],
|
|
2411
|
+
// },
|
|
2412
|
+
// } );
|
|
2413
|
+
// }
|
|
2414
|
+
|
|
2415
|
+
// if ( req.body.sortColumName && req.body.sortColumName !== '' && req.body.sortBy ) {
|
|
2416
|
+
// Query.push( {
|
|
2417
|
+
// $sort: { [req.body.sortColumName]: req.body.sortBy },
|
|
2418
|
+
// } );
|
|
2419
|
+
// } else {
|
|
2420
|
+
// // Default sort by createdAt descending if no sort specified
|
|
2421
|
+
// Query.push( {
|
|
2422
|
+
// $sort: { createdAt: -1 },
|
|
2423
|
+
// } );
|
|
2424
|
+
// }
|
|
2425
|
+
|
|
2426
|
+
// // Get total count before pagination
|
|
2427
|
+
// const countQuery = [ ...Query ];
|
|
2428
|
+
// const totalCountResult = await customzonegrouping.aggregate( [
|
|
2429
|
+
// ...countQuery,
|
|
2430
|
+
// { $count: 'total' },
|
|
2431
|
+
// ] );
|
|
2432
|
+
// console.log( '🚀 ~ getZoneGroupDetails ~ totalCountResult:', totalCountResult );
|
|
2433
|
+
// const totalCount = totalCountResult.length > 0 ? totalCountResult[0].total : 0;
|
|
2434
|
+
|
|
2435
|
+
// if ( req.body.limit && req.body.offset && !req.body.export ) {
|
|
2436
|
+
// Query.push(
|
|
2437
|
+
// { $skip: ( req.body.offset - 1 ) * req.body.limit },
|
|
2438
|
+
// { $limit: Number( req.body.limit ) },
|
|
2439
|
+
// );
|
|
2440
|
+
// }
|
|
2441
|
+
|
|
2442
|
+
// const zoneGroupList = await customzonegrouping.aggregate( Query );
|
|
2443
|
+
|
|
2444
|
+
// if ( req.body.export && zoneGroupList.length > 0 ) {
|
|
2445
|
+
// const exportdata = [];
|
|
2446
|
+
// zoneGroupList.forEach( ( element ) => {
|
|
2447
|
+
// const data = {
|
|
2448
|
+
// 'Zone Group Name': element.groupName || '--',
|
|
2449
|
+
// 'Zones tagged': Array.isArray( element.zonesTagged ) ? element.zonesTagged.join( ', ' ) : '--',
|
|
2450
|
+
// 'Zones tagged Count': element.zonesCount || 0,
|
|
2451
|
+
// // 'Stores Tagged Count': element.storesTaggedCount || 0,
|
|
2452
|
+
// };
|
|
2453
|
+
// exportdata.push( data );
|
|
2454
|
+
// } );
|
|
2455
|
+
// await download( exportdata, res );
|
|
2456
|
+
// return;
|
|
2457
|
+
// }
|
|
2458
|
+
|
|
2459
|
+
// return res.sendSuccess( {
|
|
2460
|
+
// result: zoneGroupList,
|
|
2461
|
+
// count: totalCount,
|
|
2462
|
+
// } );
|
|
2463
|
+
// } catch ( e ) {
|
|
2464
|
+
// logger.error( { error: e, function: 'getZoneGroupDetails' } );
|
|
2465
|
+
// console.error( 'getZoneGroupDetails error:', e );
|
|
2466
|
+
// return res.sendError( e, 500 );
|
|
2467
|
+
// }
|
|
2468
|
+
};
|
|
2469
|
+
|
|
2470
|
+
export const addZoneGroup = async ( req, res ) => {
|
|
2471
|
+
try {
|
|
2472
|
+
const { clientId, groupName, zonesTagged = [], productName = '' } = req.body;
|
|
2473
|
+
|
|
2474
|
+
if ( !clientId || !groupName ) {
|
|
2475
|
+
return res.sendError( 'clientId and groupName are required', 400 );
|
|
2476
|
+
}
|
|
2477
|
+
|
|
2478
|
+
// Escape regex special characters in groupName to avoid invalid regex patterns
|
|
2479
|
+
const escapedGroupName = groupName.replace( /[.*+?^${}()|[\]\\]/g, '\\$&' );
|
|
2480
|
+
|
|
2481
|
+
// Case-insensitive check for existing group
|
|
2482
|
+
const existingGroup = await customzonegrouping.findOne( {
|
|
2483
|
+
clientId,
|
|
2484
|
+
groupName: { $regex: `^${escapedGroupName}$`, $options: 'i' },
|
|
2485
|
+
} );
|
|
2486
|
+
|
|
2487
|
+
if ( existingGroup ) {
|
|
2488
|
+
return res.sendError( `groupName "${groupName}" already exists for this client`, 409 );
|
|
2489
|
+
}
|
|
2490
|
+
|
|
2491
|
+
const payload = {
|
|
2492
|
+
clientId,
|
|
2493
|
+
groupName,
|
|
2494
|
+
zonesTagged: Array.isArray( zonesTagged ) ? zonesTagged : [],
|
|
2495
|
+
productName,
|
|
2496
|
+
};
|
|
2497
|
+
|
|
2498
|
+
await customzonegrouping.create( payload );
|
|
2499
|
+
|
|
2500
|
+
// Update groupName for all zone tags in zonesTagged array
|
|
2501
|
+
if ( Array.isArray( zonesTagged ) && zonesTagged.length > 0 ) {
|
|
2502
|
+
// Update all tags that are in the zonesTagged array to have this groupName
|
|
2503
|
+
await customZoneTagService.updateMany(
|
|
2504
|
+
{
|
|
2505
|
+
clientId,
|
|
2506
|
+
tagName: { $in: zonesTagged },
|
|
2507
|
+
},
|
|
2508
|
+
{ $set: { groupName } },
|
|
2509
|
+
);
|
|
2510
|
+
await taggingService.updateMany(
|
|
2511
|
+
{
|
|
2512
|
+
clientId,
|
|
2513
|
+
tagName: { $in: zonesTagged },
|
|
2514
|
+
},
|
|
2515
|
+
{ $set: { groupName } },
|
|
2516
|
+
);
|
|
2517
|
+
}
|
|
2518
|
+
|
|
2519
|
+
logger.info( 'Zone Group Created Successfully' );
|
|
2520
|
+
|
|
2521
|
+
const logObj = {
|
|
2522
|
+
clientId,
|
|
2523
|
+
userName: req.user?.userName,
|
|
2524
|
+
email: req.user?.email,
|
|
2525
|
+
date: new Date(),
|
|
2526
|
+
logType: 'zone',
|
|
2527
|
+
logSubType: 'addZoneGroup',
|
|
2528
|
+
changes: [ `${groupName} zone group` ],
|
|
2529
|
+
eventType: 'create',
|
|
2530
|
+
showTo: [ 'client', 'tango' ],
|
|
2531
|
+
};
|
|
2532
|
+
|
|
2533
|
+
insertOpenSearchData( JSON.parse( process.env.OPENSEARCH )?.activityLog, logObj );
|
|
2534
|
+
return res.sendSuccess( 'Zone Group Created Successfully' );
|
|
2535
|
+
} catch ( e ) {
|
|
2536
|
+
// Duplicate key error from unique index
|
|
2537
|
+
if ( e.code === 11000 ) {
|
|
2538
|
+
return res.sendError( `groupName "${req.body.groupName}" already exists for this client`, 409 );
|
|
2539
|
+
}
|
|
2540
|
+
|
|
2541
|
+
logger.error( { error: e, function: 'addZoneGroup' } );
|
|
2542
|
+
return res.sendError( e.message || 'Internal Server Error', 500 );
|
|
2543
|
+
}
|
|
2544
|
+
};
|
|
2545
|
+
|
|
2546
|
+
export const updateZoneGroup = async ( req, res ) => {
|
|
2547
|
+
try {
|
|
2548
|
+
const { _id, clientId, groupName, productName, zonesTagged = [] } = req.body;
|
|
2549
|
+
|
|
2550
|
+
if ( !clientId || !groupName ) {
|
|
2551
|
+
return res.sendError( 'clientId and groupName are required', 400 );
|
|
2552
|
+
}
|
|
2553
|
+
|
|
2554
|
+
let findGroup = await customzonegrouping.findOne( { _id: _id } );
|
|
2555
|
+
|
|
2556
|
+
// Escape regex special characters in groupName to avoid invalid regex patterns
|
|
2557
|
+
const escapedGroupName = groupName.replace( /[.*+?^${}()|[\]\\]/g, '\\$&' );
|
|
2558
|
+
|
|
2559
|
+
// Case-insensitive check for existing group (excluding current group)
|
|
2560
|
+
const existingGroup = await customzonegrouping.findOne( {
|
|
2561
|
+
clientId: clientId,
|
|
2562
|
+
groupName: { $regex: `^${escapedGroupName}$`, $options: 'i' },
|
|
2563
|
+
_id: { $ne: _id },
|
|
2564
|
+
} );
|
|
2565
|
+
if ( existingGroup && existingGroup.groupName ) {
|
|
2566
|
+
return res.sendError( `groupName "${groupName}" already exists for this client`, 409 );
|
|
2567
|
+
}
|
|
2568
|
+
|
|
2569
|
+
if ( findGroup ) {
|
|
2570
|
+
let removedTags = findGroup.zonesTagged.filter( ( zone ) => !zonesTagged.includes( zone ) ); ;
|
|
2571
|
+
if ( removedTags && removedTags.length > 0 ) {
|
|
2572
|
+
let updateQuery = { clientId, tagName: { $in: removedTags } };
|
|
2573
|
+
await customZoneTagService.updateMany( updateQuery, { $set: { groupName: null } } );
|
|
2574
|
+
await taggingService.updateMany( updateQuery, { $set: { groupName: null } } );
|
|
2575
|
+
}
|
|
2576
|
+
}
|
|
2577
|
+
|
|
2578
|
+
|
|
2579
|
+
await customzonegrouping.updateOne( { _id: _id }, { groupName: groupName, zonesTagged: zonesTagged } );
|
|
2580
|
+
let updateQuery = { clientId, tagName: { $in: zonesTagged } };
|
|
2581
|
+
await customZoneTagService.updateMany( updateQuery, { $set: { groupName } } );
|
|
2582
|
+
await taggingService.updateMany( updateQuery, { $set: { groupName } } );
|
|
2583
|
+
|
|
2584
|
+
|
|
2585
|
+
const logObj = {
|
|
2586
|
+
clientId: clientId,
|
|
2587
|
+
userName: req.user?.userName,
|
|
2588
|
+
email: req.user?.email,
|
|
2589
|
+
date: new Date(),
|
|
2590
|
+
logType: 'zone',
|
|
2591
|
+
logSubType: 'updateZoneGroup',
|
|
2592
|
+
changes: [ `zone group updated from "${findGroup.groupName || groupName}" to "${groupName}"` ],
|
|
2593
|
+
eventType: 'update',
|
|
2594
|
+
previous: {
|
|
2595
|
+
groupName: findGroup.groupName || groupName,
|
|
2596
|
+
productName: findGroup.productName,
|
|
2597
|
+
zonesTagged: findGroup.zonesTagged,
|
|
2598
|
+
},
|
|
2599
|
+
current: {
|
|
2600
|
+
groupName: groupName,
|
|
2601
|
+
productName: productName,
|
|
2602
|
+
zonesTagged: zonesTagged,
|
|
2603
|
+
},
|
|
2604
|
+
oldData: {
|
|
2605
|
+
GroupName: findGroup.groupName || groupName,
|
|
2606
|
+
},
|
|
2607
|
+
newData: {
|
|
2608
|
+
GroupName: groupName,
|
|
2609
|
+
},
|
|
2610
|
+
showTo: [ 'client', 'tango' ],
|
|
2611
|
+
};
|
|
2612
|
+
|
|
2613
|
+
insertOpenSearchData( JSON.parse( process.env.OPENSEARCH )?.activityLog, logObj );
|
|
2614
|
+
|
|
2615
|
+
return res.sendSuccess( 'Zone Group Updated Successfully' );
|
|
2616
|
+
} catch ( e ) {
|
|
2617
|
+
// Duplicate key error from unique index
|
|
2618
|
+
if ( e.code === 11000 ) {
|
|
2619
|
+
return res.sendError( `groupName "${req.body.groupName}" already exists for this client`, 409 );
|
|
2620
|
+
}
|
|
2621
|
+
|
|
2622
|
+
logger.error( { error: e, function: 'updateZoneGroup' } );
|
|
2623
|
+
return res.sendError( e.message || 'Internal Server Error', 500 );
|
|
2624
|
+
}
|
|
2625
|
+
};
|
|
2626
|
+
|
|
2627
|
+
export const uploadBulkZoneGroup = async ( req, res ) => {
|
|
2628
|
+
try {
|
|
2629
|
+
const groupsArray = req.body;
|
|
2630
|
+
const clientId = req.bulkZoneClientId;
|
|
2631
|
+
|
|
2632
|
+
if ( !groupsArray || !Array.isArray( groupsArray ) || groupsArray.length === 0 ) {
|
|
2633
|
+
return res.sendError( 'group-config file must not be empty', 400 );
|
|
2634
|
+
}
|
|
2635
|
+
|
|
2636
|
+
if ( !clientId ) {
|
|
2637
|
+
return res.sendError( 'clientId is required', 400 );
|
|
2638
|
+
}
|
|
2639
|
+
|
|
2640
|
+
const zoneGroupDataList = [];
|
|
2641
|
+
const createdGroupNames = [];
|
|
2642
|
+
|
|
2643
|
+
// Process each group in the array
|
|
2644
|
+
for ( const group of groupsArray ) {
|
|
2645
|
+
if ( !group.groupName || group.groupName === '' ) {
|
|
2646
|
+
continue; // Skip groups without groupName
|
|
2647
|
+
}
|
|
2648
|
+
|
|
2649
|
+
const zoneGroupData = {
|
|
2650
|
+
clientId: clientId,
|
|
2651
|
+
groupName: group.groupName,
|
|
2652
|
+
productName: group.productName,
|
|
2653
|
+
};
|
|
2654
|
+
zoneGroupDataList.push( zoneGroupData );
|
|
2655
|
+
createdGroupNames.push( group.groupName );
|
|
2656
|
+
}
|
|
2657
|
+
|
|
2658
|
+
// Create all zone groups
|
|
2659
|
+
if ( zoneGroupDataList.length > 0 ) {
|
|
2660
|
+
await customzonegrouping.create( zoneGroupDataList );
|
|
2661
|
+
}
|
|
2662
|
+
|
|
2663
|
+
logger.info( `Bulk Zone Groups Created Successfully: ${createdGroupNames.length} groups` );
|
|
2664
|
+
const logObj = {
|
|
2665
|
+
clientId: clientId,
|
|
2666
|
+
userName: req.user?.userName,
|
|
2667
|
+
email: req.user?.email,
|
|
2668
|
+
date: new Date(),
|
|
2669
|
+
logType: 'zone',
|
|
2670
|
+
logSubType: 'addBulkZoneGroup',
|
|
2671
|
+
changes: createdGroupNames.map( ( groupName ) => `${groupName} zone group` ),
|
|
2672
|
+
eventType: 'create',
|
|
2673
|
+
showTo: [ 'client', 'tango' ],
|
|
2674
|
+
};
|
|
2675
|
+
insertOpenSearchData( JSON.parse( process.env.OPENSEARCH )?.activityLog, logObj );
|
|
2676
|
+
return res.sendSuccess( {
|
|
2677
|
+
message: `Zone Groups Created Successfully`,
|
|
2678
|
+
count: createdGroupNames.length,
|
|
2679
|
+
} );
|
|
2680
|
+
} catch ( e ) {
|
|
2681
|
+
logger.error( { error: e, function: 'uploadBulkZoneGroup' } );
|
|
2682
|
+
return res.sendError( 'Failed to upload group-config file', 500 );
|
|
2683
|
+
}
|
|
2684
|
+
};
|
|
2685
|
+
|
|
2686
|
+
export const deleteZoneGroup = async ( req, res ) => {
|
|
2687
|
+
try {
|
|
2688
|
+
let zoneGroupDetails = await customzonegrouping.findOne( { _id: req.body._id, clientId: req.body.clientId, groupName: req.body.groupName } );
|
|
2689
|
+
if ( !zoneGroupDetails || zoneGroupDetails?.length == 0 ) {
|
|
2690
|
+
return res.sendError( 'no data found', 204 );
|
|
2691
|
+
}
|
|
2692
|
+
|
|
2693
|
+
// Remove this group mapping from all custom zone tags (zones) that are linked to it
|
|
2694
|
+
await customZoneTagService.updateMany(
|
|
2695
|
+
{
|
|
2696
|
+
clientId: req.body.clientId,
|
|
2697
|
+
groupName: req.body.groupName,
|
|
2698
|
+
},
|
|
2699
|
+
{ $set: { groupName: null } },
|
|
2700
|
+
);
|
|
2701
|
+
await taggingService.updateMany(
|
|
2702
|
+
{
|
|
2703
|
+
clientId: req.body.clientId,
|
|
2704
|
+
groupName: req.body.groupName,
|
|
2705
|
+
},
|
|
2706
|
+
{ $set: { groupName: null } },
|
|
2707
|
+
);
|
|
2708
|
+
await customzonegrouping.deleteOne( { _id: req.body._id, clientId: req.body.clientId, groupName: req.body.groupName } );
|
|
2709
|
+
|
|
2710
|
+
const logObj = {
|
|
2711
|
+
clientId: req.body?.clientId,
|
|
2712
|
+
userName: req.user?.userName,
|
|
2713
|
+
email: req.user?.email,
|
|
2714
|
+
date: new Date(),
|
|
2715
|
+
logType: 'zone',
|
|
2716
|
+
logSubType: 'deleteZoneGroup',
|
|
2717
|
+
changes: [ `${req.body.groupName} zone group` ],
|
|
2718
|
+
eventType: 'delete',
|
|
2719
|
+
showTo: [ 'client', 'tango' ],
|
|
2720
|
+
};
|
|
2721
|
+
insertOpenSearchData( JSON.parse( process.env.OPENSEARCH )?.activityLog, logObj );
|
|
2722
|
+
// await updatezoneTagging( req, res );
|
|
2723
|
+
return res.sendSuccess( 'Zone Group Deleted Successfully' );
|
|
2724
|
+
} catch ( e ) {
|
|
2725
|
+
logger.error( { error: e, function: 'deleteZoneGroup' } );
|
|
2726
|
+
return res.sendError( e, 500 );
|
|
2727
|
+
}
|
|
2728
|
+
};
|
|
2729
|
+
|
|
2730
|
+
export async function oldTagsMigration() {
|
|
2731
|
+
let uniqueTags = await taggingService.find( { productName: 'tangoZone', clientId: '440' } );
|
|
2732
|
+
const result = _.uniqBy( uniqueTags, 'tagName' );
|
|
2733
|
+
|
|
2734
|
+
|
|
2735
|
+
for ( let zone of result ) {
|
|
2736
|
+
let obj = {
|
|
2737
|
+
clientId: zone.clientId,
|
|
2738
|
+
tagName: zone.tagName,
|
|
2739
|
+
productName: zone.productName,
|
|
2740
|
+
rgbColor: zone.rgbColor,
|
|
2741
|
+
rgbBorderColor: zone.rgbBorderColor,
|
|
2742
|
+
groupName: null,
|
|
2743
|
+
};
|
|
2744
|
+
await customZoneTagService.create( [ obj ] );
|
|
2745
|
+
}
|
|
2746
|
+
}
|
|
2747
|
+
|