tango-app-api-audio-analytics 1.0.7 → 1.0.9
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/index.js +0 -1
- package/package.json +2 -1
- package/src/controllers/audioAnalytics.controller.js +1 -0
- package/src/controllers/cohortAnalytics.controller.js +11 -2
- package/src/controllers/conversationAnalytics.controller.js +34 -29
- package/src/dtos/audioAnalytics.dtos.js +5 -1
- package/src/services/conversation.service.js +83 -35
package/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tango-app-api-audio-analytics",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.9",
|
|
4
4
|
"description": "audioAnalytics",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
"author": "praveenraj",
|
|
14
14
|
"license": "ISC",
|
|
15
15
|
"dependencies": {
|
|
16
|
+
"@aws-sdk/client-secrets-manager": "^3.1003.0",
|
|
16
17
|
"aws-sdk": "^2.1693.0",
|
|
17
18
|
"body-parser": "^2.2.2",
|
|
18
19
|
"cors": "^2.8.6",
|
|
@@ -46,9 +46,18 @@ export const getCohortAnalysisCard = async ( req, res ) => {
|
|
|
46
46
|
clientId,
|
|
47
47
|
} );
|
|
48
48
|
|
|
49
|
+
if ( !summaryCard ) {
|
|
50
|
+
logger.warn( 'No data returned for cohort analysis summary card', { startDate, endDate, storeId, cohortType, clientId } );
|
|
51
|
+
return res.status( 204 ).json( {
|
|
52
|
+
status: 'error',
|
|
53
|
+
message: 'No data found for the given parameters',
|
|
54
|
+
code: 'DATA_NOT_FOUND',
|
|
55
|
+
timestamp: new Date().toISOString(),
|
|
56
|
+
} );
|
|
57
|
+
}
|
|
49
58
|
return res.status( 200 ).json( {
|
|
50
|
-
status: 'success',
|
|
51
|
-
data: summaryCard,
|
|
59
|
+
status: summaryCard?.status || 'success',
|
|
60
|
+
data: summaryCard?.data || [],
|
|
52
61
|
timestamp: new Date().toISOString(),
|
|
53
62
|
} );
|
|
54
63
|
} catch ( error ) {
|
|
@@ -5,8 +5,8 @@ import {
|
|
|
5
5
|
exportConversationsFromLambda,
|
|
6
6
|
getConversationDetailsFromLambda,
|
|
7
7
|
exportConversationsToCSV,
|
|
8
|
-
filterConversationsBySearch,
|
|
9
|
-
sortConversations,
|
|
8
|
+
// filterConversationsBySearch,
|
|
9
|
+
// sortConversations,
|
|
10
10
|
} from '../services/conversation.service.js';
|
|
11
11
|
|
|
12
12
|
/**
|
|
@@ -34,6 +34,7 @@ export const getConversationsList = async ( req, res ) => {
|
|
|
34
34
|
endDate,
|
|
35
35
|
storeId,
|
|
36
36
|
clientId,
|
|
37
|
+
cohortId,
|
|
37
38
|
isAI,
|
|
38
39
|
analyticsType,
|
|
39
40
|
searchValue,
|
|
@@ -71,13 +72,14 @@ export const getConversationsList = async ( req, res ) => {
|
|
|
71
72
|
endDate,
|
|
72
73
|
storeId,
|
|
73
74
|
clientId,
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
75
|
+
cohortId,
|
|
76
|
+
isExport,
|
|
77
|
+
limit,
|
|
78
|
+
offset,
|
|
77
79
|
} );
|
|
78
80
|
|
|
79
|
-
conversations = exportResponse.conversations || [];
|
|
80
|
-
totalCount = exportResponse.totalCount || conversations.length;
|
|
81
|
+
// conversations = exportResponse.conversations || [];
|
|
82
|
+
// totalCount = exportResponse.totalCount || conversations.length;
|
|
81
83
|
|
|
82
84
|
logger.info( {
|
|
83
85
|
message: 'Export Lambda response received',
|
|
@@ -85,15 +87,15 @@ export const getConversationsList = async ( req, res ) => {
|
|
|
85
87
|
} );
|
|
86
88
|
|
|
87
89
|
// Apply search filter if provided
|
|
88
|
-
if ( searchValue ) {
|
|
89
|
-
|
|
90
|
-
}
|
|
90
|
+
// if ( searchValue ) {
|
|
91
|
+
// conversations = filterConversationsBySearch( conversations, searchValue );
|
|
92
|
+
// }
|
|
91
93
|
|
|
92
|
-
// Apply sorting
|
|
93
|
-
conversations = sortConversations( conversations, 'date', 'desc' );
|
|
94
|
+
// // Apply sorting
|
|
95
|
+
// conversations = sortConversations( conversations, 'date', 'desc' );
|
|
94
96
|
|
|
95
97
|
// Convert to CSV
|
|
96
|
-
const csv = await exportConversationsToCSV(
|
|
98
|
+
const csv = await exportConversationsToCSV( exportResponse.data );
|
|
97
99
|
res.setHeader( 'Content-Type', 'text/csv' );
|
|
98
100
|
res.setHeader( 'Content-Disposition', 'attachment; filename=conversations.csv' );
|
|
99
101
|
return res.send( csv );
|
|
@@ -116,23 +118,22 @@ export const getConversationsList = async ( req, res ) => {
|
|
|
116
118
|
offset,
|
|
117
119
|
} );
|
|
118
120
|
|
|
119
|
-
conversations = lambdaResponse.conversations || [];
|
|
120
|
-
totalCount = lambdaResponse.totalCount || conversations.length;
|
|
121
|
+
// conversations = lambdaResponse.conversations || [];
|
|
122
|
+
// totalCount = lambdaResponse.totalCount || conversations.length;
|
|
121
123
|
|
|
122
|
-
// Apply search filter if provided
|
|
123
|
-
if ( searchValue ) {
|
|
124
|
-
|
|
125
|
-
}
|
|
124
|
+
// // Apply search filter if provided
|
|
125
|
+
// if ( searchValue ) {
|
|
126
|
+
// conversations = filterConversationsBySearch( conversations, searchValue );
|
|
127
|
+
// }
|
|
126
128
|
|
|
127
|
-
// Apply sorting
|
|
128
|
-
conversations = sortConversations( conversations, 'date', 'desc' );
|
|
129
|
+
// // Apply sorting
|
|
130
|
+
// conversations = sortConversations( conversations, 'date', 'desc' );
|
|
129
131
|
|
|
130
132
|
// Return paginated JSON response
|
|
131
133
|
return res.status( 200 ).json( {
|
|
132
134
|
status: 'success',
|
|
133
135
|
data: {
|
|
134
|
-
|
|
135
|
-
conversations,
|
|
136
|
+
lambdaResponse,
|
|
136
137
|
pagination: {
|
|
137
138
|
limit,
|
|
138
139
|
offset,
|
|
@@ -167,12 +168,16 @@ export const getConversationsList = async ( req, res ) => {
|
|
|
167
168
|
export const getConversationDetails = async ( req, res ) => {
|
|
168
169
|
try {
|
|
169
170
|
const { conversationId } = req.params;
|
|
170
|
-
const {
|
|
171
|
-
|
|
172
|
-
|
|
171
|
+
const { cohortId } = req.body;
|
|
172
|
+
logger.info( {
|
|
173
|
+
message: 'Received request for conversation details',
|
|
174
|
+
conversationId,
|
|
175
|
+
cohortId,
|
|
176
|
+
} );
|
|
177
|
+
if ( !conversationId || !cohortId ) {
|
|
173
178
|
return res.status( 400 ).json( {
|
|
174
179
|
status: 'error',
|
|
175
|
-
message: 'Missing required parameters: conversationId,
|
|
180
|
+
message: 'Missing required parameters: conversationId, cohortId',
|
|
176
181
|
code: 'MISSING_PARAMETERS',
|
|
177
182
|
timestamp: new Date().toISOString(),
|
|
178
183
|
} );
|
|
@@ -181,13 +186,13 @@ export const getConversationDetails = async ( req, res ) => {
|
|
|
181
186
|
logger.info( {
|
|
182
187
|
message: 'Fetching conversation details',
|
|
183
188
|
conversationId,
|
|
184
|
-
|
|
189
|
+
cohortId,
|
|
185
190
|
} );
|
|
186
191
|
|
|
187
192
|
// Call Lambda to get conversation details
|
|
188
193
|
const conversationData = await getConversationDetailsFromLambda( {
|
|
189
194
|
conversationId,
|
|
190
|
-
|
|
195
|
+
cohortId,
|
|
191
196
|
} );
|
|
192
197
|
|
|
193
198
|
if ( !conversationData ) {
|
|
@@ -518,6 +518,7 @@ export const conversationsListValid = joi.object( {
|
|
|
518
518
|
endDate: joi.string().required().pattern( /^\d{4}-\d{2}-\d{2}$/ ).description( 'End date in YYYY-MM-DD format' ),
|
|
519
519
|
storeId: joi.array().items( joi.string() ).required().min( 1 ).description( 'Array of store IDs' ),
|
|
520
520
|
clientId: joi.array().items( joi.string() ).optional().description( 'Array of client IDs' ),
|
|
521
|
+
cohortType: joi.array().items( joi.string() ).required().min( 1 ).description( 'Array of cohort types' ),
|
|
521
522
|
isAI: joi.boolean().optional().description( 'Filter for AI-generated conversations' ),
|
|
522
523
|
analyticsType: joi.string().optional().valid( 'audio', 'text', 'all' ).description( 'Type of analytics' ),
|
|
523
524
|
searchValue: joi.string().optional().description( 'Search term' ),
|
|
@@ -530,7 +531,10 @@ export const conversationsListValid = joi.object( {
|
|
|
530
531
|
* Conversation Details Schema
|
|
531
532
|
*/
|
|
532
533
|
export const conversationDetailsValid = joi.object( {
|
|
533
|
-
|
|
534
|
+
cohortId: joi.alternatives().try(
|
|
535
|
+
joi.string(),
|
|
536
|
+
joi.array().items( joi.string() ).min( 1 ),
|
|
537
|
+
).required().description( 'Cohort ID or array of cohort IDs' ),
|
|
534
538
|
} ).strict();
|
|
535
539
|
|
|
536
540
|
/**
|
|
@@ -2,7 +2,6 @@ import { logger } from 'tango-app-api-middleware';
|
|
|
2
2
|
import axios from 'axios';
|
|
3
3
|
import { Parser } from 'json2csv';
|
|
4
4
|
|
|
5
|
-
const LAMBDA_ENDPOINT = JSON.parse( process.env.URL ) || 'http://lambda-api:8000';
|
|
6
5
|
|
|
7
6
|
/**
|
|
8
7
|
* Call the Lambda function to get cohort analysis
|
|
@@ -16,22 +15,35 @@ const LAMBDA_ENDPOINT = JSON.parse( process.env.URL ) || 'http://lambda-api:8000
|
|
|
16
15
|
*/
|
|
17
16
|
export async function getCohortAnalysisFromLambda( params ) {
|
|
18
17
|
try {
|
|
19
|
-
|
|
18
|
+
const LAMBDA_ENDPOINT = JSON.parse( process.env.URL ) || 'http://lambda-api:8000';
|
|
20
19
|
|
|
21
|
-
const
|
|
20
|
+
const payload = {
|
|
22
21
|
startDate: params.startDate,
|
|
23
22
|
endDate: params.endDate,
|
|
24
|
-
storeId: params.storeId,
|
|
25
|
-
cohortType: params.cohortType,
|
|
26
|
-
clientId: params.clientId,
|
|
27
|
-
}
|
|
23
|
+
storeId: Array.isArray( params.storeId ) ? params.storeId : [ params.storeId ],
|
|
24
|
+
cohort_id: Array.isArray( params.cohortType ) ? params.cohortType : [ params.cohortType ],
|
|
25
|
+
clientId: Array.isArray( params.clientId ) ? params.clientId : [ params.clientId ],
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
logger.info( { message: 'Calling Lambda for cohort analysis', url: LAMBDA_ENDPOINT.cohortAnalysisCard, payload } );
|
|
29
|
+
|
|
30
|
+
const response = await axios.post( `${LAMBDA_ENDPOINT.cohortAnalysisCard}`, payload, {
|
|
31
|
+
headers: {
|
|
32
|
+
'Content-Type': 'application/json',
|
|
33
|
+
},
|
|
28
34
|
timeout: 30000,
|
|
29
35
|
} );
|
|
30
36
|
|
|
31
|
-
logger.info( { message: 'Lambda response received', data: response.data } );
|
|
37
|
+
logger.info( { message: 'Lambda response received', status: response.status, data: response.data } );
|
|
32
38
|
return response.data;
|
|
33
39
|
} catch ( error ) {
|
|
34
|
-
logger.error( {
|
|
40
|
+
logger.error( {
|
|
41
|
+
error: error.message,
|
|
42
|
+
status: error.response?.status,
|
|
43
|
+
data: error.response?.data,
|
|
44
|
+
message: 'Error calling Lambda for cohort analysis',
|
|
45
|
+
params,
|
|
46
|
+
} );
|
|
35
47
|
throw new Error( `Failed to fetch cohort analysis: ${error.message}` );
|
|
36
48
|
}
|
|
37
49
|
}
|
|
@@ -52,6 +64,7 @@ export async function getCohortAnalysisFromLambda( params ) {
|
|
|
52
64
|
*/
|
|
53
65
|
export async function getConversationsListFromLambda( params ) {
|
|
54
66
|
try {
|
|
67
|
+
const LAMBDA_ENDPOINT = JSON.parse( process.env.URL ) || 'http://lambda-api:8000';
|
|
55
68
|
logger.info( { message: 'Calling Lambda for conversations list', params } );
|
|
56
69
|
|
|
57
70
|
const response = await axios.post( `${LAMBDA_ENDPOINT.cohortConversationList}/conversations/list`, {
|
|
@@ -90,24 +103,38 @@ export async function getConversationsListFromLambda( params ) {
|
|
|
90
103
|
*/
|
|
91
104
|
export async function exportConversationsFromLambda( params ) {
|
|
92
105
|
try {
|
|
93
|
-
|
|
106
|
+
const LAMBDA_ENDPOINT = JSON.parse( process.env.URL ) || 'http://lambda-api:8000';
|
|
94
107
|
|
|
95
|
-
const
|
|
108
|
+
const payload = {
|
|
96
109
|
startDate: params.startDate,
|
|
97
110
|
endDate: params.endDate,
|
|
98
|
-
storeId: params.storeId,
|
|
99
|
-
clientId: params.clientId,
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
111
|
+
storeId: Array.isArray( params.storeId ) ? params.storeId : [ params.storeId ],
|
|
112
|
+
clientId: Array.isArray( params.clientId ) ? params.clientId : [ params.clientId ],
|
|
113
|
+
cohortId: params.cohortId,
|
|
114
|
+
isExport: params.isExport ?? true,
|
|
115
|
+
limit: params.limit || 10,
|
|
116
|
+
offset: params.offset || 0,
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
logger.info( { message: 'Calling Lambda for conversations export', url: LAMBDA_ENDPOINT.cohortConversationList, payload } );
|
|
120
|
+
|
|
121
|
+
const response = await axios.post( `${LAMBDA_ENDPOINT.cohortConversationList}`, payload, {
|
|
122
|
+
headers: {
|
|
123
|
+
'Content-Type': 'application/json',
|
|
124
|
+
},
|
|
104
125
|
timeout: 30000,
|
|
105
126
|
} );
|
|
106
127
|
|
|
107
|
-
logger.info( { message: 'Lambda response received for conversations export',
|
|
128
|
+
logger.info( { message: 'Lambda response received for conversations export', data: response.data } );
|
|
108
129
|
return response.data;
|
|
109
130
|
} catch ( error ) {
|
|
110
|
-
logger.error( {
|
|
131
|
+
logger.error( {
|
|
132
|
+
error: error.message,
|
|
133
|
+
status: error.response?.status,
|
|
134
|
+
data: error.response?.data,
|
|
135
|
+
message: 'Error calling Lambda for conversations export',
|
|
136
|
+
params,
|
|
137
|
+
} );
|
|
111
138
|
throw new Error( `Failed to export conversations: ${error.message}` );
|
|
112
139
|
}
|
|
113
140
|
}
|
|
@@ -121,18 +148,32 @@ export async function exportConversationsFromLambda( params ) {
|
|
|
121
148
|
*/
|
|
122
149
|
export async function getConversationDetailsFromLambda( params ) {
|
|
123
150
|
try {
|
|
124
|
-
|
|
151
|
+
const LAMBDA_ENDPOINT = JSON.parse( process.env.URL ) || 'http://lambda-api:8000';
|
|
125
152
|
|
|
126
|
-
const
|
|
127
|
-
|
|
128
|
-
|
|
153
|
+
const payload = {
|
|
154
|
+
audio_id: params.conversationId,
|
|
155
|
+
cohort_id: Array.isArray( params.cohortId ) ? params.cohortId : [ params.cohortId ],
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
logger.info( { message: 'Calling Lambda for conversation details', url: LAMBDA_ENDPOINT.cohortConversationDetail, payload } );
|
|
159
|
+
|
|
160
|
+
const response = await axios.post( `${LAMBDA_ENDPOINT.cohortConversationDetail}`, payload, {
|
|
161
|
+
headers: {
|
|
162
|
+
'Content-Type': 'application/json',
|
|
163
|
+
},
|
|
129
164
|
timeout: 30000,
|
|
130
165
|
} );
|
|
131
166
|
|
|
132
167
|
logger.info( { message: 'Lambda response received for conversation details', conversationId: params.conversationId } );
|
|
133
168
|
return response.data;
|
|
134
169
|
} catch ( error ) {
|
|
135
|
-
logger.error( {
|
|
170
|
+
logger.error( {
|
|
171
|
+
error: error.message,
|
|
172
|
+
status: error.response?.status,
|
|
173
|
+
data: error.response?.data,
|
|
174
|
+
message: 'Error calling Lambda for conversation details',
|
|
175
|
+
params,
|
|
176
|
+
} );
|
|
136
177
|
throw new Error( `Failed to fetch conversation details: ${error.message}` );
|
|
137
178
|
}
|
|
138
179
|
}
|
|
@@ -171,20 +212,27 @@ export async function exportConversationsToCSV( conversations, columns = null )
|
|
|
171
212
|
*/
|
|
172
213
|
export async function getCohortAnalysisSummaryCard( params ) {
|
|
173
214
|
try {
|
|
174
|
-
const
|
|
215
|
+
const paramavalue = {
|
|
216
|
+
startDate: params.startDate,
|
|
217
|
+
endDate: params.endDate,
|
|
218
|
+
storeId: params.storeId,
|
|
219
|
+
cohortType: params.cohortType,
|
|
220
|
+
clientId: params.clientId,
|
|
221
|
+
};
|
|
222
|
+
const lambdaResponse = await getCohortAnalysisFromLambda( paramavalue );
|
|
175
223
|
|
|
176
224
|
// Process response to create summary card
|
|
177
|
-
const summaryCard = {
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
};
|
|
225
|
+
// const summaryCard = {
|
|
226
|
+
// totalConversations: lambdaResponse.totalConversations || 0,
|
|
227
|
+
// totalDuration: lambdaResponse.totalDuration || 0,
|
|
228
|
+
// averageRating: lambdaResponse.averageRating || 0,
|
|
229
|
+
// topCohorts: lambdaResponse.topCohorts || [],
|
|
230
|
+
// trendData: lambdaResponse.trendData || [],
|
|
231
|
+
// keyInsights: lambdaResponse.keyInsights || [],
|
|
232
|
+
// timestamp: new Date().toISOString(),
|
|
233
|
+
// };
|
|
186
234
|
|
|
187
|
-
return
|
|
235
|
+
return lambdaResponse;
|
|
188
236
|
} catch ( error ) {
|
|
189
237
|
logger.error( { error, message: 'Error getting cohort analysis summary card' } );
|
|
190
238
|
throw error;
|