tango-app-api-audit 1.0.28 → 1.0.30

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,29 +1,29 @@
1
- # README #
2
-
3
- This README would normally document whatever steps are necessary to get your application up and running.
4
-
5
- ### What is this repository for? ###
6
-
7
- * Quick summary
8
- * Version
9
- * [Learn Markdown](https://bitbucket.org/tutorials/markdowndemo)
10
-
11
- ### How do I get set up? ###
12
-
13
- * Summary of set up
14
- * Configuration
15
- * Dependencies
16
- * Database configuration
17
- * How to run tests
18
- * Deployment instructions
19
-
20
- ### Contribution guidelines ###
21
-
22
- * Writing tests
23
- * Code review
24
- * Other guidelines
25
-
26
- ### Who do I talk to? ###
27
-
28
- * Repo owner or admin
1
+ # README #
2
+
3
+ This README would normally document whatever steps are necessary to get your application up and running.
4
+
5
+ ### What is this repository for? ###
6
+
7
+ * Quick summary
8
+ * Version
9
+ * [Learn Markdown](https://bitbucket.org/tutorials/markdowndemo)
10
+
11
+ ### How do I get set up? ###
12
+
13
+ * Summary of set up
14
+ * Configuration
15
+ * Dependencies
16
+ * Database configuration
17
+ * How to run tests
18
+ * Deployment instructions
19
+
20
+ ### Contribution guidelines ###
21
+
22
+ * Writing tests
23
+ * Code review
24
+ * Other guidelines
25
+
26
+ ### Who do I talk to? ###
27
+
28
+ * Repo owner or admin
29
29
  * Other community or team contact
package/package.json CHANGED
@@ -1,39 +1,40 @@
1
- {
2
- "name": "tango-app-api-audit",
3
- "version": "1.0.28",
4
- "description": "audit & audit metrics apis",
5
- "main": "index.js",
6
- "type": "module",
7
- "scripts": {
8
- "start": "nodemon --exec \"eslint --fix . && node index.js\""
9
- },
10
- "engines": {
11
- "node": ">=18.10.0"
12
- },
13
- "author": "praveenraj",
14
- "license": "ISC",
15
- "dependencies": {
16
- "aws-sdk": "^2.1643.0",
17
- "dayjs": "^1.11.11",
18
- "dotenv": "^16.4.5",
19
- "express": "^4.19.2",
20
- "handlebars": "^4.7.8",
21
- "joi-to-swagger": "^6.2.0",
22
- "lodash": "^4.17.21",
23
- "mongodb": "^6.7.0",
24
- "nodemon": "^3.1.3",
25
- "swagger-ui-express": "^5.0.1",
26
- "tango-api-schema": "^2.0.124",
27
- "tango-app-api-middleware": "^3.1.24",
28
- "winston": "^3.13.0",
29
- "winston-daily-rotate-file": "^5.0.0"
30
- },
31
- "devDependencies": {
32
- "eslint": "^8.57.0",
33
- "eslint-config-google": "^0.14.0",
34
- "eslint-config-semistandard": "^17.0.0",
35
- "eslint-config-standard": "^17.1.0",
36
- "eslint-plugin-import": "^2.29.1",
37
- "eslint-plugin-promise": "^6.2.0"
38
- }
39
- }
1
+ {
2
+ "name": "tango-app-api-audit",
3
+ "version": "1.0.30",
4
+ "description": "audit & audit metrics apis",
5
+ "main": "index.js",
6
+ "type": "module",
7
+ "scripts": {
8
+ "start": "nodemon --exec \"eslint --fix . && node index.js\""
9
+ },
10
+ "engines": {
11
+ "node": ">=18.10.0"
12
+ },
13
+ "author": "praveenraj",
14
+ "license": "ISC",
15
+ "dependencies": {
16
+ "adm-zip": "^0.5.15",
17
+ "aws-sdk": "^2.1643.0",
18
+ "dayjs": "^1.11.11",
19
+ "dotenv": "^16.4.5",
20
+ "express": "^4.19.2",
21
+ "handlebars": "^4.7.8",
22
+ "joi-to-swagger": "^6.2.0",
23
+ "lodash": "^4.17.21",
24
+ "mongodb": "^6.7.0",
25
+ "nodemon": "^3.1.3",
26
+ "swagger-ui-express": "^5.0.1",
27
+ "tango-api-schema": "^2.0.138",
28
+ "tango-app-api-middleware": "^3.1.26",
29
+ "winston": "^3.13.0",
30
+ "winston-daily-rotate-file": "^5.0.0"
31
+ },
32
+ "devDependencies": {
33
+ "eslint": "^8.57.0",
34
+ "eslint-config-google": "^0.14.0",
35
+ "eslint-config-semistandard": "^17.0.0",
36
+ "eslint-config-standard": "^17.1.0",
37
+ "eslint-plugin-import": "^2.29.1",
38
+ "eslint-plugin-promise": "^6.2.0"
39
+ }
40
+ }
@@ -8,6 +8,8 @@ import dayjs from 'dayjs';
8
8
  import { createAssignAudit } from '../service/assignAudit.service.js';
9
9
  import { countDocumentsStore } from '../service/store.service.js';
10
10
  import { findOneClient } from '../service/client.service.js';
11
+ import { getAuditFilterData, getAuditImageData, zipDownloadImage } from '../validation/audit.validation.js';
12
+ import { getReauditImg } from './audit.controllers.js';
11
13
  // import { listQueue } from 'tango-app-api-middleware/src/utils/aws/sqs.js';
12
14
 
13
15
  export async function userAuditHistory( req, res ) {
@@ -1209,7 +1211,7 @@ export async function reTrigger( req, res ) {
1209
1211
  }
1210
1212
 
1211
1213
  const storeRecord ={
1212
- status: req.audit.status !== 'completed' ? 'skipped': 'not_assign',
1214
+ status: userAudit?.auditStatus !== 'completed' ? 'skipped': 'not_assign',
1213
1215
  };
1214
1216
 
1215
1217
  await updateOneStoreAudit( query, storeRecord );
@@ -1264,7 +1266,7 @@ export async function reTrigger( req, res ) {
1264
1266
  }
1265
1267
 
1266
1268
  const storeRecord ={
1267
- status: req.audit.status !== 'completed' ? 'skipped': 'assigned',
1269
+ status: userAudit?.auditStatus !== 'completed' ? 'skipped': 'assigned',
1268
1270
  };
1269
1271
 
1270
1272
  await updateOneStoreAudit( query, storeRecord );
@@ -1297,10 +1299,65 @@ export async function reTrigger( req, res ) {
1297
1299
  }
1298
1300
  }
1299
1301
 
1300
- export async function beforeCountImages( req, res ) {
1302
+ export async function auditImages( req, res ) {
1301
1303
  try {
1302
- const inputData = req.body;
1303
- return res.sendSuccess( { result: inputData } );
1304
+ const inputData = req.query;
1305
+ let auditImageDownload = '';
1306
+ switch ( inputData.imageType ) {
1307
+ case 'BC':
1308
+ const auditImageBC = await getAuditImageData( inputData );
1309
+ if ( auditImageBC?.errorCode ) {
1310
+ const error = auditImageBC?.errorMsg;
1311
+ const code = auditImageBC?.errorCode;
1312
+ return res.sendError( { error: error }, code );
1313
+ }
1314
+
1315
+ if ( inputData.export==true ) {
1316
+ auditImageDownload= await zipDownloadImage( auditImageBC );
1317
+ // Set response headers for the zip file
1318
+ res.set( 'Content-Type', 'application/zip' );
1319
+ res.set( 'Content-Disposition', `attachment; filename=${inputData.storeId}_${inputData.fileDate}_${inputData.imageType}.zip` );
1320
+
1321
+ // Send the zip file as the API response
1322
+ return res.send( auditImageDownload );
1323
+ }
1324
+ res.sendSuccess( { result: auditImageBC } );
1325
+
1326
+ break;
1327
+
1328
+ case 'AC':
1329
+
1330
+ const [ filterData, auditImageAC ] = await Promise.all( [ getAuditFilterData( inputData ), getAuditImageData( inputData ) ] );
1331
+
1332
+ if ( filterData?.errorCode || auditImageAC?.errorCode ) {
1333
+ const error = filterData?.errorMsg || auditImageAC.errorMsg;
1334
+ const code = filterData?.errorCode || auditImageAC.errorCode;
1335
+ return res.sendError( { error: error }, code );
1336
+ }
1337
+
1338
+ const reauditImg = await getReauditImg( filterData, auditImageAC );
1339
+
1340
+ logger.info( { filterData: filterData } );
1341
+ logger.info( { auditImageAC: auditImageAC } );
1342
+ logger.info( { reauditImg: reauditImg } );
1343
+ if ( inputData.export==true ) {
1344
+ auditImageDownload= await zipDownloadImage( reauditImg );
1345
+ // Set response headers for the zip file
1346
+ res.set( 'Content-Type', 'application/zip' );
1347
+ res.set( 'Content-Disposition', `attachment; filename=${inputData.storeId}_${inputData.fileDate}_${inputData.imageType}.zip` );
1348
+
1349
+ // Send the zip file as the API response
1350
+ return res.send( auditImageDownload );
1351
+ }
1352
+ res.sendSuccess( { result: reauditImg } );
1353
+ break;
1354
+ default:
1355
+ res.sendError( 'No Data Found', 204 );
1356
+ break;
1357
+ }
1358
+
1359
+
1360
+ // return res.sendSuccess( { result: inputData } );
1304
1361
  } catch ( error ) {
1305
1362
  const err = error.message || 'Internal Server Error';
1306
1363
  logger.info( { error: error, message: req.body, function: 'beforeCountImages' } );
@@ -1,5 +1,5 @@
1
1
  import j2s from 'joi-to-swagger';
2
- import { clientMetricsSchema, getAuditImagesSchema, overAllAuditSummarySchema, overViewCardSchema, overViewTableSchema, reTriggerValidSchema, storeMetricsSchema, summarySplitSchema, userAuditHistorySchema, userMetricsSchema } from '../dtos/auditMetrics.dtos.js';
2
+ import { auditImageValidSchema, clientMetricsSchema, getAuditImagesSchema, overAllAuditSummarySchema, overViewCardSchema, overViewTableSchema, reTriggerValidSchema, storeMetricsSchema, summarySplitSchema, userAuditHistorySchema, userMetricsSchema } from '../dtos/auditMetrics.dtos.js';
3
3
 
4
4
  export const auditMetricsDocs = {
5
5
 
@@ -223,4 +223,44 @@ export const auditMetricsDocs = {
223
223
  },
224
224
  },
225
225
  },
226
+ '/v3/audit-metrics/audit-images': {
227
+ get: {
228
+ tags: [ 'Audit Metrics' ],
229
+ description: `View and Download audit images`,
230
+ operationId: 'audit-images',
231
+ parameters: [
232
+ {
233
+ in: 'query',
234
+ name: 'fileDate',
235
+ scema: j2s( auditImageValidSchema ).swagger,
236
+ require: true,
237
+ },
238
+ {
239
+ in: 'query',
240
+ name: 'storeId',
241
+ scema: j2s( auditImageValidSchema ).swagger,
242
+ require: true,
243
+ },
244
+ {
245
+ in: 'query',
246
+ name: 'imageType',
247
+ scema: j2s( auditImageValidSchema ).swagger,
248
+ require: true,
249
+ },
250
+ {
251
+ in: 'query',
252
+ name: 'export',
253
+ scema: j2s( auditImageValidSchema ).swagger,
254
+ require: true,
255
+ },
256
+ ],
257
+ responses: {
258
+ 200: { description: 'Successful' },
259
+ 401: { description: 'Unauthorized User' },
260
+ 422: { description: 'Field Error' },
261
+ 500: { description: 'Server Error' },
262
+ 204: { description: 'Not Found' },
263
+ },
264
+ },
265
+ },
226
266
  };
@@ -167,18 +167,15 @@ export const reTriggerValid = {
167
167
  body: reTriggerValidSchema,
168
168
  };
169
169
 
170
- export const beforeCountImageSchema = joi.object(
170
+ export const auditImageValidSchema = joi.object(
171
171
  {
172
172
  fileDate: joi.string().required(),
173
173
  storeId: joi.string().required(),
174
- auditType: joi.string().optional(),
175
- triggerType: joi.string().optional(),
176
- totalCount: joi.number().required(),
177
- userId: joi.string().optional(),
178
- auditId: joi.string().required(),
174
+ imageType: joi.string().required(),
175
+ export: joi.boolean().required(),
179
176
  },
180
177
  );
181
178
 
182
- export const beforeCountImageValid = {
183
- body: beforeCountImageSchema,
179
+ export const auditImageValid = {
180
+ query: auditImageValidSchema,
184
181
  };
@@ -1,8 +1,8 @@
1
1
 
2
2
  import express from 'express';
3
3
  import { isAllowedSessionHandler, validate } from 'tango-app-api-middleware';
4
- import { beforeCountImages, clientMetrics, overAllAuditSummary, overViewCard, overViewTable, reTrigger, storeMetrics, summarySplit, userAuditHistory, userMetrics } from '../controllers/auditMetrics.controllers.js';
5
- import { beforeCountImageValid, clientMetricsValid, overAllAuditSummaryValid, overViewCardValid, overViewTableValid, reTriggerValid, storeMetricsValid, summarySplitValid, userAuditHistoryValid, userMetricsValid } from '../dtos/auditMetrics.dtos.js';
4
+ import { auditImages, clientMetrics, overAllAuditSummary, overViewCard, overViewTable, reTrigger, storeMetrics, summarySplit, userAuditHistory, userMetrics } from '../controllers/auditMetrics.controllers.js';
5
+ import { auditImageValid, clientMetricsValid, overAllAuditSummaryValid, overViewCardValid, overViewTableValid, reTriggerValid, storeMetricsValid, summarySplitValid, userAuditHistoryValid, userMetricsValid } from '../dtos/auditMetrics.dtos.js';
6
6
  import { isAuditDocumentExist, isAuditInputFolderExist } from '../validation/audit.validation.js';
7
7
 
8
8
  export const auditMetricsRouter = express.Router();
@@ -16,6 +16,6 @@ auditMetricsRouter.post( '/summary-split-up', isAllowedSessionHandler, validate(
16
16
  auditMetricsRouter.post( '/overview-table', isAllowedSessionHandler, validate( overViewTableValid ), overViewTable );
17
17
  auditMetricsRouter.post( '/overall-audit-summary', isAllowedSessionHandler, validate( overAllAuditSummaryValid ), overAllAuditSummary );
18
18
  auditMetricsRouter.post( '/re-trigger', isAllowedSessionHandler, validate( reTriggerValid ), isAuditDocumentExist, isAuditInputFolderExist, reTrigger );
19
- auditMetricsRouter.post( '/before-count-images', isAllowedSessionHandler, validate( beforeCountImageValid ), isAuditInputFolderExist, beforeCountImages );
19
+ auditMetricsRouter.get( '/audit-images', isAllowedSessionHandler, validate( auditImageValid ), isAuditInputFolderExist, auditImages );
20
20
 
21
21
  export default auditMetricsRouter;
@@ -1,6 +1,7 @@
1
- import { checkFileExist, chunkArray, getQueueUrl, logger } from 'tango-app-api-middleware';
1
+ import { checkFileExist, chunkArray, getJsonFileData, getObject, getQueueUrl, listFileWithoutLimit, logger, signedUrl } from 'tango-app-api-middleware';
2
2
  import { findOneUserAudit } from '../service/userAudit.service.js';
3
3
  import { findOneStoreAudit } from '../service/storeAudit.service.js';
4
+ import AdmZip from 'adm-zip';
4
5
 
5
6
  export async function isExistsQueue( req, res, next ) {
6
7
  try {
@@ -163,7 +164,7 @@ const mapFunction = async ( chunkData, filterData ) => {
163
164
  export async function isAuditInputFolderExist( req, res, next ) {
164
165
  try {
165
166
  const bucket = JSON.parse( process.env.BUCKET );
166
- const inputData = req.body;
167
+ const inputData = req.method === 'POST' ? req.body : req.query;
167
168
  const params={
168
169
  Bucket: bucket.auditInput,
169
170
  Key: `${inputData.fileDate}/${inputData.storeId}`,
@@ -202,4 +203,129 @@ export async function isAuditDocumentExist( req, res, next ) {
202
203
  }
203
204
  }
204
205
 
206
+ export async function getAuditImageData( data ) {
207
+ try {
208
+ const bucket = JSON.parse( process.env.BUCKET );
209
+ const storeId = data.storeId;
210
+ const fileDate = data.fileDate;
211
+ const files = [];
212
+ const fetchData = {
213
+ Bucket: `${bucket.auditInput}`,
214
+ filePath: `${fileDate}/${storeId}/`,
215
+ };
216
+ const folderPath = await listFileWithoutLimit( fetchData );
217
+ if ( folderPath.statusCode == 404 ) {
218
+ return folderPath;
219
+ }
220
+ const auditImages = folderPath;
221
+
222
+ if ( data.export==false ) {
223
+ if ( auditImages && auditImages?.length > 0 ) {
224
+ for ( let i = 0; i < auditImages.length; i++ ) {
225
+ const img = auditImages[i].Key.split( '/' );
226
+ const image = img[2].split( '.' );
227
+ const indexes = image[0].split( '_' );
228
+ const params = {
229
+ file_path: auditImages[i].Key,
230
+ Bucket: fetchData.Bucket,
231
+ };
232
+ const data = await signedUrl( params );
233
+ files.push( {
234
+ imgPath: data,
235
+ imgName: indexes[1],
236
+ imgId: image[0],
237
+ img_name: indexes[1],
238
+ // imgFile: auditImages[i],
239
+ } );
240
+ }
241
+ return files;
242
+ }
243
+ } else {
244
+ if ( auditImages && auditImages?.length > 0 ) {
245
+ for ( let i = 0; i < auditImages.length; i++ ) {
246
+ const img = auditImages[i].Key.split( '/' );
247
+ const image = img[2].split( '.' );
248
+ const indexes = image[0].split( '_' );
249
+ const params = {
250
+ file_path: auditImages[i].Key,
251
+ Bucket: fetchData.Bucket,
252
+ };
253
+ const data = await signedUrl( params );
254
+ files.push( {
255
+ imgPath: data,
256
+ imgName: indexes[1],
257
+ imgId: image[0],
258
+ imgFile: auditImages[i],
259
+ img_name: indexes[1],
260
+ } );
261
+ }
262
+ return files;
263
+ }
264
+ }
265
+ } catch ( error ) {
266
+ logger.error( { error: error, type: 'getAuditImageData' } );
267
+ return error;
268
+ }
269
+ }
270
+
271
+ export async function zipDownloadImage( data ) {
272
+ try {
273
+ const bucket = JSON.parse( process.env.BUCKET );
274
+ const zip = new AdmZip();
275
+ const split = await chunkArray( data, 10 );
276
+ const promises = await split.map( async ( chunk ) => {
277
+ for ( let i = 0; i< chunk.length; i++ ) {
278
+ const params ={
279
+ Bucket: `${bucket.auditInput}`,
280
+ Key: chunk[i].imgFile.Key,
281
+ };
282
+ const fileBuffer = await getObject( params );
283
+ zip.addFile( chunk[i].imgFile.Key, fileBuffer );
284
+ }
285
+ return zip;
286
+ } );
287
+ await Promise.all( promises );
288
+ const zipBuffer = zip.toBuffer();
289
+ return zipBuffer;
290
+ } catch ( error ) {
291
+ const err = error.message || 'Internal Server Error';
292
+ logger.error( { error: error, message: data, function: 'zipDownloadImage' } );
293
+ return err;
294
+ }
295
+ }
296
+
297
+ export async function getAuditFilterData( data ) {
298
+ try {
299
+ const bucket = JSON.parse( process.env.BUCKET );
300
+ const storeId = data.storeId;
301
+ const fileDate = data.fileDate;
302
+ const params = {
303
+ Bucket: `${bucket.auditOutput}`,
304
+ Key: `${storeId}/${fileDate}/${bucket.masterJsonFile}`,
305
+ };
306
+ const mappingFile = await getJsonFileData( params );
307
+ if ( mappingFile.statusCode ) {
308
+ return mappingFile;
309
+ } else {
310
+ const mappingData = await JSON.parse( mappingFile.toString( 'utf-8' ) );
311
+ const filterData = await mappingData.person_data.filter(
312
+ ( item ) =>
313
+ item.temp_ids < 20000 &&
314
+ item.temp_ids > 0 &&
315
+ item.person_status === '',
316
+ );
317
+ const finalResult = await filterData.map( ( i ) => {
318
+ return { ...i, img: i.index.concat( '_', i.temp_ids ) };
319
+ } );
320
+ return finalResult;
321
+ }
322
+ } catch ( error ) {
323
+ logger.error( {
324
+ error: error,
325
+ type: 'MAPPING JSON getFilterData',
326
+ function: 'getFilterData',
327
+ } );
328
+ return error;
329
+ }
330
+ }
205
331